Skip to content

Commit

Permalink
Multiple files upload #5
Browse files Browse the repository at this point in the history
  • Loading branch information
rvansa committed Dec 20, 2022
1 parent 7492804 commit 5ac5f77
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 67 deletions.
27 changes: 6 additions & 21 deletions src/main/java/jenkins/plugins/horreum/upload/HorreumUpload.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,21 @@
package jenkins.plugins.horreum.upload;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nonnull;

import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;

import hudson.EnvVars;
import hudson.Extension;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Item;
import hudson.security.ACL;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.ListBoxModel;
import hudson.util.ListBoxModel.Option;
import jenkins.model.Jenkins;
import jenkins.plugins.horreum.HorreumBaseBuilder;
import jenkins.plugins.horreum.HorreumBaseDescriptor;
import jenkins.plugins.horreum.HorreumGlobalConfig;

//TODO: Make safe functionality as upload step
public class HorreumUpload extends HorreumBaseBuilder<HorreumUploadConfig> {
Expand All @@ -44,9 +27,10 @@ public HorreumUpload(@Nonnull String credentials,
@Nonnull String start,
@Nonnull String stop,
@Nonnull String schema,
@Nonnull String jsonFile,
String jsonFile,
String files,
boolean addBuildInfo) {
super(new HorreumUploadConfig(credentials, test, owner, access, start, stop, schema, jsonFile, addBuildInfo));
super(new HorreumUploadConfig(credentials, test, owner, access, start, stop, schema, jsonFile, files, addBuildInfo));
}

public String getTest() {
Expand Down Expand Up @@ -123,8 +107,9 @@ public void setAddBuildInfo(boolean add) {

@Override
protected HorreumUploadExecutionContext createExecutionContext(AbstractBuild<?, ?> build, BuildListener listener, EnvVars envVars) {
return HorreumUploadExecutionContext.from(config, envVars, build,
listener, () -> this.config.resolveUploadFile(envVars, build));
return HorreumUploadExecutionContext.from(config, envVars, build, listener,
() -> build.getWorkspace() == null ? null : build.getWorkspace().getRemote(),
() -> this.config.resolveUploadFiles(envVars, build));
}

@Extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ public class HorreumUploadConfig extends HorreumBaseConfig {
private @Nonnull String start;
private @Nonnull String stop;
private @Nonnull String schema;
private @Nonnull String jsonFile;
private String jsonFile;
private String files;
private boolean addBuildInfo;

public HorreumUploadConfig(String credentials, String test, String owner, String access, String start, String stop, String schema, String jsonFile, boolean addBuildInfo) {
public HorreumUploadConfig(String credentials, String test, String owner, String access, String start, String stop, String schema, String jsonFile, String files, boolean addBuildInfo) {
this.setCredentials(credentials);
if (test == null || test.isEmpty()) {
throw new IllegalArgumentException("Test name (or ID) must be set.");
Expand All @@ -38,16 +39,19 @@ public HorreumUploadConfig(String credentials, String test, String owner, String
throw new IllegalArgumentException("Stop timestamp must be set.");
}
schema = orEmpty(schema);
if (jsonFile == null || jsonFile.isEmpty()) {
throw new IllegalArgumentException("JSON file must be set.");
if ((jsonFile == null || jsonFile.isEmpty()) && (files == null || files.isEmpty())) {
throw new IllegalArgumentException("Either 'jsonFile' or 'files' must be set.");
} else if (jsonFile != null && !jsonFile.isEmpty() && files != null && !files.isEmpty()) {
throw new IllegalArgumentException("You can set 'jsonFile' or 'files' but not both.");
}
this.test = Objects.requireNonNull(test);
this.owner = Objects.requireNonNull(owner);
this.access = Objects.requireNonNull(access);
this.start = Objects.requireNonNull(start);
this.stop = Objects.requireNonNull(stop);
this.schema = Objects.requireNonNull(schema);
this.jsonFile = Objects.requireNonNull(jsonFile);
this.jsonFile = jsonFile;
this.files = files;
this.addBuildInfo = addBuildInfo;
}

Expand Down Expand Up @@ -105,7 +109,6 @@ public void setSchema(@Nonnull String schema) {
this.schema = schema;
}

@Nonnull
public String getJsonFile() {
return jsonFile;
}
Expand All @@ -114,6 +117,14 @@ public void setJsonFile(@Nonnull String jsonFile) {
this.jsonFile = jsonFile;
}

public String getFiles() {
return files;
}

public void setFiles(@Nonnull String files) {
this.files = files;
}

public boolean getAddBuildInfo() {
return addBuildInfo;
}
Expand All @@ -123,22 +134,24 @@ public HorreumUploadConfig setAddBuildInfo(boolean addBuildInfo) {
return this;
}

FilePath resolveUploadFile(EnvVars envVars, AbstractBuild<?,?> build) {
if (jsonFile.trim().isEmpty()) {
return null;
}
String filePath = envVars.expand(jsonFile);
FilePath[] resolveUploadFiles(EnvVars envVars, AbstractBuild<?,?> build) {
try {
FilePath workspace = build.getWorkspace();
if (workspace == null) {
throw new IllegalStateException("Could not find workspace to check existence of upload file: " + jsonFile +
". You should use it inside a 'node' block");
}
FilePath uploadFilePath = workspace.child(filePath);
if (!uploadFilePath.exists()) {
throw new IllegalStateException("Could not find upload file: " + jsonFile);
if (jsonFile != null && !jsonFile.isEmpty()) {
FilePath uploadFilePath = workspace.child(envVars.expand(jsonFile));
if (!uploadFilePath.exists()) {
throw new IllegalStateException("Could not find upload file: " + jsonFile);
}
return new FilePath[] { uploadFilePath };
} else if (files != null && !files.isEmpty()) {
return workspace.list(envVars.expand(files));
} else {
throw new IllegalStateException();
}
return uploadFilePath;
} catch (IOException | InterruptedException e) {
throw new IllegalStateException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

Expand All @@ -38,17 +36,19 @@ public class HorreumUploadExecutionContext extends BaseExecutionContext<String>
private static final long serialVersionUID = -2066857816168989599L;
private static final String HORREUM_JENKINS_SCHEMA = "urn:horreum:jenkins-plugin:0.1";
private final Map<String, HttpRequestNameValuePair> params;
private final FilePath uploadFile;
private final String workspacePath;
private final FilePath[] uploadFiles;
private final ObjectNode buildInfo;

static HorreumUploadExecutionContext from(HorreumUploadConfig config,
EnvVars envVars,
Run<?, ?> run,
TaskListener listener,
Supplier<FilePath> filePathSupplier) {
Supplier<String> workspacePathSupplier,
Supplier<FilePath[]> uploadFilesSupplier) {
String url = envVars != null ? envVars.expand(HorreumGlobalConfig.get().getBaseUrl()) : HorreumGlobalConfig.get().getBaseUrl(); //http.resolveUrl(envVars, build, taskListener);
List<HttpRequestNameValuePair> params = config.resolveParams(); //Need to define params in freestyle project
FilePath uploadFile = filePathSupplier.get();
FilePath[] uploadFiles = uploadFilesSupplier.get();
TaskListener taskListener = config.getQuiet() ? TaskListener.NULL : listener;

ObjectNode buildInfo = null;
Expand All @@ -65,37 +65,52 @@ static HorreumUploadExecutionContext from(HorreumUploadConfig config,
buildInfo.put("startTime", run.getStartTimeInMillis());
buildInfo.put("uploadTime", System.currentTimeMillis());
}
HorreumUploadExecutionContext context = new HorreumUploadExecutionContext(
return new HorreumUploadExecutionContext(
url,
config.getCredentials(),
params,
uploadFile,
workspacePathSupplier.get(),
uploadFiles,
buildInfo,
taskListener.getLogger());
return context;
}

private HorreumUploadExecutionContext(
String url,
String credentials,
List<HttpRequestNameValuePair> params,
FilePath uploadFile,
String workspacePath,
FilePath[] uploadFiles,
ObjectNode buildInfo, PrintStream logger
) {
super(url, credentials, logger);
this.params = new HashMap<>();
params.forEach(param -> this.params.put(param.getName(), param));
this.uploadFile = uploadFile;
this.workspacePath = workspacePath;
this.uploadFiles = uploadFiles;
this.buildInfo = buildInfo;
}

@Override
protected String invoke(HorreumClient client) {
JsonNode data;
try {
data = new ObjectMapper().readTree(new File(uploadFile.getRemote()));
} catch (IOException e) {
throw new RuntimeException("File for upload cannot be read: " + uploadFile.getRemote(), e);
if (uploadFiles == null || uploadFiles.length == 0) {
throw new IllegalStateException("There are no files to upload!");
} else if (uploadFiles.length == 1) {
data = loadFile(uploadFiles[0]);
} else {
ObjectNode root = JsonNodeFactory.instance.objectNode();
for (FilePath uploadFile : uploadFiles) {
String path = uploadFile.getRemote();
if (workspacePath != null && path.startsWith(workspacePath)) {
path = path.substring(workspacePath.length());
if (path.startsWith("/")) {
path = path.substring(1);
}
}
root.set(path, loadFile(uploadFile));
}
data = root;
}
String schema = params.get("schema").getValue();
if (schema != null && schema.isEmpty()) {
Expand Down Expand Up @@ -137,4 +152,12 @@ protected String invoke(HorreumClient client) {
logger().printf("Uploaded run ID: %s%n", id);
return id;
}

private JsonNode loadFile(FilePath uploadFile) {
try {
return new ObjectMapper().readTree(new File(uploadFile.getRemote()));
} catch (IOException e) {
throw new RuntimeException("File for upload cannot be read: " + uploadFile.getRemote(), e);
}
}
}
49 changes: 35 additions & 14 deletions src/main/java/jenkins/plugins/horreum/upload/HorreumUploadStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ public HorreumUploadStep(String credentials,
String stop,
String schema,
String jsonFile,
String files,
boolean addBuildInfo) {

super(new HorreumUploadConfig(credentials, test, owner, access, start, stop, schema, jsonFile, addBuildInfo));
super(new HorreumUploadConfig(credentials, test, owner, access, start, stop, schema, jsonFile, files, addBuildInfo));
}

public String getTest() {
Expand Down Expand Up @@ -100,6 +101,15 @@ public void setJsonFile(String jsonFile) {
this.config.setJsonFile(jsonFile);
}

public String getFiles() {
return config.getJsonFile();
}

@DataBoundSetter
public void setFiles(String files) {
this.config.setFiles(files);
}

public boolean getAddBuildInfo() {
return config.getAddBuildInfo();
}
Expand Down Expand Up @@ -145,28 +155,39 @@ public static final class Execution extends HorreumBaseStep.Execution<String> {
@Override
protected BaseExecutionContext<String> createExecutionContext() throws Exception {
StepContext context = getContext();
return HorreumUploadExecutionContext.from(step.config, null, context.get(Run.class), context.get(TaskListener.class), this::resolveUploadFile);
return HorreumUploadExecutionContext.from(step.config, null, context.get(Run.class), context.get(TaskListener.class), this::resolveWorkspacePath, this::resolveUploadFiles);
}

private static final long serialVersionUID = 1L;

private FilePath resolveUploadFile() {
String uploadFile = step.getJsonFile();
if (uploadFile.trim().isEmpty()) {
return null;
private String resolveWorkspacePath() {
try {
FilePath workpace = getContext().get(FilePath.class);
return workpace == null ? null : workpace.getRemote();
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}

private static final long serialVersionUID = 1L;

private FilePath[] resolveUploadFiles() {
try {
FilePath workspace = getContext().get(FilePath.class);
if (workspace == null) {
throw new IllegalStateException("Could not find workspace to check existence of upload file: " + uploadFile +
". You should use it inside a 'node' block");
throw new IllegalStateException("Could not find workspace.");
}
FilePath uploadFilePath = workspace.child(uploadFile);
if (!uploadFilePath.exists()) {
throw new IllegalStateException("Could not find upload file: " + uploadFile);
String jsonFile = step.getJsonFile();
String files = step.getFiles();
if (jsonFile != null && !jsonFile.trim().isEmpty()) {
FilePath uploadFilePath = workspace.child(jsonFile);
if (!uploadFilePath.exists()) {
throw new IllegalStateException("Could not find upload file: " + jsonFile);
}
return new FilePath[] { uploadFilePath };
} else if (files != null && !files.trim().isEmpty()) {
return workspace.list(files);
} else {
throw new IllegalStateException();
}
return uploadFilePath;
} catch (IOException | InterruptedException e) {
throw new IllegalStateException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
<f:textbox />
</f:entry>

<f:entry field="jsonFile" title="JSON file path">
<f:entry field="jsonFile" title="JSON file path" description="Path to a single file in workspace. Cannot be combined with the selector below.">
<f:textbox />
</f:entry>

<f:entry field="files" title="Multiple files selector" description="Selector for multiple files supporting Ant FileSet syntax (e.g. '**/*.json'). Cannot be combined with single file path above.">
<f:textbox />
</f:entry>

Expand Down
Loading

0 comments on commit 5ac5f77

Please sign in to comment.