Skip to content

Commit

Permalink
Avoid serialization of anonymous classes
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi committed Aug 30, 2023
1 parent 74ca4d1 commit 6020cf6
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 135 deletions.
119 changes: 2 additions & 117 deletions src/main/java/io/jenkins/plugins/jfrog/BinaryInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;


/**
Expand All @@ -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);
}
Expand Down Expand Up @@ -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<Void>() {
@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;
}
}
Expand Down
20 changes: 2 additions & 18 deletions src/main/java/io/jenkins/plugins/jfrog/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<FilePath, IOException>() {
@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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Void> {

/**
* 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<FilePath> {
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;
}
}

0 comments on commit 6020cf6

Please sign in to comment.