Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/main/java/hudson/plugins/s3/ClientHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/hudson/plugins/s3/S3BucketPublisher.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/hudson/plugins/s3/S3Profile.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,14 +41,15 @@ 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);
this.uploadRetryTime = parseWithDefault(uploadRetryTime, 5);
this.maxDownloadRetries = parseWithDefault(maxDownloadRetries, 5);
this.downloadRetryTime = parseWithDefault(downloadRetryTime, 5);
this.signedUrlExpirySeconds = signedUrlExpirySeconds;
this.endpoint = endpoint;
if (useRole) {
this.accessKey = "";
this.secretKey = null;
Expand Down Expand Up @@ -87,6 +89,10 @@ public final Secret getSecretKey() {
return secretKey;
}

public final String getEndpoint() {
return endpoint;
}

public final int getMaxUploadRetries() {
return maxUploadRetries;
}
Expand All @@ -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<FingerprintRecord> upload(Run<?, ?> run,
Expand Down Expand Up @@ -145,10 +151,10 @@ public List<FingerprintRecord> upload(Run<?, ?> run,

final MasterSlaveCallable<String> 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());
}

Expand Down Expand Up @@ -241,7 +247,7 @@ public List<FingerprintRecord> downloadAll(Run build,
fingerprints.add(repeat(maxDownloadRetries, downloadRetryTime, dest, new Callable<FingerprintRecord>() {
@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);
}
}));
Expand Down Expand Up @@ -292,6 +298,7 @@ public String toString() {
", accessKey='" + accessKey + '\'' +
", secretKey=" + secretKey +
", useRole=" + useRole +
", endpoint=" + endpoint +
'}';
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public abstract class S3BaseUploadCallable extends S3Callable<String> {
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<String, String> 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;
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/hudson/plugins/s3/callable/S3Callable.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@ abstract class S3Callable<T> implements FileCallable<T> {
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<String, TransferManager> 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;
}

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));
}

Expand All @@ -47,4 +49,4 @@ public void checkRoles(RoleChecker roleChecker) throws SecurityException {
private String getUniqueKey() {
return region + '_' + secretKey + '_' + accessKey + '_' + useRole;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ public final class S3DownloadCallable extends S3Callable<String>
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;
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import java.util.zip.GZIPOutputStream;

public final class S3GzipCallable extends S3BaseUploadCallable implements MasterSlaveCallable<String> {
public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map<String, String> 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<String, String> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
public final class S3UploadCallable extends S3BaseUploadCallable implements MasterSlaveCallable<String> {
private static final long serialVersionUID = 1L;

public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map<String, String> 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<String, String> userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) {
super(accessKey, secretKey, useRole, endpoint, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy);
}

/**
Expand All @@ -29,4 +29,4 @@ public String invoke(FilePath file) throws IOException, InterruptedException {

return MD5.generateFromFile(file);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<f:entry title="Access key" help="/plugin/s3/help-accesskey.html">
<f:textbox name="s3.accessKey" value="${profile.accessKey}"
checkMethod="post"
checkUrl="'${rootURL}/publisher/S3BucketPublisher/loginCheck?name='+encodeURIComponent(Form.findMatchingInput(this,'s3.name').value)+'&amp;secretKey='+encodeURIComponent(Form.findMatchingInput(this,'s3.secretKey').value)+'&amp;accessKey='+encodeURIComponent(this.value)+'&amp;useRole='+encodeURIComponent(Form.findMatchingInput(this,'s3.useRole').value)"
checkUrl="'${rootURL}/publisher/S3BucketPublisher/loginCheck?name='+encodeURIComponent(Form.findMatchingInput(this,'s3.name').value)+'&amp;secretKey='+encodeURIComponent(Form.findMatchingInput(this,'s3.secretKey').value)+'&amp;accessKey='+encodeURIComponent(this.value)+'&amp;useRole='+encodeURIComponent(Form.findMatchingInput(this,'s3.useRole').value)+'&amp;endpoint='+encodeURIComponent(Form.findMatchingInput(this,'s3.endpoint').value)"
/>
</f:entry>
<f:entry title="Secret key" help="/plugin/s3/help-secretkey.html">
Expand All @@ -24,6 +24,9 @@
/>
</f:entry>
<f:advanced>
<f:entry title="Endpoint URL" help="/plugin/s3/help-endpoint.html">
<f:textbox name="s3.endpoint" value="${profile.endpoint}" onchange="Form.findMatchingInput(this,'s3.accessKey').onchange()" />
</f:entry>
<f:entry title="Max upload retries">
<f:number name="s3.maxUploadRetries" value="${profile.maxUploadRetries}"/>
</f:entry>
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/help-endpoint.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div>S3 Endpoint URL</div>
16 changes: 15 additions & 1 deletion src/test/java/hudson/plugins/s3/S3Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
Expand Down