diff --git a/src/main/java/io/jenkins/plugins/jfrog/BinaryInstaller.java b/src/main/java/io/jenkins/plugins/jfrog/BinaryInstaller.java index b1271ce8..268f9006 100644 --- a/src/main/java/io/jenkins/plugins/jfrog/BinaryInstaller.java +++ b/src/main/java/io/jenkins/plugins/jfrog/BinaryInstaller.java @@ -3,26 +3,16 @@ import hudson.FilePath; import hudson.model.Node; import hudson.model.TaskListener; -import hudson.remoting.VirtualChannel; import hudson.tools.ToolInstallation; import hudson.tools.ToolInstaller; import hudson.tools.ToolInstallerDescriptor; -import hudson.util.Secret; +import io.jenkins.plugins.jfrog.callables.JFrogCliDownloader; import io.jenkins.plugins.jfrog.configuration.JFrogPlatformInstance; -import jenkins.MasterToSlaveFileCallable; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Header; import org.jfrog.build.client.ProxyConfiguration; -import org.jfrog.build.extractor.clientConfiguration.client.artifactory.ArtifactoryManager; -import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import static io.jenkins.plugins.jfrog.Utils.createProxyConfiguration; -import static org.jfrog.build.client.DownloadResponse.SHA256_HEADER_NAME; /** @@ -31,17 +21,6 @@ * @author gail */ public abstract class BinaryInstaller extends ToolInstaller { - /** - * decoded "[RELEASE]" for thee download url - */ - private static final String RELEASE = "[RELEASE]"; - - /** - * The name of the file that contains the JFrog CLI binary sha256. - * The file will help us determine if we should download an updated version or skip it. - */ - private static final String SHA256_FILE_NAME = "sha256"; - protected BinaryInstaller(String label) { super(label); } @@ -71,104 +50,10 @@ public String getId() { } } - /** - * Download and locate the JFrog CLI binary in the specific build home directory. - * - * @param toolLocation - Location of the tool directory on the fileSystem - * @param log - Job task listener - * @param providedVersion - Version provided by the user. empty string indicates the latest version - * @param instance - JFrogPlatformInstance contains url and credentials needed for the downloading operation - * @param repository - Identifies the repository in Artifactory where the CLIs binary is stored - * @param binaryName - 'jf' or 'jf.exe' - * @param proxyConfiguration - The proxy configuration or null if not configured - * @throws IOException in case of any I/O error. - */ - private static void downloadJfrogCli(File toolLocation, TaskListener log, String providedVersion, - JFrogPlatformInstance instance, String repository, String binaryName, ProxyConfiguration proxyConfiguration) throws IOException { - // An empty string indicates the latest version. - String version = StringUtils.defaultIfBlank(providedVersion, RELEASE); - String cliUrlSuffix = String.format("/%s/v2-jf/%s/jfrog-cli-%s/%s", repository, version, OsUtils.getOsDetails(), binaryName); - - JenkinsBuildInfoLog buildInfoLog = new JenkinsBuildInfoLog(log); - - // Downloading binary from Artifactory - try (ArtifactoryManager manager = new ArtifactoryManager(instance.inferArtifactoryUrl(), Secret.toString(instance.getCredentialsConfig().getUsername()), - Secret.toString(instance.getCredentialsConfig().getPassword()), Secret.toString(instance.getCredentialsConfig().getAccessToken()), buildInfoLog)) { - if (proxyConfiguration != null) { - manager.setProxyConfiguration(proxyConfiguration); - } - // Getting updated cli binary's sha256 form Artifactory. - String artifactorySha256 = getArtifactSha256(manager, cliUrlSuffix); - if (shouldDownloadTool(toolLocation, artifactorySha256)) { - if (version.equals(RELEASE)) { - log.getLogger().printf("Download '%s' latest version from: %s%n", binaryName, instance.inferArtifactoryUrl() + cliUrlSuffix); - } else { - log.getLogger().printf("Download '%s' version %s from: %s%n", binaryName, version, instance.inferArtifactoryUrl() + cliUrlSuffix); - } - File downloadResponse = manager.downloadToFile(cliUrlSuffix, new File(toolLocation, binaryName).getPath()); - if (!downloadResponse.setExecutable(true)) { - throw new IOException("No permission to add execution permission to binary"); - } - createSha256File(toolLocation, artifactorySha256); - } - } - } - - private static void createSha256File(File toolLocation, String artifactorySha256) throws IOException { - File file = new File(toolLocation, SHA256_FILE_NAME); - Files.write(file.toPath(), artifactorySha256.getBytes(StandardCharsets.UTF_8)); - } - - /** - * We should skip the download if the tool's directory already contains the specific version, otherwise we should download it. - * A file named 'sha256' contains the specific binary sha256. - * If the file sha256 has not changed, we will skip the download, otherwise we will download and overwrite the existing files. - * - * @param toolLocation - expected location of the tool on the fileSystem. - * @param artifactorySha256 - sha256 of the expected file in artifactory. - */ - private static boolean shouldDownloadTool(File toolLocation, String artifactorySha256) throws IOException { - // In case no sha256 was provided (for example when the customer blocks headers) download the tool. - if (artifactorySha256.isEmpty()) { - return true; - } - // Looking for the sha256 file in the tool directory. - Path path = toolLocation.toPath().resolve(SHA256_FILE_NAME); - if (!Files.exists(path)) { - return true; - } - String fileContent = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); - return !StringUtils.equals(fileContent, artifactorySha256); - } - - /** - * Send REST request to Artifactory to get binary's sha256. - * - * @param manager - internal Artifactory Java manager. - * @param cliUrlSuffix - path to the specific JFrog CLI version in Artifactory, will be sent to Artifactory in the request. - * @return binary's sha256 - * @throws IOException in case of any I/O error. - */ - private static String getArtifactSha256(ArtifactoryManager manager, String cliUrlSuffix) throws IOException { - Header[] headers = manager.downloadHeaders(cliUrlSuffix); - for (Header header : headers) { - if (header.getName().equalsIgnoreCase(SHA256_HEADER_NAME)) { - return header.getValue(); - } - } - return StringUtils.EMPTY; - } - public static FilePath performJfrogCliInstallation(FilePath toolLocation, TaskListener log, String version, JFrogPlatformInstance instance, String repository, String binaryName) throws IOException, InterruptedException { ProxyConfiguration proxyConfiguration = createProxyConfiguration(); // Download Jfrog CLI binary - toolLocation.act(new MasterToSlaveFileCallable() { - @Override - public Void invoke(File f, VirtualChannel channel) throws IOException { - downloadJfrogCli(f, log, version, instance, repository, binaryName, proxyConfiguration); - return null; - } - }); + toolLocation.act(new JFrogCliDownloader(proxyConfiguration, version, instance, log, repository, binaryName)); return toolLocation; } } diff --git a/src/main/java/io/jenkins/plugins/jfrog/Utils.java b/src/main/java/io/jenkins/plugins/jfrog/Utils.java index 10a867e7..d9b35110 100644 --- a/src/main/java/io/jenkins/plugins/jfrog/Utils.java +++ b/src/main/java/io/jenkins/plugins/jfrog/Utils.java @@ -4,14 +4,12 @@ import hudson.model.Job; import hudson.model.TaskListener; import hudson.util.Secret; +import io.jenkins.plugins.jfrog.callables.TempDirCreator; import jenkins.model.Jenkins; -import jenkins.security.MasterToSlaveCallable; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jfrog.build.client.ProxyConfiguration; -import java.io.File; import java.io.IOException; -import java.util.Objects; /** * @author gail @@ -62,21 +60,7 @@ public static void deleteBuildJfrogHomeDir(FilePath ws, String buildNumber, Task public static FilePath createAndGetTempDir(final FilePath ws) throws IOException, InterruptedException { // The token that combines the project name and unique number to create unique workspace directory. String workspaceList = System.getProperty("hudson.slaves.WorkspaceList"); - return ws.act(new MasterToSlaveCallable() { - @Override - public FilePath call() { - FilePath tempDir = ws.sibling(ws.getName() + Objects.toString(workspaceList, "@") + "tmp"); - if (tempDir == null) { - throw new RuntimeException("Failed to create JFrog CLI temporary directory"); - } - tempDir = tempDir.child("jfrog"); - File tempDirFile = new File(tempDir.getRemote()); - if (tempDirFile.mkdirs()) { - tempDirFile.deleteOnExit(); - } - return tempDir; - } - }); + return ws.act(new TempDirCreator(workspaceList, ws)); } public static FilePath createAndGetJfrogCliHomeTempDir(final FilePath ws, String buildNumber) throws IOException, InterruptedException { diff --git a/src/main/java/io/jenkins/plugins/jfrog/callables/JFrogCliDownloader.java b/src/main/java/io/jenkins/plugins/jfrog/callables/JFrogCliDownloader.java new file mode 100644 index 00000000..b03226cf --- /dev/null +++ b/src/main/java/io/jenkins/plugins/jfrog/callables/JFrogCliDownloader.java @@ -0,0 +1,125 @@ +package io.jenkins.plugins.jfrog.callables; + +import hudson.model.TaskListener; +import hudson.remoting.VirtualChannel; +import hudson.util.Secret; +import io.jenkins.plugins.jfrog.JenkinsBuildInfoLog; +import io.jenkins.plugins.jfrog.OsUtils; +import io.jenkins.plugins.jfrog.configuration.JFrogPlatformInstance; +import jenkins.MasterToSlaveFileCallable; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.jfrog.build.client.ProxyConfiguration; +import org.jfrog.build.extractor.clientConfiguration.client.artifactory.ArtifactoryManager; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.jfrog.build.client.DownloadResponse.SHA256_HEADER_NAME; + +/** + * Downloads JFrog CLI. + * Runs inside an agent. + */ +@AllArgsConstructor +public class JFrogCliDownloader extends MasterToSlaveFileCallable { + + /** + * The name of the file that contains the JFrog CLI binary sha256. + * The file will help us determine if we should download an updated version or skip it. + */ + private static final String SHA256_FILE_NAME = "sha256"; + + /** + * decoded "[RELEASE]" for the download url + */ + private static final String RELEASE = "[RELEASE]"; + + ProxyConfiguration proxyConfiguration; + private String providedVersion; + JFrogPlatformInstance instance; + private TaskListener log; + String repository; + String binaryName; + + @Override + public Void invoke(File toolLocation, VirtualChannel channel) throws IOException, InterruptedException { + // An empty string indicates the latest version. + String version = StringUtils.defaultIfBlank(providedVersion, RELEASE); + String cliUrlSuffix = String.format("/%s/v2-jf/%s/jfrog-cli-%s/%s", repository, version, OsUtils.getOsDetails(), binaryName); + + JenkinsBuildInfoLog buildInfoLog = new JenkinsBuildInfoLog(log); + + // Downloading binary from Artifactory + try (ArtifactoryManager manager = new ArtifactoryManager(instance.inferArtifactoryUrl(), Secret.toString(instance.getCredentialsConfig().getUsername()), + Secret.toString(instance.getCredentialsConfig().getPassword()), Secret.toString(instance.getCredentialsConfig().getAccessToken()), buildInfoLog)) { + if (proxyConfiguration != null) { + manager.setProxyConfiguration(proxyConfiguration); + } + // Getting updated cli binary's sha256 form Artifactory. + String artifactorySha256 = getArtifactSha256(manager, cliUrlSuffix); + if (shouldDownloadTool(toolLocation, artifactorySha256)) { + if (version.equals(RELEASE)) { + log.getLogger().printf("Download '%s' latest version from: %s%n", binaryName, instance.inferArtifactoryUrl() + cliUrlSuffix); + } else { + log.getLogger().printf("Download '%s' version %s from: %s%n", binaryName, version, instance.inferArtifactoryUrl() + cliUrlSuffix); + } + File downloadResponse = manager.downloadToFile(cliUrlSuffix, new File(toolLocation, binaryName).getPath()); + if (!downloadResponse.setExecutable(true)) { + throw new IOException("No permission to add execution permission to binary"); + } + createSha256File(toolLocation, artifactorySha256); + } + } + return null; + } + + private static void createSha256File(File toolLocation, String artifactorySha256) throws IOException { + File file = new File(toolLocation, SHA256_FILE_NAME); + Files.write(file.toPath(), artifactorySha256.getBytes(StandardCharsets.UTF_8)); + } + + /** + * We should skip the download if the tool's directory already contains the specific version, otherwise we should download it. + * A file named 'sha256' contains the specific binary sha256. + * If the file sha256 has not changed, we will skip the download, otherwise we will download and overwrite the existing files. + * + * @param toolLocation - expected location of the tool on the fileSystem. + * @param artifactorySha256 - sha256 of the expected file in artifactory. + */ + private static boolean shouldDownloadTool(File toolLocation, String artifactorySha256) throws IOException { + // In case no sha256 was provided (for example when the customer blocks headers) download the tool. + if (artifactorySha256.isEmpty()) { + return true; + } + // Looking for the sha256 file in the tool directory. + Path path = toolLocation.toPath().resolve(SHA256_FILE_NAME); + if (!Files.exists(path)) { + return true; + } + String fileContent = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + return !StringUtils.equals(fileContent, artifactorySha256); + } + + /** + * Send REST request to Artifactory to get binary's sha256. + * + * @param manager - internal Artifactory Java manager. + * @param cliUrlSuffix - path to the specific JFrog CLI version in Artifactory, will be sent to Artifactory in the request. + * @return binary's sha256 + * @throws IOException in case of any I/O error. + */ + private static String getArtifactSha256(ArtifactoryManager manager, String cliUrlSuffix) throws IOException { + Header[] headers = manager.downloadHeaders(cliUrlSuffix); + for (Header header : headers) { + if (header.getName().equalsIgnoreCase(SHA256_HEADER_NAME)) { + return header.getValue(); + } + } + return StringUtils.EMPTY; + } +} diff --git a/src/main/java/io/jenkins/plugins/jfrog/callables/TempDirCreator.java b/src/main/java/io/jenkins/plugins/jfrog/callables/TempDirCreator.java new file mode 100644 index 00000000..7a8c0544 --- /dev/null +++ b/src/main/java/io/jenkins/plugins/jfrog/callables/TempDirCreator.java @@ -0,0 +1,33 @@ +package io.jenkins.plugins.jfrog.callables; + +import hudson.FilePath; +import hudson.remoting.VirtualChannel; +import jenkins.MasterToSlaveFileCallable; +import lombok.AllArgsConstructor; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +/** + * Create a temporary directory inside the agent. + */ +@AllArgsConstructor +public class TempDirCreator extends MasterToSlaveFileCallable { + private String workspaceList; + private FilePath ws; + + @Override + public FilePath invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + FilePath tempDir = ws.sibling(ws.getName() + Objects.toString(workspaceList, "@") + "tmp"); + if (tempDir == null) { + throw new RuntimeException("Failed to create JFrog CLI temporary directory"); + } + tempDir = tempDir.child("jfrog"); + File tempDirFile = new File(tempDir.getRemote()); + if (tempDirFile.mkdirs()) { + tempDirFile.deleteOnExit(); + } + return tempDir; + } +}