diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 87153180f6bf..14bb1c1e0d83 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -679,9 +679,8 @@ protected static void addDependencies(URL hpiResUrl, String fromPath, Set d * */ protected void loadDetachedPlugins() { - InstallState installState = Jenkins.getActiveInstance().getInstallState(); - if (InstallState.UPGRADE.equals(installState)) { - VersionNumber lastExecVersion = new VersionNumber(InstallUtil.getLastExecVersion()); + VersionNumber lastExecVersion = new VersionNumber(InstallUtil.getLastExecVersion()); + if (lastExecVersion.isNewerThan(InstallUtil.NEW_INSTALL_VERSION) && lastExecVersion.isOlderThan(Jenkins.getVersion())) { LOGGER.log(INFO, "Upgrading Jenkins. The last running version was {0}. This Jenkins is version {1}.", new Object[] {lastExecVersion, Jenkins.VERSION}); diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java index 20d9a68c76b9..48b8d33ab6ac 100644 --- a/core/src/main/java/jenkins/install/InstallUtil.java +++ b/core/src/main/java/jenkins/install/InstallUtil.java @@ -70,7 +70,8 @@ public class InstallUtil { private static final Logger LOGGER = Logger.getLogger(InstallUtil.class.getName()); // tests need this to be 1.0 - private static final VersionNumber NEW_INSTALL_VERSION = new VersionNumber("1.0"); + @Restricted(NoExternalUse.class) + public static final VersionNumber NEW_INSTALL_VERSION = new VersionNumber("1.0"); private static final VersionNumber FORCE_NEW_INSTALL_VERSION = new VersionNumber("0.0"); /** diff --git a/test/src/test/java/hudson/PluginManagerUtil.java b/test/src/test/java/hudson/PluginManagerUtil.java index 2263622b4c44..423213b7d6c4 100644 --- a/test/src/test/java/hudson/PluginManagerUtil.java +++ b/test/src/test/java/hudson/PluginManagerUtil.java @@ -26,6 +26,8 @@ import jenkins.RestartRequiredException; import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; +import org.junit.runner.Description; +import org.jvnet.hudson.test.RestartableJenkinsRule; import org.jvnet.hudson.test.JenkinsRule; import java.io.File; @@ -47,6 +49,15 @@ public void before() throws Throwable { }; } + public static RestartableJenkinsRule newRestartableJenkinsRule() { + return new RestartableJenkinsRule() { + @Override + public JenkinsRule createJenkinsRule(Description description) { + return newJenkinsRule(); + } + }; + } + public static void dynamicLoad(String plugin, Jenkins jenkins) throws IOException, InterruptedException, RestartRequiredException { dynamicLoad(plugin, jenkins, false); } diff --git a/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java b/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java new file mode 100644 index 000000000000..eecc1e2b4909 --- /dev/null +++ b/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java @@ -0,0 +1,121 @@ +/* + * The MIT License + * + * Copyright 2017 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.install; + +import hudson.ClassicPluginStrategy; +import hudson.ClassicPluginStrategy.DetachedPlugin; +import hudson.PluginManager; +import hudson.PluginManagerUtil; +import hudson.PluginWrapper; +import hudson.util.VersionNumber; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class LoadDetachedPluginsTest { + + @Rule public RestartableJenkinsRule rr = PluginManagerUtil.newRestartableJenkinsRule(); + + @Issue("JENKINS-48365") + @Test + @LocalData + public void upgradeFromJenkins1() throws IOException { + VersionNumber since = new VersionNumber("1.550"); + rr.then(r -> { + List detachedPlugins = ClassicPluginStrategy.getDetachedPlugins(since); + assertThat("Plugins have been detached since the pre-upgrade version", + detachedPlugins.size(), greaterThan(4)); + assertThat("Plugins detached between the pre-upgrade version and the current version should be installed", + getInstalledDetachedPlugins(r, detachedPlugins).size(), equalTo(detachedPlugins.size())); + assertNoFailedPlugins(r); + }); + } + + @Issue("JENKINS-48365") + @Test + @LocalData + public void upgradeFromJenkins2() { + VersionNumber since = new VersionNumber("2.0"); + rr.then(r -> { + List detachedPlugins = ClassicPluginStrategy.getDetachedPlugins(since); + assertThat("Plugins have been detached since the pre-upgrade version", + detachedPlugins.size(), greaterThan(1)); + assertThat("Plugins detached between the pre-upgrade version and the current version should be installed", + getInstalledDetachedPlugins(r, detachedPlugins).size(), equalTo(detachedPlugins.size())); + assertNoFailedPlugins(r); + }); + } + + @Test + public void newInstallation() { + rr.then(r -> { + List detachedPlugins = ClassicPluginStrategy.getDetachedPlugins(); + assertThat("Detached plugins should exist", detachedPlugins, not(empty())); + assertThat("Detached plugins should not be installed on a new instance", + getInstalledDetachedPlugins(r, detachedPlugins), empty()); + assertNoFailedPlugins(r); + }); + rr.then(r -> { + List detachedPlugins = ClassicPluginStrategy.getDetachedPlugins(); + assertThat("Detached plugins should exist", detachedPlugins, not(empty())); + assertThat("Detached plugins should not be installed after restarting", + getInstalledDetachedPlugins(r, detachedPlugins), empty()); + assertNoFailedPlugins(r); + }); + } + + private List getInstalledDetachedPlugins(JenkinsRule r, List detachedPlugins) { + PluginManager pluginManager = r.jenkins.getPluginManager(); + List installedPlugins = new ArrayList<>(); + for (DetachedPlugin plugin : detachedPlugins) { + PluginWrapper wrapper = pluginManager.getPlugin(plugin.getShortName()); + if (wrapper != null) { + installedPlugins.add(wrapper); + assertTrue("Detached plugins should be active if installed", wrapper.isActive()); + assertThat("Detached plugins should not have dependency errors", wrapper.getDependencyErrors(), empty()); + } + } + return installedPlugins; + } + + private void assertNoFailedPlugins(JenkinsRule r) { + assertThat("Detached plugins and their dependencies should not fail to install", + r.jenkins.getPluginManager().getFailedPlugins(), empty()); + } + +} diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins1/config.xml b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins1/config.xml new file mode 100644 index 000000000000..6fcea351c2fa --- /dev/null +++ b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins1/config.xml @@ -0,0 +1,34 @@ + + + + 1.550 + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULLNAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + All + false + false + + + + All + 0 + + + + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2/jenkins.install.InstallUtil.lastExecVersion b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2/jenkins.install.InstallUtil.lastExecVersion new file mode 100644 index 000000000000..415b19fc3623 --- /dev/null +++ b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2/jenkins.install.InstallUtil.lastExecVersion @@ -0,0 +1 @@ +2.0 \ No newline at end of file