Skip to content
Permalink
Browse files
[JENKINS-50597] First direct test of error handling: non-200 response…
… codes from artifact upload.
  • Loading branch information
jglick committed May 23, 2018
1 parent d4b4006 commit 6f9c2b2b0adcc678a7fc5dc30e7f95e79c5b6584
41 pom.xml
@@ -17,9 +17,9 @@
<revision>1.0-alpha-1</revision>
<changelist>-SNAPSHOT</changelist>
<jclouds.version>2.0.3</jclouds.version>
<jenkins.version>2.118</jenkins.version>
<jenkins.version>2.121</jenkins.version>
<java.level>8</java.level>
<workflow-api-plugin.version>2.28-rc333.0675e9e9cb4c</workflow-api-plugin.version> <!-- TODO https://github.com/jenkinsci/workflow-api-plugin/pull/67 -->
<workflow-api-plugin.version>2.28-rc337.8abe7c5204d9</workflow-api-plugin.version> <!-- TODO https://github.com/jenkinsci/workflow-api-plugin/pull/67 -->
<useBeta>true</useBeta>
</properties>

@@ -63,13 +63,6 @@
-->
</exclusions>
</dependency>
<!-- to make it work in eclipse during development -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0.1</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
@@ -136,6 +129,30 @@
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>2.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.53</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-durable-task-step</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-basic-steps</artifactId>
<version>2.8-rc351.c6608322f479</version> <!-- TODO https://github.com/jenkinsci/workflow-basic-steps-plugin/pull/60 -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
@@ -160,6 +177,12 @@
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>2.15</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

@@ -64,6 +64,7 @@
import jenkins.MasterToSlaveFileCallable;
import jenkins.model.ArtifactManager;
import jenkins.util.VirtualFile;
import org.apache.commons.io.IOUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

@@ -321,10 +322,14 @@ private static void uploadFile(Path f, URL url) throws IOException {
Files.copy(f, out);
}
int responseCode = connection.getResponseCode();
String urlSafe = url.toString().replaceFirst("[?].+$", "?…");
if (responseCode < 200 || responseCode >= 300) {
throw new IOException(String.format("Failed to upload %s to %s, response: %d %s", f.toAbsolutePath(), url,
responseCode, connection.getResponseMessage()));
String diag;
try (InputStream err = connection.getErrorStream()) {
diag = err != null ? IOUtils.toString(err, connection.getContentEncoding()) : null;
}
throw new IOException(String.format("Failed to upload %s to %s, response: %d %s, body: %s", f.toAbsolutePath(), urlSafe, responseCode, connection.getResponseMessage(), diag));
}
LOGGER.log(Level.FINE, "Uploaded {0} to {1}: {2}", new Object[] { f.toAbsolutePath(), url, responseCode });
LOGGER.log(Level.FINE, "Uploaded {0} to {1}: {2}", new Object[] { f.toAbsolutePath(), urlSafe, responseCode });
}
}
@@ -27,6 +27,7 @@
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -37,10 +38,14 @@
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.bootstrap.HttpServer;
import org.apache.http.impl.bootstrap.ServerBootstrap;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.protocol.HttpContext;
import org.eclipse.jetty.util.ConcurrentHashSet;
import org.jclouds.ContextBuilder;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
@@ -62,6 +67,12 @@ public String getPrefix() {
public String getContainer() {
return "container";
}

private static final Set<String> fails = new ConcurrentHashSet<>();

static void failIn(String method, String key) {
fails.add(method + ":" + key);
}

@Override
public synchronized BlobStoreContext getContext() throws IOException {
@@ -70,12 +81,17 @@ public synchronized BlobStoreContext getContext() throws IOException {
HttpServer server = ServerBootstrap.bootstrap().
registerHandler("*", (HttpRequest request, HttpResponse response, HttpContext _context) -> {
String method = request.getRequestLine().getMethod();
Matcher m = Pattern.compile("/([^/]+)/(.+)").matcher(request.getRequestLine().getUri());
Matcher m = Pattern.compile("/([^/]+)/(.+)[?]method=" + method).matcher(request.getRequestLine().getUri());
if (!m.matches()) {
throw new IllegalStateException();
}
String container = m.group(1);
String key = m.group(2);
if (fails.remove(method + ":" + key)) {
response.setStatusLine(new BasicStatusLine(HttpVersion.HTTP_1_0, 500, "simulated failure"));
response.setEntity(new StringEntity("Detailed explanation."));
return;
}
BlobStore blobStore = context.getBlobStore();
switch (method) {
case "GET": {
@@ -121,7 +137,7 @@ public URI toURI(String container, String key) {

@Override
public URL toExternalURL(Blob blob, HttpMethod httpMethod) throws IOException {
return new URL(baseURL, blob.getMetadata().getContainer() + "/" + blob.getMetadata().getName());
return new URL(baseURL, blob.getMetadata().getContainer() + "/" + blob.getMetadata().getName() + "?method=" + httpMethod);
}

}
@@ -0,0 +1,65 @@
/*
* The MIT License
*
* Copyright 2018 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.jenkins.plugins.artifact_manager_jclouds;

import hudson.model.Result;
import jenkins.model.ArtifactManagerConfiguration;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.Before;
import org.junit.Rule;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;

@Issue("JENKINS-50597")
public class NetworkTest {

@ClassRule
public static BuildWatcher buildWatcher = new BuildWatcher();

@Rule
public JenkinsRule r = new JenkinsRule();

@Before
public void configureManager() throws Exception {
MockBlobStore mockBlobStore = new MockBlobStore();
mockBlobStore.getContext().getBlobStore().createContainerInLocation(null, mockBlobStore.getContainer());
ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(new JCloudsArtifactManagerFactory(mockBlobStore));
}

@Test
public void exceptionArchiving() throws Exception {
WorkflowJob p = r.createProject(WorkflowJob.class, "p");
r.createSlave("remote", null, null);
MockBlobStore.failIn("PUT", "p/1/artifacts/f");
p.setDefinition(new CpsFlowDefinition("node('remote') {writeFile file: 'f', text: '.'; archiveArtifacts 'f'}", true));
WorkflowRun b = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0));
r.assertLogContains("/container/p/1/artifacts/f?…, response: 500 simulated failure, body: Detailed explanation.", b);
}

}

0 comments on commit 6f9c2b2

Please sign in to comment.