diff --git a/src/main/java/hudson/plugins/gradle/injection/BuildScanInjection.java b/src/main/java/hudson/plugins/gradle/injection/BuildScanInjection.java deleted file mode 100644 index 99008098..00000000 --- a/src/main/java/hudson/plugins/gradle/injection/BuildScanInjection.java +++ /dev/null @@ -1,10 +0,0 @@ -package hudson.plugins.gradle.injection; - -import hudson.EnvVars; -import hudson.model.Node; - -public interface BuildScanInjection { - - void inject(Node node, EnvVars envGlobal, EnvVars envComputer); - -} diff --git a/src/main/java/hudson/plugins/gradle/injection/CopyUtil.java b/src/main/java/hudson/plugins/gradle/injection/CopyUtil.java index e82861aa..f772e907 100644 --- a/src/main/java/hudson/plugins/gradle/injection/CopyUtil.java +++ b/src/main/java/hudson/plugins/gradle/injection/CopyUtil.java @@ -2,10 +2,14 @@ import hudson.FilePath; import hudson.Util; +import jenkins.model.Jenkins; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; public final class CopyUtil { @@ -19,17 +23,13 @@ public static void copyResourceToNode(FilePath nodePath, String resourceName) th }); } - public static String unsafeResourceDigest(String resourceName) { - try { - return doWithResource(resourceName, Util::getDigestOf); - } catch (IOException | InterruptedException e) { - throw new IllegalStateException(e); - } + public static void copyDownloadedResourceToNode(FilePath controllerRootPath, FilePath nodePath, String resourceName) throws IOException, InterruptedException { + nodePath.copyFrom(controllerRootPath.child(MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR).child(resourceName)); } - public static String readResource(String resourceName) { + public static String unsafeResourceDigest(String resourceName) { try { - return doWithResource(resourceName, IOUtils::toString); + return doWithResource(resourceName, Util::getDigestOf); } catch (IOException | InterruptedException e) { throw new IllegalStateException(e); } diff --git a/src/main/java/hudson/plugins/gradle/injection/DevelocityComputerListener.java b/src/main/java/hudson/plugins/gradle/injection/DevelocityComputerListener.java index 35f523a7..35a57cbb 100644 --- a/src/main/java/hudson/plugins/gradle/injection/DevelocityComputerListener.java +++ b/src/main/java/hudson/plugins/gradle/injection/DevelocityComputerListener.java @@ -4,9 +4,12 @@ import hudson.EnvVars; import hudson.Extension; import hudson.model.Computer; +import hudson.model.Node; import hudson.model.TaskListener; import hudson.slaves.ComputerListener; +import jenkins.model.Jenkins; +import java.util.Map; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -21,29 +24,51 @@ public class DevelocityComputerListener extends ComputerListener { private static final Logger LOGGER = Logger.getLogger(DevelocityComputerListener.class.getName()); - private final DevelocityInjector injector; + private final GradleBuildScanInjection gradleBuildScanInjection; + private final MavenBuildScanInjection mavenBuildScanInjection; + private final MavenExtensionDownloadHandler mavenExtensionDownloadHandler; private final Supplier injectionConfigSupplier; public DevelocityComputerListener() { - this(new DevelocityInjector(), new JenkinsInjectionConfig()); + this( + new GradleBuildScanInjection(), + new MavenBuildScanInjection(), + new MavenExtensionDownloadHandler(), + new JenkinsInjectionConfig() + ); } @VisibleForTesting - DevelocityComputerListener(DevelocityInjector injector, - Supplier injectionConfigSupplier) { - this.injector = injector; + DevelocityComputerListener( + GradleBuildScanInjection gradleBuildScanInjection, + MavenBuildScanInjection mavenBuildScanInjection, + MavenExtensionDownloadHandler mavenExtensionDownloadHandler, + Supplier injectionConfigSupplier + ) { + this.gradleBuildScanInjection = gradleBuildScanInjection; + this.mavenBuildScanInjection = mavenBuildScanInjection; + this.mavenExtensionDownloadHandler = mavenExtensionDownloadHandler; this.injectionConfigSupplier = injectionConfigSupplier; } @Override public void onOnline(Computer computer, TaskListener listener) { try { + InjectionConfig injectionConfig = injectionConfigSupplier.get(); EnvVars globalEnvVars = computer.buildEnvironment(listener); - if (InjectionUtil.globalAutoInjectionCheckEnabled(globalEnvVars) && isFeatureDisabled()) { + if (InjectionUtil.globalAutoInjectionCheckEnabled(globalEnvVars) && injectionConfig.isDisabled()) { return; } - injector.inject(computer, globalEnvVars); + Map extensionsDigest = mavenExtensionDownloadHandler.getExtensionDigests( + () -> Jenkins.get().getRootDir(), injectionConfig + ); + + Node node = computer.getNode(); + EnvVars computerEnvVars = computer.getEnvironment(); + + gradleBuildScanInjection.inject(node, globalEnvVars, computerEnvVars); + mavenBuildScanInjection.inject(node, extensionsDigest); } catch (Throwable t) { /* * We should catch everything because this is not handled by {@link hudson.slaves.SlaveComputer#setChannel(Channel, OutputStream, Channel.Listener)} @@ -58,11 +83,6 @@ public void onOnline(Computer computer, TaskListener listener) { } } - private boolean isFeatureDisabled() { - InjectionConfig injectionConfig = injectionConfigSupplier.get(); - return injectionConfig.isDisabled(); - } - private static final class JenkinsInjectionConfig implements Supplier { private JenkinsInjectionConfig() { diff --git a/src/main/java/hudson/plugins/gradle/injection/DevelocityInjector.java b/src/main/java/hudson/plugins/gradle/injection/DevelocityInjector.java deleted file mode 100644 index 1d87496e..00000000 --- a/src/main/java/hudson/plugins/gradle/injection/DevelocityInjector.java +++ /dev/null @@ -1,40 +0,0 @@ -package hudson.plugins.gradle.injection; - -import com.google.common.annotations.VisibleForTesting; -import hudson.EnvVars; -import hudson.model.Computer; -import hudson.model.Node; - -import java.util.Arrays; -import java.util.Collection; -import java.util.logging.Level; -import java.util.logging.Logger; - -public final class DevelocityInjector { - - private static final Logger LOGGER = Logger.getLogger(DevelocityInjector.class.getName()); - - private final Collection injectors; - - public DevelocityInjector() { - this(new GradleBuildScanInjection(), new MavenBuildScanInjection()); - } - - @VisibleForTesting - DevelocityInjector(BuildScanInjection... injectors) { - this.injectors = Arrays.asList(injectors); - } - - public void inject(Computer computer, EnvVars globalEnvVars) { - try { - Node node = computer.getNode(); - EnvVars computerEnvVars = computer.getEnvironment(); - - for (BuildScanInjection injector : injectors) { - injector.inject(node, globalEnvVars, computerEnvVars); - } - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Error injecting build scans on " + computer.getName(), e); - } - } -} diff --git a/src/main/java/hudson/plugins/gradle/injection/GradleBuildScanInjection.java b/src/main/java/hudson/plugins/gradle/injection/GradleBuildScanInjection.java index 00a70c42..cca9e8ee 100644 --- a/src/main/java/hudson/plugins/gradle/injection/GradleBuildScanInjection.java +++ b/src/main/java/hudson/plugins/gradle/injection/GradleBuildScanInjection.java @@ -20,7 +20,7 @@ import static hudson.plugins.gradle.injection.CopyUtil.*; -public class GradleBuildScanInjection implements BuildScanInjection, GradleInjectionAware { +public class GradleBuildScanInjection implements GradleInjectionAware { private static final Logger LOGGER = Logger.getLogger(GradleBuildScanInjection.class.getName()); @@ -36,7 +36,6 @@ public class GradleBuildScanInjection implements BuildScanInjection, GradleInjec private final Supplier initScriptDigest = Suppliers.memoize(() -> unsafeResourceDigest(RESOURCE_INIT_SCRIPT_GRADLE)); - @Override public void inject(Node node, EnvVars envGlobal, EnvVars envComputer) { if (node == null) { return; diff --git a/src/main/java/hudson/plugins/gradle/injection/InjectionConfigChangeListener.java b/src/main/java/hudson/plugins/gradle/injection/InjectionConfigChangeListener.java index 6cce39bc..2860480d 100644 --- a/src/main/java/hudson/plugins/gradle/injection/InjectionConfigChangeListener.java +++ b/src/main/java/hudson/plugins/gradle/injection/InjectionConfigChangeListener.java @@ -5,13 +5,17 @@ import hudson.Extension; import hudson.XmlFile; import hudson.model.Computer; +import hudson.model.Node; import hudson.model.Saveable; import hudson.model.listeners.SaveableListener; import jenkins.model.Jenkins; import java.util.Arrays; import java.util.Collection; +import java.util.Map; import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Performs build scans auto-injection/cleanup when the {@link InjectionConfig} changes. @@ -19,23 +23,35 @@ @Extension public class InjectionConfigChangeListener extends SaveableListener { - private final DevelocityInjector injector; + private static final Logger LOGGER = Logger.getLogger(InjectionConfigChangeListener.class.getName()); + + private final GradleBuildScanInjection gradleBuildScanInjection; + private final MavenBuildScanInjection mavenBuildScanInjection; + private final MavenExtensionDownloadHandler mavenExtensionDownloadHandler; private final Supplier globalEnvVarsSupplier; private final Supplier> computersSupplier; public InjectionConfigChangeListener() { this( - new DevelocityInjector(), - new JenkinsGlobalEnvVars(), - new JenkinsComputers() + new GradleBuildScanInjection(), + new MavenBuildScanInjection(), + new MavenExtensionDownloadHandler(), + new JenkinsGlobalEnvVars(), + new JenkinsComputers() ); } @VisibleForTesting - InjectionConfigChangeListener(DevelocityInjector injector, - Supplier globalEnvVarsSupplier, - Supplier> computersSupplier) { - this.injector = injector; + InjectionConfigChangeListener( + GradleBuildScanInjection gradleBuildScanInjection, + MavenBuildScanInjection mavenBuildScanInjection, + MavenExtensionDownloadHandler mavenExtensionDownloadHandler, + Supplier globalEnvVarsSupplier, + Supplier> computersSupplier + ) { + this.gradleBuildScanInjection = gradleBuildScanInjection; + this.mavenBuildScanInjection = mavenBuildScanInjection; + this.mavenExtensionDownloadHandler = mavenExtensionDownloadHandler; this.globalEnvVarsSupplier = globalEnvVarsSupplier; this.computersSupplier = computersSupplier; } @@ -50,10 +66,22 @@ public void onChange(Saveable saveable, XmlFile file) { return; } - for (Computer computer : computersSupplier.get()) { - if (computer.isOnline()) { - injector.inject(computer, globalEnvVars); + try { + Map extensionsDigest = mavenExtensionDownloadHandler.ensureExtensionsDownloaded( + () -> Jenkins.get().getRootDir(), injectionConfig + ); + + for (Computer computer : computersSupplier.get()) { + if (computer.isOnline()) { + Node node = computer.getNode(); + EnvVars computerEnvVars = computer.getEnvironment(); + + gradleBuildScanInjection.inject(node, globalEnvVars, computerEnvVars); + mavenBuildScanInjection.inject(node, extensionsDigest); + } } + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Invocation of onChange failed", e); } } } diff --git a/src/main/java/hudson/plugins/gradle/injection/MavenBuildScanInjection.java b/src/main/java/hudson/plugins/gradle/injection/MavenBuildScanInjection.java index 62d0b344..1bffac41 100644 --- a/src/main/java/hudson/plugins/gradle/injection/MavenBuildScanInjection.java +++ b/src/main/java/hudson/plugins/gradle/injection/MavenBuildScanInjection.java @@ -1,15 +1,15 @@ package hudson.plugins.gradle.injection; -import com.cloudbees.plugins.credentials.CredentialsProvider; -import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; -import hudson.EnvVars; import hudson.FilePath; import hudson.model.Node; +import jenkins.model.Jenkins; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -17,7 +17,7 @@ import static hudson.plugins.gradle.injection.MavenExtClasspathUtils.constructExtClasspath; import static hudson.plugins.gradle.injection.MavenExtClasspathUtils.isUnix; -public class MavenBuildScanInjection implements BuildScanInjection, MavenInjectionAware { +public class MavenBuildScanInjection implements MavenInjectionAware { private static final Logger LOGGER = Logger.getLogger(MavenBuildScanInjection.class.getName()); @@ -34,8 +34,7 @@ public class MavenBuildScanInjection implements BuildScanInjection, MavenInjecti private final MavenExtensionsHandler extensionsHandler = new MavenExtensionsHandler(); - @Override - public void inject(Node node, EnvVars envGlobal, EnvVars envComputer) { + public void inject(Node node, Map extensionsDigest) { if (node == null) { return; } @@ -49,7 +48,11 @@ public void inject(Node node, EnvVars envGlobal, EnvVars envComputer) { boolean enabled = isInjectionEnabledForNode(config, node); try { if (enabled) { - inject(config, node, nodeRootPath); + if (!extensionsDigest.isEmpty()) { + inject(config, node, nodeRootPath, extensionsDigest); + } else { + LOGGER.log(Level.WARNING, "Extension digests are not present even though injection is enabled"); + } } else { cleanup(node, nodeRootPath); } @@ -60,7 +63,7 @@ public void inject(Node node, EnvVars envGlobal, EnvVars envComputer) { } } - private void inject(InjectionConfig config, Node node, FilePath nodeRootPath) { + private void inject(InjectionConfig config, Node node, FilePath nodeRootPath, Map extensionsDigest) { try { EnvUtil.setEnvVar(node, JENKINSGRADLEPLUGIN_MAVEN_AUTO_INJECTION, "true"); @@ -69,25 +72,14 @@ private void inject(InjectionConfig config, Node node, FilePath nodeRootPath) { LOGGER.info("Injecting Maven extensions " + nodeRootPath); List extensions = new ArrayList<>(); + FilePath controllerRootPath = Jenkins.get().getRootPath(); + MavenExtension develocityMavenExtension = MavenExtension.getDevelocityMavenExtension(config.getMavenExtensionVersion()); - MavenExtension.RepositoryCredentials repositoryCredentials = getRepositoryCredentials(config.getMavenExtensionRepositoryCredentialId()); - extensions.add(extensionsHandler.downloadExtensionToAgent( - develocityMavenExtension, - config.getMavenExtensionVersion(), - nodeRootPath, - repositoryCredentials, - config.getMavenExtensionRepositoryUrl() - )); - if (InjectionUtil.isValid(InjectionConfig.checkRequiredVersion(config.getCcudExtensionVersion()))) { - extensions.add(extensionsHandler.downloadExtensionToAgent( - MavenExtension.CCUD, - config.getCcudExtensionVersion(), - nodeRootPath, - repositoryCredentials, - config.getMavenExtensionRepositoryUrl() - )); - } else { + extensions.add(extensionsHandler.copyExtensionToAgent(develocityMavenExtension, controllerRootPath, nodeRootPath, extensionsDigest.get(develocityMavenExtension))); + if (InjectionUtil.isInvalid(InjectionConfig.checkRequiredVersion(config.getCcudExtensionVersion()))) { extensionsHandler.deleteExtensionFromAgent(MavenExtension.CCUD, nodeRootPath); + } else { + extensions.add(extensionsHandler.copyExtensionToAgent(MavenExtension.CCUD, controllerRootPath, nodeRootPath, extensionsDigest.get(MavenExtension.CCUD))); } boolean isUnix = isUnix(node); @@ -125,17 +117,6 @@ private void inject(InjectionConfig config, Node node, FilePath nodeRootPath) { } } - private static MavenExtension.RepositoryCredentials getRepositoryCredentials(String repositoryCredentialId) { - List allCredentials - = CredentialsProvider.lookupCredentialsInItem(StandardUsernamePasswordCredentials.class, null, null); - - return allCredentials.stream() - .filter(it -> it.getId().equals(repositoryCredentialId)) - .findFirst() - .map(it -> new MavenExtension.RepositoryCredentials(it.getUsername(), it.getPassword().getPlainText())) - .orElse(null); - } - private void cleanup(Node node, FilePath rootPath) { try { extensionsHandler.deleteAllExtensionsFromAgent(rootPath); @@ -147,5 +128,4 @@ private void cleanup(Node node, FilePath rootPath) { } } - } diff --git a/src/main/java/hudson/plugins/gradle/injection/MavenExtension.java b/src/main/java/hudson/plugins/gradle/injection/MavenExtension.java index 887cd89c..ecf37eae 100644 --- a/src/main/java/hudson/plugins/gradle/injection/MavenExtension.java +++ b/src/main/java/hudson/plugins/gradle/injection/MavenExtension.java @@ -1,23 +1,26 @@ package hudson.plugins.gradle.injection; +import javax.annotation.Nullable; + public enum MavenExtension { - DEVELOCITY("develocity-maven-extension", new MavenCoordinates("com.gradle", "develocity-maven-extension")), - GRADLE_ENTERPRISE("gradle-enterprise-maven-extension", new MavenCoordinates("com.gradle", "gradle-enterprise-maven-extension")), - CCUD("common-custom-user-data-maven-extension", new MavenCoordinates("com.gradle", "common-custom-user-data-maven-extension")), - CONFIGURATION("configuration-maven-extension", new MavenCoordinates("com.gradle", "configuration-maven-extension")); + DEVELOCITY("develocity-maven-extension", "develocity_metadata", new MavenCoordinates("com.gradle", "develocity-maven-extension")), + GRADLE_ENTERPRISE("gradle-enterprise-maven-extension", "develocity_metadata", new MavenCoordinates("com.gradle", "gradle-enterprise-maven-extension")), + CCUD("common-custom-user-data-maven-extension", "ccud_metadata", new MavenCoordinates("com.gradle", "common-custom-user-data-maven-extension")), + CONFIGURATION("configuration-maven-extension", "configuration_metadata", new MavenCoordinates("com.gradle", "configuration-maven-extension")); private static final String EXTENSION_REPOSITORY_PATH = "/com/gradle/%s/%s/%s-%s.jar"; private static final String DEFAULT_REPOSITORY_URL = "https://repo1.maven.org/maven2" + EXTENSION_REPOSITORY_PATH; private static final String JAR_EXTENSION = ".jar"; private static final String LAST_GRADLE_ENTERPRISE_VERSION = "1.20.1"; - private final MavenCoordinates coordinates; - private final String name; + private final String downloadMetadataFileName; + private final MavenCoordinates coordinates; - MavenExtension(String name, MavenCoordinates coordinates) { + MavenExtension(String name, String downloadMetadataFileName, MavenCoordinates coordinates) { this.name = name; + this.downloadMetadataFileName = downloadMetadataFileName; this.coordinates = coordinates; } @@ -27,7 +30,7 @@ public String getTargetJarName() { // Only used for CONFIGURATION extension as the embedded JAR contains the version public String getEmbeddedJarName() { - return name + "-1.0.0" + JAR_EXTENSION; + return this == CONFIGURATION ? name + "-1.0.0" + JAR_EXTENSION : name + JAR_EXTENSION; } public MavenCoordinates getCoordinates() { @@ -38,11 +41,15 @@ public String getName() { return name; } + public String getDownloadMetadataFileName() { + return downloadMetadataFileName; + } + public static MavenExtension getDevelocityMavenExtension(String version) { return version.compareTo(LAST_GRADLE_ENTERPRISE_VERSION) > 0 ? DEVELOCITY : GRADLE_ENTERPRISE; } - public String createDownloadUrl(String version, String repositoryUrl) { + public String createDownloadUrl(String version, @Nullable String repositoryUrl) { if (repositoryUrl == null || InjectionUtil.isInvalid(InjectionConfig.checkUrl(repositoryUrl))) { repositoryUrl = DEFAULT_REPOSITORY_URL; } diff --git a/src/main/java/hudson/plugins/gradle/injection/MavenExtensionDownloadHandler.java b/src/main/java/hudson/plugins/gradle/injection/MavenExtensionDownloadHandler.java new file mode 100644 index 00000000..65a30f4c --- /dev/null +++ b/src/main/java/hudson/plugins/gradle/injection/MavenExtensionDownloadHandler.java @@ -0,0 +1,135 @@ +package hudson.plugins.gradle.injection; + +import com.cloudbees.plugins.credentials.CredentialsProvider; +import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; +import hudson.Util; +import hudson.plugins.gradle.injection.extension.ExtensionClient; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +public class MavenExtensionDownloadHandler implements MavenInjectionAware { + + public static final String DOWNLOAD_CACHE_DIR = "jenkins-gradle-plugin/cache"; + + private final ExtensionClient extensionClient = new ExtensionClient(); + + public Map ensureExtensionsDownloaded(Supplier root, InjectionConfig injectionConfig) throws IOException { + if (!isInjectionDisabledGlobally(injectionConfig)) { + Map extensionsDigest = new HashMap<>(); + Path cacheDir = root.get().toPath().resolve(DOWNLOAD_CACHE_DIR); + + MavenExtension develocityMavenExtension = MavenExtension.getDevelocityMavenExtension(injectionConfig.getMavenExtensionVersion()); + + extensionsDigest.put(develocityMavenExtension, getOrDownloadExtensionDigest(injectionConfig, cacheDir, develocityMavenExtension)); + if (InjectionUtil.isValid(InjectionConfig.checkRequiredVersion(injectionConfig.getCcudExtensionVersion()))) { + extensionsDigest.put(MavenExtension.CCUD, getOrDownloadExtensionDigest(injectionConfig, cacheDir, MavenExtension.CCUD)); + } + + return extensionsDigest; + } + + return Collections.emptyMap(); + } + + public Map getExtensionDigests(Supplier rootDir, InjectionConfig injectionConfig) throws IOException { + if (!isInjectionDisabledGlobally(injectionConfig)) { + Map extensionDigests = new HashMap<>(); + Path cacheDir = rootDir.get().toPath().resolve(DOWNLOAD_CACHE_DIR); + + MavenExtension develocityMavenExtension = MavenExtension.getDevelocityMavenExtension(injectionConfig.getMavenExtensionVersion()); + + getExtensionDigest(cacheDir, develocityMavenExtension).ifPresent(it -> extensionDigests.put(develocityMavenExtension, it)); + if (InjectionUtil.isValid(InjectionConfig.checkRequiredVersion(injectionConfig.getCcudExtensionVersion()))) { + getExtensionDigest(cacheDir, MavenExtension.CCUD).ifPresent(it -> extensionDigests.put(MavenExtension.CCUD, it)); + } + + return extensionDigests; + } + + return Collections.emptyMap(); + } + + private static Optional getExtensionDigest(Path parent, MavenExtension extension) throws IOException { + Path metadataFile = parent.resolve(extension.getDownloadMetadataFileName()); + if (Files.exists(metadataFile)) { + String[] metadata = Files.readString(metadataFile).split(","); + + return Optional.of(metadata[1]); + } + + return Optional.empty(); + } + + private String getOrDownloadExtensionDigest(InjectionConfig injectionConfig, Path parent, MavenExtension extension) throws IOException { + Path metadataFile = parent.resolve(extension.getDownloadMetadataFileName()); + String version = extension == MavenExtension.CCUD + ? injectionConfig.getCcudExtensionVersion() + : injectionConfig.getMavenExtensionVersion(); + + if (Files.exists(metadataFile)) { + String[] metadata = Files.readString(metadataFile).split(","); + String extensionVersion = metadata[0]; + String extensionDigest = metadata[1]; + + if (!extensionVersion.equals(version)) { + return downloadExtension(injectionConfig, parent, extension, metadataFile, version); + } else { + return extensionDigest; + } + } else { + return downloadExtension(injectionConfig, parent, extension, metadataFile, version); + } + } + + private String downloadExtension( + InjectionConfig injectionConfig, + Path parent, + MavenExtension extension, + Path metadataFile, + String version + ) throws IOException { + Files.createDirectories(parent); + + Path jarFile = parent.resolve(extension.getEmbeddedJarName()); + + String downloadUrl = extension.createDownloadUrl(version, injectionConfig.getMavenExtensionRepositoryUrl()); + MavenExtension.RepositoryCredentials repositoryCredentials + = getRepositoryCredentials(injectionConfig.getMavenExtensionRepositoryCredentialId()); + + try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(jarFile))) { + extensionClient.downloadExtension(downloadUrl, repositoryCredentials, outputStream); + } + + String digest = Util.getDigestOf(jarFile.toFile()); + + Files.writeString(metadataFile, version + "," + digest); + + return digest; + } + + private static MavenExtension.RepositoryCredentials getRepositoryCredentials(String repositoryCredentialId) { + if (repositoryCredentialId == null) { + return null; + } + + List allCredentials + = CredentialsProvider.lookupCredentialsInItem(StandardUsernamePasswordCredentials.class, null, null); + + return allCredentials.stream() + .filter(it -> it.getId().equals(repositoryCredentialId)) + .findFirst() + .map(it -> new MavenExtension.RepositoryCredentials(it.getUsername(), it.getPassword().getPlainText())) + .orElse(null); + } +} diff --git a/src/main/java/hudson/plugins/gradle/injection/MavenExtensionsHandler.java b/src/main/java/hudson/plugins/gradle/injection/MavenExtensionsHandler.java index 18160254..876c44a6 100644 --- a/src/main/java/hudson/plugins/gradle/injection/MavenExtensionsHandler.java +++ b/src/main/java/hudson/plugins/gradle/injection/MavenExtensionsHandler.java @@ -3,21 +3,20 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import hudson.FilePath; -import hudson.plugins.gradle.injection.extension.ExtensionClient; -import javax.annotation.Nullable; -import java.io.BufferedOutputStream; import java.io.IOException; +import java.nio.file.Files; import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; +import static hudson.plugins.gradle.injection.CopyUtil.copyDownloadedResourceToNode; import static hudson.plugins.gradle.injection.CopyUtil.copyResourceToNode; import static hudson.plugins.gradle.injection.CopyUtil.unsafeResourceDigest; -public class MavenExtensionsHandler { +public final class MavenExtensionsHandler { static final String LIB_DIR_PATH = "jenkins-gradle-plugin/lib"; @@ -30,14 +29,8 @@ public FilePath copyExtensionToAgent(MavenExtension extension, FilePath rootPath return fileHandlers.get(extension).copyExtensionToAgent(rootPath); } - public FilePath downloadExtensionToAgent( - MavenExtension extension, - String version, - FilePath rootPath, - @Nullable MavenExtension.RepositoryCredentials repositoryCredentials, - @Nullable String repositoryUrl - ) throws IOException, InterruptedException { - return fileHandlers.get(extension).downloadExtensionToAgent(rootPath, version, repositoryCredentials, repositoryUrl); + public FilePath copyExtensionToAgent(MavenExtension extension, FilePath controllerRootPath, FilePath rootPath, String digest) throws IOException, InterruptedException { + return fileHandlers.get(extension).copyExtensionToAgent(controllerRootPath, rootPath, digest); } public void deleteExtensionFromAgent(MavenExtension extension, FilePath rootPath) throws IOException, InterruptedException { @@ -55,7 +48,8 @@ private static final class MavenExtensionFileHandler { MavenExtensionFileHandler(MavenExtension extension) { this.extension = extension; - this.extensionDigest = Suppliers.memoize(() -> unsafeResourceDigest(extension.getEmbeddedJarName())); + this.extensionDigest = + Suppliers.memoize(() -> unsafeResourceDigest(extension.getEmbeddedJarName())); } /** @@ -67,23 +61,18 @@ public FilePath copyExtensionToAgent(FilePath rootPath) throws IOException, Inte if (extensionChanged(extensionLocation)) { copyResourceToNode(extensionLocation, extension.getEmbeddedJarName()); } + return extensionLocation; } /** - * Downloads the extension to the agent from a repository and returns a path to the extension on the agent. + * Copies the extension to the agent, if it is not already present, and returns a path to the extension + * on the agent. Uses passed digest to verify if the extension has changed. */ - public FilePath downloadExtensionToAgent( - FilePath rootPath, - String version, - @Nullable MavenExtension.RepositoryCredentials repositoryCredentials, - @Nullable String repositoryUrl - ) throws IOException, InterruptedException { + public FilePath copyExtensionToAgent(FilePath controllerRootPath, FilePath rootPath, String digest) throws IOException, InterruptedException { FilePath extensionLocation = getExtensionLocation(rootPath); - try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(extensionLocation.write())) { - ExtensionClient.INSTANCE.downloadExtension( - extension.createDownloadUrl(version, repositoryUrl), repositoryCredentials, bufferedOutputStream - ); + if (extensionChanged(extensionLocation, digest)) { + copyDownloadedResourceToNode(controllerRootPath, extensionLocation, extension.getEmbeddedJarName()); } return extensionLocation; @@ -101,13 +90,18 @@ private FilePath getExtensionLocation(FilePath rootPath) { } private boolean extensionChanged(FilePath nodePath) throws IOException, InterruptedException { + return extensionChanged(nodePath, extensionDigest.get()); + } + + private boolean extensionChanged(FilePath nodePath, String digest) throws IOException, InterruptedException { if (!nodePath.exists()) { return true; } String existingFileDigest = nodePath.digest(); - return !Objects.equals(existingFileDigest, extensionDigest.get()); + + return !Objects.equals(existingFileDigest, digest); } - } + } } diff --git a/src/main/java/hudson/plugins/gradle/injection/extension/ExtensionClient.java b/src/main/java/hudson/plugins/gradle/injection/extension/ExtensionClient.java index d079862e..83468595 100644 --- a/src/main/java/hudson/plugins/gradle/injection/extension/ExtensionClient.java +++ b/src/main/java/hudson/plugins/gradle/injection/extension/ExtensionClient.java @@ -15,11 +15,9 @@ public final class ExtensionClient { - public static final ExtensionClient INSTANCE = new ExtensionClient(); - private final OkHttpClient httpClient; - private ExtensionClient() { + public ExtensionClient() { this.httpClient = new OkHttpClient().newBuilder() .readTimeout(60, TimeUnit.SECONDS) .build(); diff --git a/src/test/groovy/hudson/plugins/gradle/injection/BuildScanInjectionMavenIntegrationTest.groovy b/src/test/groovy/hudson/plugins/gradle/injection/BuildScanInjectionMavenIntegrationTest.groovy index 2b9167eb..ebd7fb04 100644 --- a/src/test/groovy/hudson/plugins/gradle/injection/BuildScanInjectionMavenIntegrationTest.groovy +++ b/src/test/groovy/hudson/plugins/gradle/injection/BuildScanInjectionMavenIntegrationTest.groovy @@ -42,7 +42,7 @@ class BuildScanInjectionMavenIntegrationTest extends BaseMavenIntegrationTest { private static final String POM_XML = '4.0.0com.examplemy-pom0.1-SNAPSHOTpommy-pommy-pom' private static final String INJECT_CCUD = '[DEBUG] Executing extension: CommonCustomUserDataDevelocityListener' - def 'does not copy configuration extension if it was not changed'() { + def 'does not copy #extension if it was not changed'() { when: def slave = createSlaveAndTurnOnInjection() turnOnBuildInjectionAndRestart(slave) @@ -51,7 +51,7 @@ class BuildScanInjectionMavenIntegrationTest extends BaseMavenIntegrationTest { then: extensionDirectory.list().size() == 3 - def originalExtension = extensionDirectory.list().find { it.name == MavenExtension.CONFIGURATION.getTargetJarName() } + def originalExtension = extensionDirectory.list().find { it.name == extension } originalExtension != null def originalExtensionLastModified = originalExtension.lastModified() originalExtensionLastModified > 0 @@ -66,13 +66,16 @@ class BuildScanInjectionMavenIntegrationTest extends BaseMavenIntegrationTest { then: extensionDirectory.list().size() == 3 - def updatedExtension = extensionDirectory.list().find { it.name == MavenExtension.CONFIGURATION.getTargetJarName() } + def updatedExtension = extensionDirectory.list().find { it.name == extension } updatedExtension != null updatedExtension.lastModified() == originalExtensionLastModified updatedExtension.digest() == originalExtensionDigest + + where: + extension << [DEVELOCITY_EXTENSION_JAR, CCUD_EXTENSION_JAR, CONFIGURATION_EXTENSION_JAR] } - def 'copies a new version of configuration extension if it was changed'() { + def 'copies a new version of #extension if it was changed'() { when: def slave = createSlaveAndTurnOnInjection() turnOnBuildInjectionAndRestart(slave) @@ -81,7 +84,7 @@ class BuildScanInjectionMavenIntegrationTest extends BaseMavenIntegrationTest { then: extensionDirectory.list().size() == 3 - def originalExtension = extensionDirectory.list().find { it.name == MavenExtension.CONFIGURATION.getTargetJarName() } + def originalExtension = extensionDirectory.list().find { it.name == extension } originalExtension != null def originalExtensionLastModified = originalExtension.lastModified() originalExtensionLastModified > 0 @@ -98,7 +101,7 @@ class BuildScanInjectionMavenIntegrationTest extends BaseMavenIntegrationTest { extensionDirectory = slave.toComputer().node.rootPath.child(MavenExtensionsHandler.LIB_DIR_PATH) then: - extensionDirectory.list().find { it.name == MavenExtension.CONFIGURATION.getTargetJarName() }?.lastModified() != originalExtensionLastModified + extensionDirectory.list().find { it.name == extension }?.lastModified() != originalExtensionLastModified when: restartSlave(slave) @@ -106,10 +109,13 @@ class BuildScanInjectionMavenIntegrationTest extends BaseMavenIntegrationTest { extensionDirectory = slave.toComputer().node.rootPath.child(MavenExtensionsHandler.LIB_DIR_PATH) then: - def updatedGeExtension = extensionDirectory.list().find { it.name == MavenExtension.CONFIGURATION.getTargetJarName() } + def updatedGeExtension = extensionDirectory.list().find { it.name == extension } updatedGeExtension != null updatedGeExtension.lastModified() != originalExtensionLastModified updatedGeExtension.digest() == originalExtensionDigest + + where: + extension << [DEVELOCITY_EXTENSION_JAR, CCUD_EXTENSION_JAR, CONFIGURATION_EXTENSION_JAR] } @Issue('https://issues.jenkins.io/browse/JENKINS-70663') diff --git a/src/test/groovy/hudson/plugins/gradle/injection/DevelocityComputerListenerTest.groovy b/src/test/groovy/hudson/plugins/gradle/injection/DevelocityComputerListenerTest.groovy index b7a684ef..8ec54419 100644 --- a/src/test/groovy/hudson/plugins/gradle/injection/DevelocityComputerListenerTest.groovy +++ b/src/test/groovy/hudson/plugins/gradle/injection/DevelocityComputerListenerTest.groovy @@ -4,6 +4,8 @@ import hudson.EnvVars import hudson.model.Computer import hudson.model.Node import hudson.model.TaskListener +import org.junit.Rule +import org.junit.rules.TemporaryFolder import spock.lang.Specification import spock.lang.Subject import spock.lang.Unroll @@ -14,12 +16,17 @@ class DevelocityComputerListenerTest extends Specification { def globalEnvVars = new EnvVars([GLOBAL: "true"]) def computer = Mock(Computer) - def injector = Mock(BuildScanInjection) + def gradleBuildScanInjection = Mock(GradleBuildScanInjection) + def mavenBuildScanInjection = Mock(MavenBuildScanInjection) + def mavenExtensionDownloadHandler = Mock(MavenExtensionDownloadHandler) def injectionConfig = Mock(InjectionConfig) @Subject def gradleEnterpriseComputerListener = - new DevelocityComputerListener(new DevelocityInjector(injector), { injectionConfig }) + new DevelocityComputerListener(gradleBuildScanInjection, mavenBuildScanInjection, mavenExtensionDownloadHandler, { injectionConfig }) + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder() @Unroll def "performs injection when computer gets online (isGlobalAutoInjectionCheckEnabled=#isGlobalAutoInjectionCheckEnabled, isGlobalInjectionEnabled=#isGlobalInjectionEnabled)"() { @@ -34,12 +41,15 @@ class DevelocityComputerListenerTest extends Specification { def computerEnvVars = new EnvVars([COMPUTER: "ture"]) computer.getNode() >> node computer.getEnvironment() >> computerEnvVars + def root = tempFolder.newFolder() + + mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ root }, injectionConfig) >> { } when: gradleEnterpriseComputerListener.onOnline(computer, Mock(TaskListener)) then: - (isInjectionExpected ? 1 : 0) * injector.inject(node, globalEnvVars, computerEnvVars) + (isInjectionExpected ? 1 : 0) * gradleBuildScanInjection.inject(node, globalEnvVars, computerEnvVars) where: isGlobalAutoInjectionCheckEnabled | isGlobalInjectionEnabled || isInjectionExpected @@ -58,8 +68,11 @@ class DevelocityComputerListenerTest extends Specification { def computerEnvVars = new EnvVars([COMPUTER: "ture"]) computer.getNode() >> node computer.getEnvironment() >> computerEnvVars + def root = tempFolder.newFolder() + mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ root }, injectionConfig) >> { } - injector.inject(node, globalEnvVars, computerEnvVars) >> { throw new ExpectedException() } + gradleBuildScanInjection.inject(node, globalEnvVars, computerEnvVars) >> { throw new ExpectedException() } + mavenBuildScanInjection.inject(node, [:]) when: gradleEnterpriseComputerListener.onOnline(computer, Mock(TaskListener)) diff --git a/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigChangeListenerTest.groovy b/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigChangeListenerTest.groovy index 66f4ebf9..1d1e6151 100644 --- a/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigChangeListenerTest.groovy +++ b/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigChangeListenerTest.groovy @@ -4,6 +4,8 @@ import hudson.EnvVars import hudson.XmlFile import hudson.model.Computer import hudson.model.Node +import org.junit.Rule +import org.junit.rules.TemporaryFolder import spock.lang.Specification import spock.lang.Subject import spock.lang.Unroll @@ -16,12 +18,17 @@ class InjectionConfigChangeListenerTest extends Specification { def globalEnvVars = new EnvVars([GLOBAL: "true"]) def computer = Mock(Computer) - def injector = Mock(BuildScanInjection) + def gradleBuildScanInjection = Mock(GradleBuildScanInjection) + def mavenBuildScanInjection = Mock(MavenBuildScanInjection) + def mavenExtensionDownloadHandler = Mock(MavenExtensionDownloadHandler) def injectionConfig = Mock(InjectionConfig) + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder() + @Subject def injectionConfigChangeListener = - new InjectionConfigChangeListener(new DevelocityInjector(injector), { globalEnvVars }, { [computer] }) + new InjectionConfigChangeListener(gradleBuildScanInjection, mavenBuildScanInjection, mavenExtensionDownloadHandler, { globalEnvVars }, { [computer] }) @Unroll def "performs injection when configuration changes (isGlobalAutoInjectionCheckEnabled=#isGlobalAutoInjectionCheckEnabled, isGlobalInjectionEnabled=#isGlobalInjectionEnabled, isComputerOffline=#isComputerOffline)"() { @@ -36,12 +43,14 @@ class InjectionConfigChangeListenerTest extends Specification { def computerEnvVars = new EnvVars([COMPUTER: "ture"]) computer.getNode() >> node computer.getEnvironment() >> computerEnvVars + def root = tempFolder.newFolder() + mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ root }, injectionConfig) >> {} when: injectionConfigChangeListener.onChange(injectionConfig, UNUSED_XML_FILE) then: - (isInjectionExpected ? 1 : 0) * injector.inject(node, globalEnvVars, computerEnvVars) + (isInjectionExpected ? 1 : 0) * gradleBuildScanInjection.inject(node, globalEnvVars, computerEnvVars) where: isGlobalAutoInjectionCheckEnabled | isGlobalInjectionEnabled | isComputerOffline || isInjectionExpected @@ -64,8 +73,11 @@ class InjectionConfigChangeListenerTest extends Specification { def computerEnvVars = new EnvVars([COMPUTER: "ture"]) computer.getNode() >> node computer.getEnvironment() >> computerEnvVars + def root = tempFolder.newFolder() + mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ root }, injectionConfig) >> {} - injector.inject(node, globalEnvVars, computerEnvVars) >> { throw new ExpectedException() } + gradleBuildScanInjection.inject(node, globalEnvVars, computerEnvVars) >> { throw new ExpectedException() } + mavenBuildScanInjection.inject(node, [:]) when: injectionConfigChangeListener.onChange(injectionConfig, UNUSED_XML_FILE) diff --git a/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigTest.groovy b/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigTest.groovy index bdbba722..a8cd8f60 100644 --- a/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigTest.groovy +++ b/src/test/groovy/hudson/plugins/gradle/injection/InjectionConfigTest.groovy @@ -178,7 +178,6 @@ class InjectionConfigTest extends BaseJenkinsIntegrationTest { form.getInputByName("_.mavenExtensionVersion").setValueAttribute("1.22") form.getInputByName("_.ccudExtensionVersion").setValueAttribute("2.0") - form.getInputByName("_.mavenExtensionRepositoryUrl").setValueAttribute("https://localhost/repostiry") getAddButton(form, "Maven auto-injection enabled nodes").click() form.getInputsByName("_.label").last().setValueAttribute("maven1") @@ -204,7 +203,6 @@ class InjectionConfigTest extends BaseJenkinsIntegrationTest { mavenExtensionVersion == "1.22" ccudExtensionVersion == "2.0" - mavenExtensionRepositoryUrl == "https://localhost/repostiry" mavenInjectionEnabledNodes*.label == ['maven1'] mavenInjectionDisabledNodes*.label == ['maven2'] } diff --git a/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionDownloadHandlerTest.groovy b/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionDownloadHandlerTest.groovy new file mode 100644 index 00000000..a61ea731 --- /dev/null +++ b/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionDownloadHandlerTest.groovy @@ -0,0 +1,101 @@ +package hudson.plugins.gradle.injection + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification +import spock.lang.Subject + +class MavenExtensionDownloadHandlerTest extends Specification { + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder() + + @Subject + private final MavenExtensionDownloadHandler mavenExtensionDownloadHandler = new MavenExtensionDownloadHandler() + + def 'extensions are not redownloaded if config has not changed'() { + given: + def controllerFolder = tempFolder.newFolder() + + def originalConfig = Mock(InjectionConfig) + with(originalConfig) { + enabled >> true + server >> 'https://scans.gradle.com' + mavenExtensionVersion >> '1.22.1' + ccudExtensionVersion >> '2.0.1' + } + + when: + def extensionsDownloaded = mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ controllerFolder }, originalConfig) + + then: + extensionsDownloaded.size() == 2 + def originalDevelocityDigest = extensionsDownloaded.get(MavenExtension.DEVELOCITY) + def originalCcudDigest = extensionsDownloaded.get(MavenExtension.CCUD) + + originalDevelocityDigest != null && originalCcudDigest != null + + when: + def originalDevelocityLastModified = new File(controllerFolder, MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR + "/" + MavenExtension.DEVELOCITY.getEmbeddedJarName()).lastModified() + def originalCcudLastModified = new File(controllerFolder, MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR + "/" + MavenExtension.CCUD.getEmbeddedJarName()).lastModified() + + def sameExtensions = mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ controllerFolder }, originalConfig) + + then: + sameExtensions.size() == 2 + + def sameDevelocityLastModified = new File(controllerFolder, MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR + "/" + MavenExtension.DEVELOCITY.getEmbeddedJarName()).lastModified() + def sameCcudLastModified = new File(controllerFolder, MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR + "/" + MavenExtension.CCUD.getEmbeddedJarName()).lastModified() + + def sameDevelocityDigest = sameExtensions.get(MavenExtension.DEVELOCITY) + def sameCcudDigest = sameExtensions.get(MavenExtension.CCUD) + + sameDevelocityDigest == originalDevelocityDigest && sameCcudDigest == originalCcudDigest + sameDevelocityLastModified == originalDevelocityLastModified && sameCcudLastModified == originalCcudLastModified + } + + def 'configuration change triggers re-download of the extensions'() { + given: + def controllerFolder = tempFolder.newFolder() + + def originalConfig = Mock(InjectionConfig) + with(originalConfig) { + enabled >> true + server >> 'https://scans.gradle.com' + mavenExtensionVersion >> '1.22' + ccudExtensionVersion >> '2.0' + } + + when: + def extensionsDownloaded = mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ controllerFolder }, originalConfig) + + then: + extensionsDownloaded.size() == 2 + def originalDevelocityDigest = extensionsDownloaded.get(MavenExtension.DEVELOCITY) + def originalCcudDigest = extensionsDownloaded.get(MavenExtension.CCUD) + + originalDevelocityDigest != null && originalCcudDigest != null + + when: + def updatedConfig = Mock(InjectionConfig) + with(updatedConfig) { + enabled >> true + server >> 'https://scans.gradle.com' + mavenExtensionVersion >> '1.22.1' + ccudExtensionVersion >> '2.0.1' + } + + def redownloadedExtensions = mavenExtensionDownloadHandler.ensureExtensionsDownloaded({ controllerFolder }, updatedConfig) + + then: + redownloadedExtensions.size() == 2 + def redownloadedDevelocityDigest = redownloadedExtensions.get(MavenExtension.DEVELOCITY) + def redownloadedCcudDigest = redownloadedExtensions.get(MavenExtension.CCUD) + + redownloadedDevelocityDigest != null && redownloadedCcudDigest != null + + and: + redownloadedDevelocityDigest != originalDevelocityDigest && redownloadedCcudDigest != originalCcudDigest + } + +} diff --git a/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionsHandlerTest.groovy b/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionsHandlerTest.groovy index fe4844d7..edea16a6 100644 --- a/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionsHandlerTest.groovy +++ b/src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionsHandlerTest.groovy @@ -1,6 +1,7 @@ package hudson.plugins.gradle.injection import hudson.FilePath +import hudson.plugins.gradle.injection.extension.ExtensionClient import org.junit.Rule import org.junit.rules.TemporaryFolder import spock.lang.Specification @@ -14,6 +15,8 @@ class MavenExtensionsHandlerTest extends Specification { @Subject MavenExtensionsHandler mavenExtensionsHandler = new MavenExtensionsHandler() + ExtensionClient extensionClient = new ExtensionClient() + def "only copies configuration extension if it doesn't exist"() { given: def folder = tempFolder.newFolder() @@ -37,19 +40,32 @@ class MavenExtensionsHandlerTest extends Specification { secondLastModified == firstLastModified } - def "downloads Develocity/CCUD extensions from the default repository"() { + def "copies Develocity/CCUD extensions when digest doesn't match"() { given: - def folder = tempFolder.newFolder() - def root = new FilePath(folder) + def controllerFolder = tempFolder.newFolder() + def agentFolder = tempFolder.newFolder() + def controllerRoot = new FilePath(controllerFolder) + def agentRoot = new FilePath(agentFolder) + def cacheDirectory = controllerRoot.child(MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR) + + cacheDirectory.child(MavenExtension.DEVELOCITY.getEmbeddedJarName()).write() + .withCloseable { + extensionClient.downloadExtension(MavenExtension.DEVELOCITY.createDownloadUrl("1.22.1", null), null, it) + } + + cacheDirectory.child(MavenExtension.CCUD.getEmbeddedJarName()).write() + .withCloseable { + extensionClient.downloadExtension(MavenExtension.CCUD.createDownloadUrl("2.0.1", null), null, it) + } when: - def firstFilePath = mavenExtensionsHandler.downloadExtensionToAgent(MavenExtension.DEVELOCITY, "1.22", root, null, null) + def firstFilePath = mavenExtensionsHandler.copyExtensionToAgent(MavenExtension.DEVELOCITY, controllerRoot, agentRoot, null) then: firstFilePath.exists() when: - def secondFilePath = mavenExtensionsHandler.downloadExtensionToAgent(MavenExtension.CCUD, "2.0", root, null, null) + def secondFilePath = mavenExtensionsHandler.copyExtensionToAgent(MavenExtension.CCUD, controllerRoot, agentRoot, null) then: secondFilePath.exists() @@ -57,25 +73,42 @@ class MavenExtensionsHandlerTest extends Specification { def "removes all files"() { given: - def folder = tempFolder.newFolder() - def root = new FilePath(folder) + def controllerFolder = tempFolder.newFolder() + def agentFolder = tempFolder.newFolder() + def controllerRoot = new FilePath(controllerFolder) + def agentRoot = new FilePath(agentFolder) + def cacheDirectory = controllerRoot.child(MavenExtensionDownloadHandler.DOWNLOAD_CACHE_DIR) + + cacheDirectory.child(MavenExtension.DEVELOCITY.getEmbeddedJarName()).write() + .withCloseable { + extensionClient.downloadExtension(MavenExtension.DEVELOCITY.createDownloadUrl("1.22.1", null), null, it) + } + + cacheDirectory.child(MavenExtension.CCUD.getEmbeddedJarName()).write() + .withCloseable { + extensionClient.downloadExtension(MavenExtension.CCUD.createDownloadUrl("2.0.1", null), null, it) + } when: - def geExtensionFilePath = mavenExtensionsHandler.downloadExtensionToAgent(MavenExtension.DEVELOCITY, "1.22", root, null, null) - def ccudExtensionFilePath = mavenExtensionsHandler.downloadExtensionToAgent(MavenExtension.CCUD, "2.0", root, null, null) + def firstFilePath = mavenExtensionsHandler.copyExtensionToAgent(MavenExtension.DEVELOCITY, controllerRoot, agentRoot, null) then: - geExtensionFilePath.exists() - ccudExtensionFilePath.exists() + firstFilePath.exists() + + when: + def secondFilePath = mavenExtensionsHandler.copyExtensionToAgent(MavenExtension.CCUD, controllerRoot, agentRoot, null) + + then: + secondFilePath.exists() - geExtensionFilePath.getParent().remote == ccudExtensionFilePath.getParent().remote + firstFilePath.getParent().remote == secondFilePath.getParent().remote when: - mavenExtensionsHandler.deleteAllExtensionsFromAgent(root) + mavenExtensionsHandler.deleteAllExtensionsFromAgent(agentRoot) then: - !geExtensionFilePath.exists() - !ccudExtensionFilePath.exists() + !firstFilePath.exists() + !secondFilePath.exists() } }