Skip to content

Commit

Permalink
[JENKINS-33662] Implemented a quick & dirty upgrade wizard
Browse files Browse the repository at this point in the history
  • Loading branch information
kohsuke committed Mar 18, 2016
1 parent 8c1f8cf commit 3899be3
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 0 deletions.
140 changes: 140 additions & 0 deletions core/src/main/java/jenkins/install/UpgradeWizard.java
@@ -0,0 +1,140 @@
package jenkins.install;

import hudson.Extension;
import hudson.model.PageDecorator;
import hudson.model.UpdateSite.Plugin;
import hudson.util.HttpResponses;
import hudson.util.VersionNumber;
import jenkins.model.Jenkins;
import org.apache.commons.io.FileUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static java.util.logging.Level.*;
import static org.apache.commons.io.FileUtils.*;
import static org.apache.commons.lang.StringUtils.*;

/**
* This is a stop-gap measure until JENKINS-33663 comes in.
* This call may go away. Please don't use it from outside.
*
* @author Kohsuke Kawaguchi
*/
@Restricted(NoExternalUse.class)
@Extension
public class UpgradeWizard extends PageDecorator {
@Inject
private Jenkins jenkins;

/**
* Is this instance fully upgraded?
*/
private volatile boolean upToDate;

/**
* File that captures the state of upgrade.
*
* This file records the vesrion number that the installation has upgraded to.
*/
/*package*/ File getStateFile() {
return new File(Jenkins.getInstance().getRootDir(),"upgraded");
}

public UpgradeWizard() throws IOException {
updateUpToDate();
}

private void updateUpToDate() throws IOException {
upToDate = new VersionNumber("2.0").compareTo(getCurrentLevel()) <= 0;
}

/**
* What is the version the upgrade wizard has run the last time and upgraded to?
*/
private VersionNumber getCurrentLevel() throws IOException {
VersionNumber from = new VersionNumber("1.0");
File state = getStateFile();
if (state.exists()) {
from = new VersionNumber(defaultIfBlank(readFileToString(state), "1.0").trim());
}
return from;
}

/**
* Do we need to show the upgrade wizard prompt?
*/
public boolean isDue() {
if (upToDate)
return false;

// only admin users should see this
if (!jenkins.hasPermission(Jenkins.ADMINISTER))
return false;

return System.currentTimeMillis() > getStateFile().lastModified();
}

/**
* Snooze the upgrade wizard notice.
*/
@RequirePOST
public HttpResponse doSnooze() throws IOException {
jenkins.checkPermission(Jenkins.ADMINISTER);
File f = getStateFile();
FileUtils.touch(f);
f.setLastModified(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1));
LOGGER.log(FINE, "Snoozed the upgrade wizard notice");
return HttpResponses.redirectToContextRoot();
}

/**
* Performs the upgrade activity.
*/
@RequirePOST
public HttpResponse doUpgrade() throws IOException {
try {
if (new VersionNumber("2.0").compareTo(getCurrentLevel())>0) {
// 1.0 -> 2.0 upgrade
LOGGER.log(WARNING, "Performing 1.0 to 2.0 upgrade");

for (String shortName : Arrays.asList("workflow-aggregator", "pipeline-stage-view", "github-organization-folder")) {
Plugin p = jenkins.getUpdateCenter().getPlugin(shortName);
if (p==null) {
LOGGER.warning("Plugin not found in the update center: " + shortName);
} else {
p.deploy(true);
}
}

// upgrade to 2.0 complete (if plugin installations fail, that's too bad)
FileUtils.writeStringToFile(getStateFile(),"2.0");

// send the user to the update center so that people can see the installation & restart if need be.
return HttpResponses.redirectViaContextPath("updateCenter/");
}

// in the future...
// if (new VersionNumber("3.0").compareTo(getCurrentLevel())>0) {
//
// }

return NOOP;
} finally {
updateUpToDate();
}
}

/*package*/ static final HttpResponse NOOP = HttpResponses.redirectToContextRoot();


private static final Logger LOGGER = Logger.getLogger(UpgradeWizard.class.getName());
}
49 changes: 49 additions & 0 deletions core/src/main/resources/jenkins/install/UpgradeWizard/footer.jelly
@@ -0,0 +1,49 @@
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:if test="${it.due}">
<style>
#upgrade-fragment {
background-color: #fce94f;
}

#upgrade-notice {
font-weight: bold;
text-align: center;
font-size: 20px;
height: 60px;

display: flex;
justify-content: center;
align-items: center;
}

#upgrade-snooze {
float:right;
margin: 3px;
text-decoration: none;
display: block;
}
</style>
<div style="display:none">
<div id="upgrade-fragment">
<a id="upgrade-snooze" class="post" href="${rootURL}/${it.url}/snooze">
x
</a>
<div id="upgrade-notice">
<div>
<j:whitespace>Welcome to Jenkins v2! </j:whitespace>
<f:link href="${rootURL}/${it.url}/upgrade" post="true">Click here to install the rest of v2 functionalities</f:link>
<j:whitespace> and finish the upgrade process</j:whitespace>
</div>
</div>
</div>
</div>
<script>
(function() {
var e = $('upgrade-fragment');
e.remove();
$('page-body').insert({ top: e });
})();
</script>
</j:if>
</l:html>
78 changes: 78 additions & 0 deletions test/src/test/java/jenkins/install/UpgradeWizardTest.java
@@ -0,0 +1,78 @@
package jenkins.install;

import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import javax.inject.Inject;

import java.io.IOException;

import static org.junit.Assert.*;

/**
* @author Kohsuke Kawaguchi
*/
public class UpgradeWizardTest {
@Rule
public final JenkinsRule j = new JenkinsRule();

@Inject
UpgradeWizard uw;

@Before
public void setup() {
j.jenkins.getInjector().injectMembers(this);
}

@Test
public void snooze() throws Exception {
assertTrue(uw.isDue());
uw.doSnooze();
assertFalse(uw.isDue());
}

/**
* If not upgraded, the upgrade should cause some side effect.
*/
@Test
public void upgrade() throws Exception {
assertTrue(j.jenkins.getUpdateCenter().getJobs().size() == 0);
assertTrue(newInstance().isDue());
assertNotSame(UpgradeWizard.NOOP, uw.doUpgrade());

// can't really test this because UC metadata is empty
// assertTrue(j.jenkins.getUpdateCenter().getJobs().size() > 0);
}

/**
* If already upgraded, don't show anything
*/
@Test
public void fullyUpgraded() throws Exception {
FileUtils.writeStringToFile(uw.getStateFile(), "2.0");
assertFalse(newInstance().isDue());
assertSame(UpgradeWizard.NOOP, uw.doUpgrade());
}

/**
* If downgraded from future version, don't prompt upgrade wizard.
*/
@Test
public void downgradeFromFuture() throws Exception {
FileUtils.writeStringToFile(uw.getStateFile(), "3.0");
assertFalse(newInstance().isDue());
assertSame(UpgradeWizard.NOOP, uw.doUpgrade());
}

/**
* Fresh instance of {@link UpgradeWizard} to test its behavior.
*/
private UpgradeWizard newInstance() throws IOException {
UpgradeWizard uw2 = new UpgradeWizard();
j.jenkins.getInjector().injectMembers(uw2);
return uw2;
}
}

0 comments on commit 3899be3

Please sign in to comment.