Skip to content

Commit

Permalink
Merge pull request #3 from martazobro/mz-cloudfrontInvalidation
Browse files Browse the repository at this point in the history
implemented CloudFront invalidation among all available distributions
  • Loading branch information
martazobro committed Nov 11, 2015
2 parents b08f46f + f1d9be5 commit 075ecb5
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 2 deletions.
8 changes: 7 additions & 1 deletion src/main/java/hudson/plugins/s3/Entry.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public final class Entry implements Describable<Entry> {

public boolean gzipFiles;

/**
* use to invalidate uploaded files among available CloudFront distributions
*/
public boolean invalidateAfterUpload;

/**
* Metadata overrides
*/
Expand All @@ -76,7 +81,7 @@ public final class Entry implements Describable<Entry> {
@DataBoundConstructor
public Entry(String bucket, String sourceFile, String storageClass, String selectedRegion,
boolean noUploadOnFailure, boolean uploadFromSlave, boolean managedArtifacts,
boolean useServerSideEncryption, boolean flatten, boolean gzipFiles, List<MetadataPair> userMetadata) {
boolean useServerSideEncryption, boolean flatten, boolean gzipFiles, boolean invalidateAfterUpload, List<MetadataPair> userMetadata) {
this.bucket = bucket;
this.sourceFile = sourceFile;
this.storageClass = storageClass;
Expand All @@ -87,6 +92,7 @@ public Entry(String bucket, String sourceFile, String storageClass, String selec
this.useServerSideEncryption = useServerSideEncryption;
this.flatten = flatten;
this.gzipFiles = gzipFiles;
this.invalidateAfterUpload = invalidateAfterUpload;
this.userMetadata = userMetadata;
}

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/hudson/plugins/s3/S3BucketPublisher.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ public boolean perform(AbstractBuild<?, ?> build,
log(listener.getLogger(), "bucket=" + bucket + ", file=" + src.getName() + " region=" + selRegion + ", upload from slave=" + entry.uploadFromSlave + " managed="+ entry.managedArtifacts + " , server encryption "+entry.useServerSideEncryption);
records.add(profile.upload(build, listener, bucket, src, searchPathLength, escapedMetadata, storageClass, selRegion, entry.uploadFromSlave, entry.managedArtifacts, entry.useServerSideEncryption, entry.flatten, entry.gzipFiles));
}
if (entry.invalidateAfterUpload){
profile.invalidate(build, listener, bucket, searchPathLength, paths);
}
if (entry.managedArtifacts) {
artifacts.addAll(records);

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/hudson/plugins/s3/S3Profile.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package hudson.plugins.s3;

import com.amazonaws.ClientConfiguration;

import hudson.FilePath;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -34,6 +36,7 @@
import hudson.ProxyConfiguration;
import hudson.plugins.s3.callable.S3DownloadCallable;
import hudson.plugins.s3.callable.S3UploadCallable;
import hudson.plugins.s3.callable.cloudfront.CloudFrontInvalidationCallable;
import hudson.util.Secret;

public class S3Profile {
Expand Down Expand Up @@ -207,6 +210,27 @@ public FingerprintRecord upload(AbstractBuild<?,?> build, final BuildListener li
}
}

public void invalidate(AbstractBuild<?, ?> build, final BuildListener listener, String bucketName,
int searchPathLength, FilePath... paths) throws IOException, InterruptedException {
int retryCount = 0;
while (true) {
try {
CloudFrontInvalidationCallable callable = new CloudFrontInvalidationCallable(accessKey, secretKey, useRole, bucketName,
searchPathLength);
callable.invoke(paths);
return;
} catch (Exception e) {
retryCount++;
if (retryCount >= maxUploadRetries) {
throw new IOException("invalidate paths "
+ Arrays.toString(paths) + ": " + e
+ ":: Failed after " + retryCount + " tries.", e);
}
Thread.sleep(retryWaitTime * 1000);
}
}
}

public List<String> list(Run build, String bucket, String expandedFilter) {
AmazonS3Client s3client = getClient();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package hudson.plugins.s3.callable.cloudfront;

import hudson.util.Secret;

import java.io.Serializable;

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.cloudfront.AmazonCloudFront;
import com.amazonaws.services.cloudfront.AmazonCloudFrontClient;

public class AbstractCloudFrontCallable implements Serializable {
private static final long serialVersionUID = 1L;

private final String accessKey;
private final Secret secretKey;
private final boolean useRole;
private AmazonCloudFront cloudFrontClient;

public AbstractCloudFrontCallable(String accessKey, Secret secretKey, boolean useRole) {
this.accessKey = accessKey;
this.secretKey = secretKey;
this.useRole = useRole;
}

protected AmazonCloudFront getClient() {
if (cloudFrontClient == null) {
if (useRole) {
cloudFrontClient = new AmazonCloudFrontClient();
} else {
cloudFrontClient = new AmazonCloudFrontClient(new BasicAWSCredentials(accessKey, secretKey.getPlainText()));
}
}
return cloudFrontClient;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package hudson.plugins.s3.callable.cloudfront;

import hudson.FilePath;
import hudson.plugins.s3.Destination;
import hudson.util.Secret;

import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import com.amazonaws.services.cloudfront.model.CreateInvalidationRequest;
import com.amazonaws.services.cloudfront.model.CreateInvalidationResult;
import com.amazonaws.services.cloudfront.model.DistributionSummary;
import com.amazonaws.services.cloudfront.model.Invalidation;
import com.amazonaws.services.cloudfront.model.InvalidationBatch;
import com.amazonaws.services.cloudfront.model.ListDistributionsRequest;
import com.amazonaws.services.cloudfront.model.Paths;
import com.google.common.base.Function;
import com.google.common.collect.Lists;

public class CloudFrontInvalidationCallable extends AbstractCloudFrontCallable {

private static final long serialVersionUID = 1L;

private static final Logger log = Logger.getLogger(CloudFrontInvalidationCallable.class.getName());

private static final String UNIX_SEPARATOR = "/";
private final String bucket;
private final int searchPathLength;
private Function<FilePath, String> filePathToS3KeysTransformer;

public CloudFrontInvalidationCallable(String accessKey, Secret secretKey,
boolean useRole, String bucket, int searchPathLength) {
super(accessKey, secretKey, useRole);
this.bucket = bucket;
this.searchPathLength = searchPathLength;
filePathToS3KeysTransformer = new Function<FilePath, String>() {

@Override
public String apply(FilePath filePath) {
if (!isValidFile(filePath)) {
return null;
}
String fileName = filePath.getRemote().substring(CloudFrontInvalidationCallable.this.searchPathLength);
Destination dest = new Destination(CloudFrontInvalidationCallable.this.bucket, fileName);

String key = dest.objectName;
if (!key.startsWith(UNIX_SEPARATOR)) {
key = UNIX_SEPARATOR.concat(key);
}
return key;
}

private boolean isValidFile(FilePath filePath) {
try {
return !filePath.isDirectory();
} catch (Exception e) {
log.info(String.format("Failed to check filePath %s, %s",
filePath.getName(), e.toString()));
}
return false;
}

};
}

public void invoke(FilePath...paths) {
String[] keysToInvalidate = getS3Keys(paths).toArray(new String[paths.length]);
List<DistributionSummary> distributionitems = getClient().listDistributions(new ListDistributionsRequest()).getDistributionList().getItems();
for (DistributionSummary distribution: distributionitems){
if (distribution.isEnabled()){
Invalidation invalidationResult = invalidateFiles(distribution, keysToInvalidate);
log.info(String.format(
"Invalidated cache %s of path %s with status = %s", distribution.getAliases().toString(),
invalidationResult.getInvalidationBatch().getPaths().toString(), invalidationResult.getStatus()));
}

}
}

private List<String> getS3Keys(FilePath[] paths) {
return Lists.transform(Arrays.asList(paths), filePathToS3KeysTransformer);
}

private Invalidation invalidateFiles(DistributionSummary distributionSummary, String[] keysToInvalidate) {
String distributionId = distributionSummary.getId();
String callerReference = String.valueOf(System.currentTimeMillis());
CreateInvalidationResult invalidationResult = getClient().createInvalidation(
new CreateInvalidationRequest(distributionId,
new InvalidationBatch(new Paths().withItems(keysToInvalidate)
.withQuantity(keysToInvalidate.length), callerReference)));
return invalidationResult.getInvalidation();
}
}
4 changes: 3 additions & 1 deletion src/main/resources/hudson/plugins/s3/Entry/config.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
<f:entry field="gzipFiles" title="GZIP files">
<f:checkbox />
</f:entry>

<f:entry field="invalidateAfterUpload" title="Invalidate after upload">
<f:checkbox />
</f:entry>
<f:entry title="Metadata tags">
<f:repeatableProperty field="userMetadata">
<f:entry title="">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div>When enabled, Jenkins will perform CloudFront invalidation of uploaded files among available distributions</div>

0 comments on commit 075ecb5

Please sign in to comment.