Skip to content
Merged
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
106 changes: 71 additions & 35 deletions src/main/java/com/amazonaws/codedeploy/AWSCodeDeployPublisher.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*
* Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
*
* http://aws.amazon.com/apache2.0
*
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
Expand Down Expand Up @@ -76,7 +76,7 @@ public class AWSCodeDeployPublisher extends Publisher {
public static final long DEFAULT_TIMEOUT_SECONDS = 900;
public static final long DEFAULT_POLLING_FREQUENCY_SECONDS = 15;
public static final String ROLE_SESSION_NAME = "jenkins-codedeploy-plugin";
public static final Regions[] AVAILABLE_REGIONS = {Regions.US_EAST_1, Regions.US_WEST_2};
public static final Regions[] AVAILABLE_REGIONS = {Regions.AP_SOUTHEAST_2, Regions.EU_WEST_1, Regions.US_EAST_1, Regions.US_WEST_2};

private final String s3bucket;
private final String s3prefix;
Expand All @@ -91,6 +91,7 @@ public class AWSCodeDeployPublisher extends Publisher {
private final String region;
private final String includes;
private final String excludes;
private final String subdirectory;
private final String proxyHost;
private final int proxyPort;

Expand Down Expand Up @@ -118,7 +119,8 @@ public AWSCodeDeployPublisher(
String includes,
String proxyHost,
int proxyPort,
String excludes) {
String excludes,
String subdirectory) {

this.externalId = externalId;
this.applicationName = applicationName;
Expand All @@ -127,6 +129,7 @@ public AWSCodeDeployPublisher(
this.region = region;
this.includes = includes;
this.excludes = excludes;
this.subdirectory = subdirectory;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
this.credentials = credentials;
Expand Down Expand Up @@ -178,8 +181,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis
if ("awsAccessKey".equals(credentials)) {
if (StringUtils.isEmpty(this.awsAccessKey) && StringUtils.isEmpty(this.awsSecretKey)) {
aws = AWSClients.fromDefaultCredentialChain(
this.region,
this.proxyHost,
this.region,
this.proxyHost,
this.proxyPort);
} else {
aws = AWSClients.fromBasicCredentials(
Expand All @@ -205,7 +208,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis
verifyCodeDeployApplication(aws);

String projectName = build.getProject().getName();
RevisionLocation revisionLocation = zipAndUpload(aws, projectName, build.getWorkspace());
RevisionLocation revisionLocation = zipAndUpload(aws, projectName, getSourceDirectory(build.getWorkspace()));

registerRevision(aws, revisionLocation);
String deploymentId = createDeployment(aws, revisionLocation);
Expand All @@ -224,6 +227,30 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis
return success;
}

private FilePath getSourceDirectory(FilePath basePath) throws IOException, InterruptedException {
String subdirectory = this.subdirectory.trim().length() > 0 ? this.subdirectory.trim() : "";
if (!subdirectory.isEmpty() && !subdirectory.startsWith("/")) {
subdirectory = "/" + subdirectory;
}
FilePath sourcePath = basePath.withSuffix(subdirectory).absolutize();
File sourceDirectory = new File(sourcePath.getRemote());
if (!sourceDirectory.isDirectory() || !isSubDirectory(basePath, sourcePath)) {
throw new IllegalArgumentException("Provided path is not a subdirectory of the workspace: " + sourcePath );
}
return sourcePath;
}

private boolean isSubDirectory(FilePath parent, FilePath child) {
FilePath parentFolder = child;
while (parentFolder!=null) {
if (parent.equals(parentFolder)) {
return true;
}
parentFolder = child.getParent();
}
return false;
}

private void verifyCodeDeployApplication(AWSClients aws) throws IllegalArgumentException {
// Check that the application exists
ListApplicationsResult applications = aws.codedeploy.listApplications();
Expand All @@ -243,43 +270,48 @@ private void verifyCodeDeployApplication(AWSClients aws) throws IllegalArgumentE
}
}

private RevisionLocation zipAndUpload(AWSClients aws, String projectName, FilePath workspace) throws IOException, InterruptedException {
private RevisionLocation zipAndUpload(AWSClients aws, String projectName, FilePath sourceDirectory) throws IOException, InterruptedException {

File zipFile = File.createTempFile(projectName + "-", ".zip");
this.logger.println("Zipping workspace into " + zipFile.getAbsolutePath());
String key;

workspace.zip(
new FileOutputStream(zipFile),
new DirScanner.Glob(this.includes, this.excludes)
);
try {
this.logger.println("Zipping files into " + zipFile.getAbsolutePath());

String key;
if (this.s3prefix.isEmpty()) {
key = zipFile.getName();
} else {
key = this.s3prefix;
if (this.s3prefix.endsWith("/")) {
key += zipFile.getName();
sourceDirectory.zip(
new FileOutputStream(zipFile),
new DirScanner.Glob(this.includes, this.excludes)
);

if (this.s3prefix.isEmpty()) {
key = zipFile.getName();
} else {
key += "/" + zipFile.getName();
key = this.s3prefix;
if (this.s3prefix.endsWith("/")) {
key += zipFile.getName();
} else {
key += "/" + zipFile.getName();
}
}
}

logger.println("Uploading zip to s3://" + this.s3bucket + "/" + key);
PutObjectResult s3result = aws.s3.putObject(this.s3bucket, key, zipFile);
logger.println("Uploading zip to s3://" + this.s3bucket + "/" + key);
PutObjectResult s3result = aws.s3.putObject(this.s3bucket, key, zipFile);


S3Location s3Location = new S3Location();
s3Location.setBucket(this.s3bucket);
s3Location.setKey(key);
s3Location.setBundleType(BundleType.Zip);
s3Location.setETag(s3result.getETag());
S3Location s3Location = new S3Location();
s3Location.setBucket(this.s3bucket);
s3Location.setKey(key);
s3Location.setBundleType(BundleType.Zip);
s3Location.setETag(s3result.getETag());

RevisionLocation revisionLocation = new RevisionLocation();
revisionLocation.setRevisionType(RevisionLocationType.S3);
revisionLocation.setS3Location(s3Location);
RevisionLocation revisionLocation = new RevisionLocation();
revisionLocation.setRevisionType(RevisionLocationType.S3);
revisionLocation.setS3Location(s3Location);

return revisionLocation;
return revisionLocation;
} finally {
zipFile.delete();
}
}

private void registerRevision(AWSClients aws, RevisionLocation revisionLocation) {
Expand Down Expand Up @@ -413,7 +445,7 @@ public FormValidation doCheckName(@QueryParameter String value)
}

public boolean isApplicable(Class<? extends AbstractProject> aClass) {
// Indicates that this builder can be used with all kinds of project types
// Indicates that this builder can be used with all kinds of project types
return true;
}

Expand Down Expand Up @@ -583,6 +615,10 @@ public String getExcludes() {
return excludes;
}

public String getSubdirectory() {
return subdirectory;
}

public String getRegion() {
return region;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
<f:textbox />
</f:entry>
<f:entry title="S3 Prefix" field="s3prefix">
<f:textbox default="/" />
<f:textbox default="" />
</f:entry>
<f:entry title="Subdirectory" field="subdirectory">
<f:textbox default="" />
</f:entry>
<f:entry title="Include Files" field="includes">
<f:textbox default="**" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
A subdirectory inside the workspace to be packed instead of the whole workspace. Remember that the
<i>appspec.yml</i> must be placed at the top of the .zip archive. The <i>excludes</i> and <i>includes</i> will
be applied based on this path.
</div>