diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index 0038e537ee..3084ef796f 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -251,14 +251,18 @@ public class ClassicPluginStrategy implements PluginStrategy { createClassLoader(paths, dependencyLoader, atts), disableFile, dependencies, optionalDependencies); } - private static void fix(Attributes atts, List optionalDependencies) { + private void fix(Attributes atts, List optionalDependencies) { String pluginName = atts.getValue("Short-Name"); String jenkinsVersion = atts.getValue("Jenkins-Version"); if (jenkinsVersion==null) jenkinsVersion = atts.getValue("Hudson-Version"); - - optionalDependencies.addAll(DetachedPluginsUtil.getImpliedDependencies(pluginName, jenkinsVersion)); + + for (Dependency d : DetachedPluginsUtil.getImpliedDependencies(pluginName, jenkinsVersion)) { + LOGGER.fine(() -> "implied dep " + pluginName + " → " + d.shortName); + pluginManager.considerDetachedPlugin(d.shortName); + optionalDependencies.add(d); + } } /** diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 187547a1b0..266f7b48fa 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -600,6 +600,25 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas }}); } + void considerDetachedPlugin(String shortName) { + if (new File(rootDir, shortName + ".jpi").isFile()) { + LOGGER.fine(() -> "not considering loading a detached dependency " + shortName + " as it is already on disk"); + return; + } + LOGGER.fine(() -> "considering loading a detached dependency " + shortName); + for (String loadedFile : loadPluginsFromWar("/WEB-INF/detached-plugins", (dir, name) -> normalisePluginName(name).equals(shortName))) { + String loaded = normalisePluginName(loadedFile); + File arc = new File(rootDir, loaded + ".jpi"); + LOGGER.info(() -> "Loading a detached plugin as a dependency: " + arc); + try { + plugins.add(strategy.createPluginWrapper(arc)); + } catch (IOException e) { + failedPlugins.add(new FailedPlugin(arc.getName(), e)); + } + + } + } + protected @Nonnull Set loadPluginsFromWar(@Nonnull String fromPath) { return loadPluginsFromWar(fromPath, null); } diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 56000f32b8..c5570b7839 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -143,6 +143,7 @@ public class UpdateSite { */ private static final String signatureValidatorPrefix = "update site"; + private static final Set warnedMissing = Collections.synchronizedSet(new HashSet<>()); public UpdateSite(String id, String url) { this.id = id; @@ -1093,7 +1094,7 @@ public class UpdateSite { VersionNumber requiredVersion = e.getValue() != null ? new VersionNumber(e.getValue()) : null; Plugin depPlugin = Jenkins.getInstance().getUpdateCenter().getPlugin(e.getKey(), requiredVersion); if (depPlugin == null) { - LOGGER.log(Level.WARNING, "Could not find dependency {0} of {1}", new Object[] {e.getKey(), name}); + LOGGER.log(warnedMissing.add(e.getKey()) ? Level.WARNING : Level.FINE, "Could not find dependency {0} of {1}", new Object[] {e.getKey(), name}); continue; } diff --git a/test/src/test/java/hudson/model/AbstractProjectTest.java b/test/src/test/java/hudson/model/AbstractProjectTest.java index 071b5b9ec7..52496a6711 100644 --- a/test/src/test/java/hudson/model/AbstractProjectTest.java +++ b/test/src/test/java/hudson/model/AbstractProjectTest.java @@ -476,9 +476,6 @@ public class AbstractProjectTest { public void configDotXmlSubmissionToDifferentType() throws Exception { TestPluginManager tpm = (TestPluginManager) j.jenkins.pluginManager; tpm.installDetachedPlugin("javadoc"); - tpm.installDetachedPlugin("junit"); - tpm.installDetachedPlugin("display-url-api"); - tpm.installDetachedPlugin("mailer"); tpm.installDetachedPlugin("maven-plugin"); j.jenkins.setCrumbIssuer(null); diff --git a/test/src/test/java/hudson/model/UsageStatisticsTest.java b/test/src/test/java/hudson/model/UsageStatisticsTest.java index ed1f95d782..69af370730 100644 --- a/test/src/test/java/hudson/model/UsageStatisticsTest.java +++ b/test/src/test/java/hudson/model/UsageStatisticsTest.java @@ -24,7 +24,6 @@ package hudson.model; import com.google.common.io.Resources; -import hudson.ClassicPluginStrategy; import hudson.Util; import hudson.model.UsageStatistics.CombinedCipherInputStream; import hudson.node_monitors.ArchitectureMonitor; @@ -118,7 +117,7 @@ public class UsageStatisticsTest { assertThat("No duplicates", reported.contains(name), is(false)); reported.add(name); } - assertThat(reported, containsInAnyOrder("credentials")); + assertThat(reported, hasItem("credentials")); // Compare content to watch out for backwards compatibility compareWithFile("jobs.json", sortJobTypes((JSONObject) o.get("jobs"))); diff --git a/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java b/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java index 2c181cdb4d..f81549e85b 100644 --- a/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java +++ b/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java @@ -24,6 +24,7 @@ package jenkins.install; +import hudson.ClassicPluginStrategy; import jenkins.plugins.DetachedPluginsUtil; import jenkins.plugins.DetachedPluginsUtil.DetachedPlugin; import hudson.Plugin; @@ -34,6 +35,8 @@ import hudson.util.VersionNumber; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; import org.junit.Rule; import org.junit.Test; @@ -44,18 +47,15 @@ import org.jvnet.hudson.test.RestartableJenkinsRule; import org.jvnet.hudson.test.SmokeTest; 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.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import org.jvnet.hudson.test.LoggerRule; @Category(SmokeTest.class) public class LoadDetachedPluginsTest { @Rule public RestartableJenkinsRule rr = PluginManagerUtil.newRestartableJenkinsRule(); + @Rule public LoggerRule logging = new LoggerRule(); @Issue("JENKINS-48365") @Test @@ -119,7 +119,7 @@ public class LoadDetachedPluginsTest { Plugin scriptSecurity = r.jenkins.getPlugin("script-security"); assertThat("Script-security should be installed", scriptSecurity, notNullValue()); assertThat("Dependencies of detached plugins should be upgraded to the required version", - scriptSecurity.getWrapper().getVersionNumber(), equalTo(new VersionNumber("1.18.1"))); + scriptSecurity.getWrapper().getVersionNumber(), equalTo(new VersionNumber("1.56"))); assertNoFailedPlugins(r); }); } @@ -161,6 +161,29 @@ public class LoadDetachedPluginsTest { }); } + @Issue("JENKINS-55582") + @LocalData + @Test + public void installDetachedDependencies() { + logging.record(PluginManager.class, Level.FINE).record(ClassicPluginStrategy.class, Level.FINE); + rr.then(r -> { + List activePlugins = r.jenkins.getPluginManager().getPlugins().stream().filter(PluginWrapper::isActive).map(PluginWrapper::getShortName).collect(Collectors.toList()); + assertThat("we precreated $JENKINS_HOME/plugins/example.jpi so it had better be loaded", activePlugins, hasItem("example")); + { // Check that it links correctly against an implied dependency from a detached plugin: + Class callerC = r.jenkins.pluginManager.uberClassLoader.loadClass("io.jenkins.plugins.example.Caller"); + assertLoader(callerC, "example", r); + Object jdkInstaller = callerC.getMethod("use").invoke(null); + assertLoader(jdkInstaller.getClass(), "jdk-tool", r); + } + assertThat("it had various implicit detached dependencies so those should have been loaded too", activePlugins, hasSize(greaterThan(1))); + }); + } + private void assertLoader(Class c, String expectedPlugin, JenkinsRule r) { + PluginWrapper pw = r.jenkins.pluginManager.whichPlugin(c); + assertNotNull("did not expect to be loading " + c + " from " + c.getClassLoader(), pw); + assertEquals(expectedPlugin, pw.getShortName()); + } + private List getInstalledDetachedPlugins(JenkinsRule r, List detachedPlugins) { PluginManager pluginManager = r.jenkins.getPluginManager(); List installedPlugins = new ArrayList<>(); diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/installDetachedDependencies/example.zip b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/installDetachedDependencies/example.zip new file mode 100644 index 0000000000..3ce8ab40a7 Binary files /dev/null and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/installDetachedDependencies/example.zip differ diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/installDetachedDependencies/plugins/example.jpi b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/installDetachedDependencies/plugins/example.jpi new file mode 100644 index 0000000000..0a2481622f Binary files /dev/null and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/installDetachedDependencies/plugins/example.jpi differ diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency/jenkins.install.InstallUtil.lastExecVersion b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency/jenkins.install.InstallUtil.lastExecVersion new file mode 100644 index 0000000000..415b19fc36 --- /dev/null +++ b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency/jenkins.install.InstallUtil.lastExecVersion @@ -0,0 +1 @@ +2.0 \ No newline at end of file diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency.zip b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency/plugins/script-security.jpi similarity index 65% rename from test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency.zip rename to test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency/plugins/script-security.jpi index 557841273a..39a6c2b267 100644 Binary files a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency.zip and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerDependency/plugins/script-security.jpi differ diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/jenkins.install.InstallUtil.lastExecVersion b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/jenkins.install.InstallUtil.lastExecVersion new file mode 100644 index 0000000000..415b19fc36 --- /dev/null +++ b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/jenkins.install.InstallUtil.lastExecVersion @@ -0,0 +1 @@ +2.0 \ No newline at end of file diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/plugins/command-launcher.jpi b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/plugins/command-launcher.jpi new file mode 100644 index 0000000000..770dd49efe Binary files /dev/null and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/plugins/command-launcher.jpi differ diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin.zip b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/plugins/script-security.jpi similarity index 50% rename from test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin.zip rename to test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/plugins/script-security.jpi index 2c18d023bd..5843cf3822 100644 Binary files a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin.zip and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin/plugins/script-security.jpi differ diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency/jenkins.install.InstallUtil.lastExecVersion b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency/jenkins.install.InstallUtil.lastExecVersion new file mode 100644 index 0000000000..415b19fc36 --- /dev/null +++ b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency/jenkins.install.InstallUtil.lastExecVersion @@ -0,0 +1 @@ +2.0 \ No newline at end of file diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency.zip b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency/plugins/script-security.jpi similarity index 77% rename from test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency.zip rename to test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency/plugins/script-security.jpi index b85561cf0d..5a8e624bca 100644 Binary files a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency.zip and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithOlderDependency/plugins/script-security.jpi differ diff --git a/war/pom.xml b/war/pom.xml index 0604603e11..5bcfd78481 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -387,7 +387,7 @@ THE SOFTWARE. org.jenkins-ci.plugins script-security - 1.18.1 + 1.56 hpi