Skip to content

Commit

Permalink
Merge pull request #349 from dwnusbaum/content-type-issues
Browse files Browse the repository at this point in the history
`JCloudsArtifactManager` fails to archive artifacts when their workspace path is not identical to their archive path
  • Loading branch information
jglick authored Jan 20, 2023
2 parents 0e0e95d + 5a152c5 commit b5d21c7
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.test</groupId>
<artifactId>docker-fixtures</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private String getBlobPath(String key, String path) {
public void archive(FilePath workspace, Launcher launcher, BuildListener listener, Map<String, String> artifacts)
throws IOException, InterruptedException {
LOGGER.log(Level.FINE, "Archiving from {0}: {1}", new Object[] { workspace, artifacts });
Map<String, String> contentTypes = workspace.act(new ContentTypeGuesser(new ArrayList<>(artifacts.keySet()), listener));
Map<String, String> contentTypes = workspace.act(new ContentTypeGuesser(new ArrayList<>(artifacts.values()), listener));
LOGGER.fine(() -> "guessing content types: " + contentTypes);
Map<String, URL> artifactUrls = new HashMap<>();
BlobStore blobStore = getContext().getBlobStore();
Expand All @@ -132,7 +132,7 @@ public void archive(FilePath workspace, Launcher launcher, BuildListener listene
String blobPath = getBlobPath(path);
Blob blob = blobStore.blobBuilder(blobPath).build();
blob.getMetadata().setContainer(provider.getContainer());
blob.getMetadata().getContentMetadata().setContentType(contentTypes.get(entry.getKey()));
blob.getMetadata().getContentMetadata().setContentType(contentTypes.get(entry.getValue()));
artifactUrls.put(entry.getValue(), provider.toExternalURL(blob, HttpMethod.PUT));
}

Expand Down Expand Up @@ -167,6 +167,8 @@ public Map<String, String> invoke(File f, VirtualChannel channel) {
contentTypes.put(relPath, contentType);
} catch (IOException e) {
Functions.printStackTrace(e, listener.error("Unable to determine content type for file: " + theFile));
// A content type must be specified; otherwise, the metadata signature will be computed from data that includes "Content-Type:", but no such HTTP header will be sent, and AWS will reject the request.
contentTypes.put(relPath, "application/octet-stream");
}
}
return contentTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
import io.jenkins.plugins.artifact_manager_jclouds.BlobStoreProviderDescriptor;
import io.jenkins.plugins.artifact_manager_jclouds.BlobStoreProvider;
import io.jenkins.plugins.artifact_manager_jclouds.JCloudsArtifactManagerFactory;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.*;

import java.io.IOException;
Expand Down Expand Up @@ -68,14 +71,18 @@
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Item;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.sshslaves.SSHLauncher;
import hudson.remoting.Which;
import hudson.slaves.DumbSlave;
import hudson.tasks.ArtifactArchiver;
import io.jenkins.plugins.aws.global_configuration.CredentialsAwsGlobalConfiguration;
import java.io.Serializable;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.Set;
import jenkins.branch.BranchSource;
import jenkins.model.ArtifactManagerConfiguration;
import jenkins.model.ArtifactManagerFactory;
Expand All @@ -84,6 +91,7 @@
import jenkins.plugins.git.GitSampleRepoRule;
import jenkins.plugins.git.traits.BranchDiscoveryTrait;
import jenkins.security.MasterToSlaveCallable;
import jenkins.util.BuildListenerAdapter;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
Expand All @@ -93,7 +101,14 @@
import org.jenkinsci.plugins.workflow.flow.FlowCopier;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProjectTest;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.steps.StepExecutions;
import org.jvnet.hudson.test.MockAuthorizationStrategy;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.DataBoundConstructor;

public class JCloudsArtifactManagerTest extends S3AbstractTest {

Expand Down Expand Up @@ -306,6 +321,26 @@ public void contentType() throws Exception {
assertThat(response.getContentType(), equalTo("application/json"));
}

@Test
public void archiveWithDistinctArchiveAndWorkspacePaths() throws Exception {
String text = "some regular text";
ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(getArtifactManagerFactory(null, null));

j.createSlave("remote", null, null);

WorkflowJob p = j.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"node('remote') {\n" +
" writeFile file: 'f.txt', text: '" + text + "'\n" +
" archiveWithCustomPath(archivePath: 'what/an/interesting/path/to/f.txt', workspacePath: 'f.txt')\n" +
"}", true));
j.buildAndAssertSuccess(p);

WebResponse response = j.createWebClient().goTo("job/p/1/artifact/what/an/interesting/path/to/f.txt", null).getWebResponse();
assertThat(response.getContentAsString(), equalTo(text));
assertThat(response.getContentType(), equalTo("text/plain"));
}

//@Test
public void archiveSingleLargeFile() throws Exception {
ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(getArtifactManagerFactory(null, null));
Expand Down Expand Up @@ -339,4 +374,36 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
}
}

public static class ArchiveArtifactWithCustomPathStep extends Step implements Serializable {
private static final long serialVersionUID = 1L;
private final String archivePath;
private final String workspacePath;
@DataBoundConstructor
public ArchiveArtifactWithCustomPathStep(String archivePath, String workspacePath) {
this.archivePath = archivePath;
this.workspacePath = workspacePath;
}
@Override
public StepExecution start(StepContext context) throws Exception {
return StepExecutions.synchronousNonBlocking(context, context2 -> {
context.get(Run.class).pickArtifactManager().archive(
context.get(FilePath.class),
context.get(Launcher.class),
new BuildListenerAdapter(context.get(TaskListener.class)),
Collections.singletonMap(archivePath, workspacePath));
return null;
});
}
@TestExtension("archiveWithDistinctArchiveAndWorkspacePaths")
public static class DescriptorImpl extends StepDescriptor {
@Override
public String getFunctionName() {
return "archiveWithCustomPath";
}
@Override
public Set<? extends Class<?>> getRequiredContext() {
return Set.of(FilePath.class, Launcher.class, TaskListener.class);
}
}
}
}

0 comments on commit b5d21c7

Please sign in to comment.