Skip to content

Commit

Permalink
JENKINS-33244 / JENKINS-34174 - retry for failed plugins & specify
Browse files Browse the repository at this point in the history
download timeout
  • Loading branch information
kzantow committed Apr 21, 2016
1 parent d1bcd1f commit 6974d2b
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 28 deletions.
32 changes: 28 additions & 4 deletions core/src/main/java/hudson/PluginManager.java
Expand Up @@ -36,6 +36,8 @@
import hudson.model.ItemGroupMixIn;
import hudson.model.UpdateCenter;
import hudson.model.UpdateSite;
import hudson.model.UpdateCenter.DownloadJob;
import hudson.model.UpdateCenter.InstallationJob;
import hudson.security.Permission;
import hudson.security.PermissionScope;
import hudson.util.CyclicGraphDetector;
Expand Down Expand Up @@ -1055,6 +1057,20 @@ public HttpResponse doUpdateSources(StaplerRequest req) throws IOException {

return new HttpRedirect("./sites");
}

/**
* Called to progress status beyond installing plugins, e.g. if
* there were failures that prevented installation from naturally proceeding
*/
@RequirePOST
@Restricted(DoNotUse.class) // WebOnly
public void doInstallPluginsDone() {
Jenkins j = Jenkins.getInstance();
j.checkPermission(Jenkins.ADMINISTER);
if(InstallState.INITIAL_PLUGINS_INSTALLING.equals(j.getInstallState())) {
Jenkins.getInstance().setInstallState(InstallState.INITIAL_PLUGINS_INSTALLING.getNextState());
}
}

/**
* Performs the installation of the plugins.
Expand Down Expand Up @@ -1182,22 +1198,30 @@ private void trackInitialPluginInstall(@Nonnull final List<Future<UpdateCenter.U
new Thread() {
@Override
public void run() {
final boolean failures[] = { false };
INSTALLING: while (true) {
try {
updateCenter.persistInstallStatus();
updateCenter.persistInstallStatus();
Thread.sleep(500);
failures[0] = false;
for (Future<UpdateCenter.UpdateCenterJob> jobFuture : installJobs) {
if(!jobFuture.isDone() && !jobFuture.isCancelled()) {
continue INSTALLING;
}
UpdateCenter.UpdateCenterJob job = jobFuture.get();
if(job instanceof InstallationJob && ((InstallationJob)job).status instanceof DownloadJob.Failure) {
failures[0] = true;
}
}
} catch (InterruptedException e) {
} catch (Exception e) {
LOGGER.log(WARNING, "Unexpected error while waiting for initial plugin set to install.", e);
}
break;
}
updateCenter.persistInstallStatus();
jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLING.getNextState());
updateCenter.persistInstallStatus();
if(!failures[0]) {
jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLING.getNextState());
}
}
}.start();
}
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/hudson/model/UpdateCenter.java
Expand Up @@ -135,6 +135,11 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas

private static final String UPDATE_CENTER_URL = System.getProperty(UpdateCenter.class.getName()+".updateCenterUrl","http://updates.jenkins-ci.org/");

/**
* Read timeout when downloading plugins, defaults to 1 minute
*/
private static final int PLUGIN_DOWNLOAD_READ_TIMEOUT = Integer.getInteger(UpdateCenter.class.getName()+".pluginDownloadReadTimeoutSeconds", 60) * 1000;

