Skip to content

Commit

Permalink
Download extensions only once and if the version changes (#488)
Browse files Browse the repository at this point in the history
* Download extensions only once and if the version changes

* Update tests

* Update src/test/groovy/hudson/plugins/gradle/injection/MavenExtensionDownloadHandlerTest.groovy

Co-authored-by: Alexis Tual <[email protected]>

* Do not download extensions on agents onOnline

* Fix CCUD extension handling

* Do not set bogus extension repository URL in test

* Disable a couple of tests to validate windows failures

* Reenable tests again

---------

Co-authored-by: Alexis Tual <[email protected]>
  • Loading branch information
welandaz and alextu authored Sep 18, 2024
1 parent 9f7f4ee commit e435729
Show file tree
Hide file tree
Showing 17 changed files with 464 additions and 190 deletions.

This file was deleted.

16 changes: 8 additions & 8 deletions src/main/java/hudson/plugins/gradle/injection/CopyUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<InjectionConfig> injectionConfigSupplier;

public DevelocityComputerListener() {
this(new DevelocityInjector(), new JenkinsInjectionConfig());
this(
new GradleBuildScanInjection(),
new MavenBuildScanInjection(),
new MavenExtensionDownloadHandler(),
new JenkinsInjectionConfig()
);
}

@VisibleForTesting
DevelocityComputerListener(DevelocityInjector injector,
Supplier<InjectionConfig> injectionConfigSupplier) {
this.injector = injector;
DevelocityComputerListener(
GradleBuildScanInjection gradleBuildScanInjection,
MavenBuildScanInjection mavenBuildScanInjection,
MavenExtensionDownloadHandler mavenExtensionDownloadHandler,
Supplier<InjectionConfig> 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<MavenExtension, String> 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)}
Expand All @@ -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<InjectionConfig> {

private JenkinsInjectionConfig() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand All @@ -36,7 +36,6 @@ public class GradleBuildScanInjection implements BuildScanInjection, GradleInjec

private final Supplier<String> initScriptDigest = Suppliers.memoize(() -> unsafeResourceDigest(RESOURCE_INIT_SCRIPT_GRADLE));

@Override
public void inject(Node node, EnvVars envGlobal, EnvVars envComputer) {
if (node == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,53 @@
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.
*/
@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<EnvVars> globalEnvVarsSupplier;
private final Supplier<Collection<Computer>> 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<EnvVars> globalEnvVarsSupplier,
Supplier<Collection<Computer>> computersSupplier) {
this.injector = injector;
InjectionConfigChangeListener(
GradleBuildScanInjection gradleBuildScanInjection,
MavenBuildScanInjection mavenBuildScanInjection,
MavenExtensionDownloadHandler mavenExtensionDownloadHandler,
Supplier<EnvVars> globalEnvVarsSupplier,
Supplier<Collection<Computer>> computersSupplier
) {
this.gradleBuildScanInjection = gradleBuildScanInjection;
this.mavenBuildScanInjection = mavenBuildScanInjection;
this.mavenExtensionDownloadHandler = mavenExtensionDownloadHandler;
this.globalEnvVarsSupplier = globalEnvVarsSupplier;
this.computersSupplier = computersSupplier;
}
Expand All @@ -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<MavenExtension, String> 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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
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;

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());

Expand All @@ -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<MavenExtension, String> extensionsDigest) {
if (node == null) {
return;
}
Expand All @@ -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);
}
Expand All @@ -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<MavenExtension, String> extensionsDigest) {
try {
EnvUtil.setEnvVar(node, JENKINSGRADLEPLUGIN_MAVEN_AUTO_INJECTION, "true");

Expand All @@ -69,25 +72,14 @@ private void inject(InjectionConfig config, Node node, FilePath nodeRootPath) {
LOGGER.info("Injecting Maven extensions " + nodeRootPath);

List<FilePath> 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);
Expand Down Expand Up @@ -125,17 +117,6 @@ private void inject(InjectionConfig config, Node node, FilePath nodeRootPath) {
}
}

private static MavenExtension.RepositoryCredentials getRepositoryCredentials(String repositoryCredentialId) {
List<StandardUsernamePasswordCredentials> 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);
Expand All @@ -147,5 +128,4 @@ private void cleanup(Node node, FilePath rootPath) {
}
}


}
Loading

0 comments on commit e435729

Please sign in to comment.