diff --git a/src/main/java/hudson/plugins/s3/ClientHelper.java b/src/main/java/hudson/plugins/s3/ClientHelper.java index 8251d2d2..5879fb67 100644 --- a/src/main/java/hudson/plugins/s3/ClientHelper.java +++ b/src/main/java/hudson/plugins/s3/ClientHelper.java @@ -19,7 +19,7 @@ public class ClientHelper { "hudson.plugins.s3.DEFAULT_AMAZON_S3_REGION", com.amazonaws.services.s3.model.Region.US_Standard.toAWSRegion().getName()); - public static AmazonS3Client createClient(String accessKey, String secretKey, boolean useRole, String region, ProxyConfiguration proxy) + public static AmazonS3Client createClient(String accessKey, String secretKey, boolean useRole, String endpoint, String region, ProxyConfiguration proxy) { Region awsRegion = getRegionFromString(region); @@ -33,6 +33,9 @@ public static AmazonS3Client createClient(String accessKey, String secretKey, bo } client.setRegion(awsRegion); + if (endpoint != null && !endpoint.trim().isEmpty()){ + client.setEndpoint(endpoint); + } return client; } diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index dd688fb5..34c85712 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -496,6 +496,7 @@ public FormValidation doLoginCheck(final StaplerRequest req, StaplerResponse rsp final String name = Util.fixNull(req.getParameter("name")); final String accessKey = Util.fixNull(req.getParameter("accessKey")); final String secretKey = Util.fixNull(req.getParameter("secretKey")); + final String endpoint = Util.fixNull(req.getParameter("endpoint")); final String useIAMCredential = Util.fixNull(req.getParameter("useRole")); final boolean couldBeValidated = !name.isEmpty() && !accessKey.isEmpty() && !secretKey.isEmpty(); @@ -517,7 +518,7 @@ public FormValidation doLoginCheck(final StaplerRequest req, StaplerResponse rsp final String defaultRegion = ClientHelper.DEFAULT_AMAZON_S3_REGION_NAME; final AmazonS3Client client = ClientHelper.createClient( - accessKey, secretKey, useRole, defaultRegion, Jenkins.getActiveInstance().proxy); + accessKey, secretKey, useRole, endpoint, defaultRegion, Jenkins.getActiveInstance().proxy); try { client.listBuckets(); diff --git a/src/main/java/hudson/plugins/s3/S3Profile.java b/src/main/java/hudson/plugins/s3/S3Profile.java index 95074592..02a5174e 100644 --- a/src/main/java/hudson/plugins/s3/S3Profile.java +++ b/src/main/java/hudson/plugins/s3/S3Profile.java @@ -30,6 +30,7 @@ public class S3Profile { private final String name; private final String accessKey; private final Secret secretKey; + private final String endpoint; private final int maxUploadRetries; private final int uploadRetryTime; private final int maxDownloadRetries; @@ -40,7 +41,7 @@ public class S3Profile { private final int signedUrlExpirySeconds; @DataBoundConstructor - public S3Profile(String name, String accessKey, String secretKey, boolean useRole, int signedUrlExpirySeconds, String maxUploadRetries, String uploadRetryTime, String maxDownloadRetries, String downloadRetryTime, boolean keepStructure) { + public S3Profile(String name, String accessKey, String secretKey, boolean useRole, String endpoint, int signedUrlExpirySeconds, String maxUploadRetries, String uploadRetryTime, String maxDownloadRetries, String downloadRetryTime, boolean keepStructure) { this.name = name; this.useRole = useRole; this.maxUploadRetries = parseWithDefault(maxUploadRetries, 5); @@ -48,6 +49,7 @@ public S3Profile(String name, String accessKey, String secretKey, boolean useRol this.maxDownloadRetries = parseWithDefault(maxDownloadRetries, 5); this.downloadRetryTime = parseWithDefault(downloadRetryTime, 5); this.signedUrlExpirySeconds = signedUrlExpirySeconds; + this.endpoint = endpoint; if (useRole) { this.accessKey = ""; this.secretKey = null; @@ -87,6 +89,10 @@ public final Secret getSecretKey() { return secretKey; } + public final String getEndpoint() { + return endpoint; + } + public final int getMaxUploadRetries() { return maxUploadRetries; } @@ -112,7 +118,7 @@ public int getSignedUrlExpirySeconds() { } public AmazonS3Client getClient(String region) { - return ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, region, getProxy()); + return ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, endpoint, region, getProxy()); } public List upload(Run run, @@ -145,10 +151,10 @@ public List upload(Run run, final MasterSlaveCallable upload; if (gzipFiles) { - upload = new S3GzipCallable(accessKey, secretKey, useRole, dest, userMetadata, + upload = new S3GzipCallable(accessKey, secretKey, useRole, endpoint, dest, userMetadata, storageClass, selregion, useServerSideEncryption, getProxy()); } else { - upload = new S3UploadCallable(accessKey, secretKey, useRole, dest, userMetadata, + upload = new S3UploadCallable(accessKey, secretKey, useRole, endpoint, dest, userMetadata, storageClass, selregion, useServerSideEncryption, getProxy()); } @@ -241,7 +247,7 @@ public List downloadAll(Run build, fingerprints.add(repeat(maxDownloadRetries, downloadRetryTime, dest, new Callable() { @Override public FingerprintRecord call() throws IOException, InterruptedException { - final String md5 = target.act(new S3DownloadCallable(accessKey, secretKey, useRole, dest, artifact.getRegion(), getProxy())); + final String md5 = target.act(new S3DownloadCallable(accessKey, secretKey, useRole, endpoint, dest, artifact.getRegion(), getProxy())); return new FingerprintRecord(true, dest.bucketName, target.getName(), artifact.getRegion(), md5); } })); @@ -292,6 +298,7 @@ public String toString() { ", accessKey='" + accessKey + '\'' + ", secretKey=" + secretKey + ", useRole=" + useRole + + ", endpoint=" + endpoint + '}'; } diff --git a/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java index 76e72cc5..bfdf89a6 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java @@ -23,10 +23,10 @@ public abstract class S3BaseUploadCallable extends S3Callable { private final boolean useServerSideEncryption; - public S3BaseUploadCallable(String accessKey, Secret secretKey, boolean useRole, + public S3BaseUploadCallable(String accessKey, Secret secretKey, boolean useRole, String endpoint, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, selregion, proxy); + super(accessKey, secretKey, useRole, endpoint, selregion, proxy); this.dest = dest; this.storageClass = storageClass; this.userMetadata = userMetadata; diff --git a/src/main/java/hudson/plugins/s3/callable/S3Callable.java b/src/main/java/hudson/plugins/s3/callable/S3Callable.java index 0916366a..ea9cf654 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3Callable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3Callable.java @@ -16,15 +16,17 @@ abstract class S3Callable implements FileCallable { private final String accessKey; private final Secret secretKey; private final boolean useRole; + private final String endpoint; private final String region; private final ProxyConfiguration proxy; private static transient HashMap transferManagers = new HashMap<>(); - S3Callable(String accessKey, Secret secretKey, boolean useRole, String region, ProxyConfiguration proxy) { + S3Callable(String accessKey, Secret secretKey, boolean useRole, String endpoint, String region, ProxyConfiguration proxy) { this.accessKey = accessKey; this.secretKey = secretKey; this.useRole = useRole; + this.endpoint = endpoint; this.region = region; this.proxy = proxy; } @@ -32,7 +34,7 @@ abstract class S3Callable implements FileCallable { protected synchronized TransferManager getTransferManager() { final String uniqueKey = getUniqueKey(); if (transferManagers.get(uniqueKey) == null) { - final AmazonS3 client = ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, region, proxy); + final AmazonS3 client = ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, endpoint, region, proxy); transferManagers.put(uniqueKey, new TransferManager(client)); } @@ -47,4 +49,4 @@ public void checkRoles(RoleChecker roleChecker) throws SecurityException { private String getUniqueKey() { return region + '_' + secretKey + '_' + accessKey + '_' + useRole; } -} \ No newline at end of file +} diff --git a/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java index 82964882..e362b9d4 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java @@ -16,9 +16,9 @@ public final class S3DownloadCallable extends S3Callable private static final long serialVersionUID = 1L; private final Destination dest; - public S3DownloadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, String region, ProxyConfiguration proxy) + public S3DownloadCallable(String accessKey, Secret secretKey, boolean useRole, String endpoint, Destination dest, String region, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, region, proxy); + super(accessKey, secretKey, useRole, endpoint, region, proxy); this.dest = dest; } diff --git a/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java b/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java index 735831e0..046e361d 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java @@ -17,8 +17,8 @@ import java.util.zip.GZIPOutputStream; public final class S3GzipCallable extends S3BaseUploadCallable implements MasterSlaveCallable { - public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy); + public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, String endpoint, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { + super(accessKey, secretKey, useRole, endpoint, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy); } // Return a File containing the gzipped contents of the input file. diff --git a/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java index 064aea57..101d0748 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java @@ -14,8 +14,8 @@ public final class S3UploadCallable extends S3BaseUploadCallable implements MasterSlaveCallable { private static final long serialVersionUID = 1L; - public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy); + public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, String endpoint, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { + super(accessKey, secretKey, useRole, endpoint, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy); } /** @@ -29,4 +29,4 @@ public String invoke(FilePath file) throws IOException, InterruptedException { return MD5.generateFromFile(file); } -} \ No newline at end of file +} diff --git a/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly b/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly index 86dcfb42..fdfd27df 100644 --- a/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly +++ b/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly @@ -14,7 +14,7 @@ @@ -24,6 +24,9 @@ /> + + + diff --git a/src/main/webapp/help-endpoint.html b/src/main/webapp/help-endpoint.html new file mode 100644 index 00000000..819ae045 --- /dev/null +++ b/src/main/webapp/help-endpoint.html @@ -0,0 +1 @@ +
S3 Endpoint URL
diff --git a/src/test/java/hudson/plugins/s3/S3Test.java b/src/test/java/hudson/plugins/s3/S3Test.java index 808972d7..53b085c0 100644 --- a/src/test/java/hudson/plugins/s3/S3Test.java +++ b/src/test/java/hudson/plugins/s3/S3Test.java @@ -28,7 +28,9 @@ import static com.google.common.collect.Lists.newArrayList; import static org.junit.Assert.assertEquals; + public class S3Test { + @Rule public JenkinsRule j = new JenkinsRule(); @@ -40,7 +42,7 @@ public void testConfigExists() throws Exception { @Test public void testConfigContainsProfiles() throws Exception { - final S3Profile profile = new S3Profile("S3 profile random name", null, null, true, 0, "0", "0", "0", "0", true); + final S3Profile profile = new S3Profile("S3 profile random name", null, null, true, "", 0, "0", "0", "0", "0", true); replaceS3PluginProfile(profile); @@ -99,6 +101,18 @@ public void dontSetBuildResultTest() throws Exception { j.assertBuildStatus(Result.FAILURE, r); } + @Test + public void setEndpointTest() throws Exception { + String newEndpoint = "http://s3.example.com"; + HtmlPage page = j.createWebClient().goTo("configure"); + final S3Profile profile = new S3Profile("S3 profile random name", null, null, true, newEndpoint, 0, "0", "0", "0", "0", true); + WebAssert.assertTextNotPresent(page, newEndpoint); + replaceS3PluginProfile(profile); + page = j.createWebClient().goTo("configure"); + WebAssert.assertTextPresent(page, newEndpoint); + assertEquals(newEndpoint, profile.getEndpoint()); + } + private Entry entryForFile(String fileName) { return new Entry("bucket", fileName, "", "", "", false, false, true, false, false, false, false, false, null); }