/**
* {@linkplain UpdateSite#getId() ID} of the default update site.
* @since 1.483
Expand Down Expand Up @@ -1004,6 +1009,11 @@ public File download(DownloadJob job, URL src) throws IOException {
URLConnection con = null;
try {
con = connect(job,src);
//JENKINS-34174 - set timeout for downloads, may hang indefinitely
// particularly noticeable during 2.0 install when downloading
// many plugins
con.setReadTimeout(PLUGIN_DOWNLOAD_READ_TIMEOUT);

int total = con.getContentLength();
in = new CountingInputStream(con.getInputStream());
byte[] buf = new byte[8192];
Expand Down
Expand Up @@ -50,4 +50,8 @@ installWizard_configureProxy_save=Save and Continue
installWizard_skipPluginInstallations=Skip Plugin Installations
installWizard_installIncomplete_dependenciesLabel=Dependencies
installWizard_installingConsole_dependencyIndicatorNote=** - required dependency
installWizard_pluginInstallFailure_title=Installation Failures
installWizard_pluginInstallFailure_message=Some plugins failed to install properly, you may retry installing them or continue \
with the failed plugins
installWizard_continue=Continue
installWizard_retry=Retry
14 changes: 14 additions & 0 deletions war/src/main/js/api/pluginManager.js
Expand Up @@ -173,6 +173,20 @@ exports.isRestartRequired = function(handler) {
});
};

/**
* Skip failed plugins, continue
*/
exports.installPluginsDone = function(handler) {
jenkins.post('/pluginManager/installPluginsDone', {}, function() {
handler();
}, {
timeout: pluginManagerErrorTimeoutMillis,
error: function(xhr, textStatus, errorThrown) {
handler.call({ isError: true, message: errorThrown });
}
});

This comment has been minimized.

Copy link
@recena

recena May 3, 2016

Contributor

; forgotten

}

/**
* Restart Jenkins
*/
Expand Down
49 changes: 45 additions & 4 deletions war/src/main/js/pluginSetupWizardGui.js
Expand Up @@ -110,6 +110,7 @@ var createPluginSetupWizard = function(appendTarget) {
var loadingPanel = require('./templates/loadingPanel.hbs');
var welcomePanel = require('./templates/welcomePanel.hbs');
var progressPanel = require('./templates/progressPanel.hbs');
var pluginSuccessPanel = require('./templates/successPanel.hbs');
var pluginSelectionPanel = require('./templates/pluginSelectionPanel.hbs');
var setupCompletePanel = require('./templates/setupCompletePanel.hbs');
var proxyConfigPanel = require('./templates/proxyConfigPanel.hbs');
Expand Down Expand Up @@ -241,6 +242,7 @@ var createPluginSetupWizard = function(appendTarget) {

// plugin data for the progress panel
var installingPlugins = [];
var failedPluginNames = [];
var getInstallingPlugin = function(plugName) {
for(var i = 0; i < installingPlugins.length; i++) {
var p = installingPlugins[i];
Expand All @@ -250,6 +252,16 @@ var createPluginSetupWizard = function(appendTarget) {
}
return null;
};
var setFailureStatus = function(plugData) {
var plugFailIdx = failedPluginNames.indexOf(plugData.name);
if(/.*Fail.*/.test(plugData.installStatus)) {
if(plugFailIdx < 0) {
failedPluginNames.push(plugData.name);
}
} else if(plugFailIdx > 0) {
failedPluginNames = failedPluginNames.slice(plugFailIdx,1);
}
};

// recursively get all the dependencies for a particular plugin, this is used to show 'installing' status
// when only dependencies are being installed
Expand Down Expand Up @@ -336,6 +348,12 @@ var createPluginSetupWizard = function(appendTarget) {

// Define actions
var showInstallProgress = function(state) {
// check for installing plugins that failed
if(failedPluginNames.length > 0) {
setPanel(pluginSuccessPanel, { installingPlugins : installingPlugins, failedPlugins: true });
return;
}

if(state) {
if(/CREATE_ADMIN_USER/.test(state)) {
setupFirstUser();
Expand Down Expand Up @@ -396,6 +414,8 @@ var createPluginSetupWizard = function(appendTarget) {
state = 'fail';
}

setFailureStatus(j);

if(txt && state) {
for(var installingIdx = 0; installingIdx < installingPlugins.length; installingIdx++) {
var installing = installingPlugins[installingIdx];
Expand Down Expand Up @@ -440,7 +460,7 @@ var createPluginSetupWizard = function(appendTarget) {
else {
// mark complete
$('.progress-bar').css({width: '100%'});
setupFirstUser();
showInstallProgress('CREATE_ADMIN_USER'); // this temporary, changed in another PR
}
}));
};
Expand Down Expand Up @@ -744,6 +764,23 @@ var createPluginSetupWizard = function(appendTarget) {
});
};

// push failed plugins to retry
var retryFailedPlugins = function() {
var failedPlugins = failedPluginNames;
failedPluginNames = [];
installPlugins(failedPlugins);
};

// continue with failed plugins
var continueWithFailedPlugins = function() {
pluginManager.installPluginsDone(function(){
pluginManager.installStatus(handleGenericError(function(data) {
failedPluginNames = [];
showInstallProgress(data.state);
}));
});
};

// Call this to resume an installation after restart
var resumeInstallation = function() {
// don't re-initialize installing plugins
Expand Down Expand Up @@ -823,6 +860,8 @@ var createPluginSetupWizard = function(appendTarget) {
'.show-proxy-config': setupProxy,
'.save-proxy-config': saveProxyConfig,
'.skip-plugin-installs': function() { installPlugins([]); },
'.retry-failed-plugins': retryFailedPlugins,
'.continue-with-failed-plugins': continueWithFailedPlugins,
'.start-over': startOver
};
for(var cls in actions) {
Expand Down Expand Up @@ -855,13 +894,15 @@ var createPluginSetupWizard = function(appendTarget) {
selectedPluginNames = [];
loadPluginData(handleGenericError(function() {
for (var i = 0; i < jobs.length; i++) {
var j = jobs[i];
// If the job does not have a 'correlationId', then it was not selected
// by the user for install i.e. it's probably a dependency plugin.
if (jobs[i].correlationId) {
selectedPluginNames.push(jobs[i].name);
if (j.correlationId) {
selectedPluginNames.push(j.name);
}
setFailureStatus(j)
}
showInstallProgress(data.state);
showInstallProgress(data.state);
}));
} else {
showInstallProgress(data.state);
Expand Down
49 changes: 29 additions & 20 deletions war/src/main/js/templates/successPanel.hbs
@@ -1,26 +1,35 @@
<div class="modal-header">
<h4 class="modal-title">{{translations.installWizard_pluginsInstalled_title}}</h4>
<h4 class="modal-title">{{translations.installWizard_welcomePanel_title}}</h4>
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel success-panel">
<h1>{{translations.installWizard_pluginsInstalled_banner}}</h1>
<div class="jumbotron welcome-panel success-panel">
<h1>{{translations.installWizard_pluginInstallFailure_title}}</h1>

{{#if restartRequired}}
<p>{{translations.installWizard_installComplete_message}} {{translations.installWizard_installComplete_restartRequiredMessage}}</p>
<button type="button" class="btn btn-primary install-done-restart">
{{translations.installWizard_installComplete_restartLabel}}
</button>
{{else}}
<p>{{translations.installWizard_installComplete_message}}</p>
<button type="button" class="btn btn-primary install-done">
{{translations.installWizard_installComplete_finishButtonLabel}}
</button>
{{/if}}
</div>
{{#if failedPlugins}}
<p>{{translations.installWizard_pluginInstallFailure_message}}</p>
<button type="button" class="btn btn-primary retry-failed-plugins">
{{translations.installWizard_retry}}
</button>
{{/if}}
</div>

<div class="selected-plugin-progress success-panel">
{{#each installingPlugins}}
<div class="selected-plugin {{installStatus}}" data-name="{{id name}}">{{title}}</div>
{{/each}}
</div>
<div class="selected-plugin-progress success-panel">
{{#each installingPlugins}}
<div class="selected-plugin {{installStatus}}" data-name="{{id name}}">{{title}}</div>
{{/each}}
</div>
</div>
<div class="modal-footer">
{{#if failedPlugins}}
<button type="button" class="btn btn-link continue-with-failed-plugins">
{{translations.installWizard_continue}}
</button>
<button type="button" class="btn btn-primary retry-failed-plugins">
{{translations.installWizard_retry}}
</button>
{{else}}
<button type="button" class="btn btn-primary continue-with-failed-plugins">
{{translations.installWizard_continue}}
</button>
{{/if}}
</div>

0 comments on commit 6974d2b

Please sign in to comment.