Skip to content

Commit

Permalink
Add multi-region package and deploy custom task
Browse files Browse the repository at this point in the history
  • Loading branch information
fieldju committed Apr 11, 2017
1 parent 7b73428 commit bb5c2ad
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 138 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
group=com.fieldju
artifactId=gradle-aws-sam-deployer-plugin
version=1.4.1
version=1.5.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.fieldju.gradle.plugins.lambdasam.services

import com.amazonaws.regions.Regions
import com.amazonaws.services.cloudformation.AmazonCloudFormationClient
import com.fieldju.commons.StringUtils
import com.fieldju.gradle.plugins.lambdasam.services.cloudformation.CloudFormationDeployer
import com.fieldju.gradle.plugins.lambdasam.services.s3.S3Uploader
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.logging.Logger

class PackageAndDeployTaskHelper {

Logger logger;

PackageAndDeployTaskHelper(Logger logger) {
this.logger = logger
}

def multiRegionDeploy(String templatePath,
Map<String, String> regionTemplatePathMap,
List<String> regions,
String stackName,
Map<String, Map<String, String>> regionToParameterOverridesMap,
boolean executeChangeSet) {

regions.each { String region ->
Map<String, String> parameterOverrides
if (regionToParameterOverridesMap.containsKey(region)) {
parameterOverrides = regionToParameterOverridesMap."${region}"
} else {
logger.lifecycle("regionToParameterOverridesMap does not contain an entry for region ${region} defaulting to empty map")
parameterOverrides = [:]
}

String calculatedTemplatePath
if (StringUtils.isNotBlank(templatePath)) {
calculatedTemplatePath = templatePath
} else if (regionTemplatePathMap.containsKey(region)) {
calculatedTemplatePath = regionTemplatePathMap."${region}"
} else {
throw new GradleException("templatePath or regionTemplatePathMap.'\${region}' for " +
"region: ${region} must be set")
}

CloudFormationDeployer deployer = new CloudFormationDeployer(
AmazonCloudFormationClient.builder()
.standard()
.withRegion(Regions.fromName(region))
.build() as AmazonCloudFormationClient
)

logger.lifecycle("Creating and executing changeset for ${templatePath} with stackname ${stackName} in region ${region} with overrides ${parameterOverrides}")
deployer.deployStack(stackName, calculatedTemplatePath, parameterOverrides, executeChangeSet)
}
}

def uploadArtifactsAndInjectS3UrlsIntoCopiedCFTemplate(String region,
String s3Bucket,
String s3Prefix,
String kmsKeyId,
boolean forceUploads,
String samTemplatePath,
Map<String, String> tokenArtifactMap,
Project project,
AntBuilder ant) {
Map<String, String> tokenS3UriMap = [:]
if (tokenArtifactMap.isEmpty()) {
logger.warn("There were no tokens defined in the tokenArtifactMap, this task will not upload any" +
" artifacts to s3 and automatically inject them into the copied deployable sam template.")
} else {
S3Uploader s3Uploader = new S3Uploader(region, kmsKeyId, forceUploads)

tokenArtifactMap.each { token, artifactPath ->
File artifactToUploadToS3 = new File(artifactPath)
if (! (artifactToUploadToS3.exists() && artifactToUploadToS3.isFile())) {
throw new GradleException("The artifact: ${artifactPath} for token: ${token} did not exist or " +
"was not a file (you must archive folders)")
}

try {
def s3Uri = s3Uploader.uploadWithDedup(s3Bucket, s3Prefix, artifactToUploadToS3)
tokenS3UriMap.put(token, s3Uri)
} catch (Throwable t) {
throw new GradleException("Failed to upload artifact ${artifactToUploadToS3.absolutePath}", t)
}
}
}

logger.lifecycle("Copying sam template to build dir")
// copy the template to the build dir
File buildDir = new File("${project.getBuildDir().absolutePath}${File.separator}sam")
buildDir.mkdirs()
File dest = new File("${buildDir.absolutePath}${File.separator}sam-deploy-${region}.yaml")
dest.write(getSamTemplateAsString(samTemplatePath))
// replace the tokens with the s3 URIs
tokenS3UriMap.each { token, uri ->
logger.lifecycle("Injecting ${uri} into ${dest.absolutePath} for token: ${token}")
ant.replace(file: dest.absolutePath, token: token, value: uri)
}

return dest.absolutePath
}

private String getSamTemplateAsString(String samTemplatePath) {
if (samTemplatePath == null || samTemplatePath == "") {
throw new GradleException("samTemplatePath is a required property")
}

File samTemplate = new File(samTemplatePath)

// if the template is not a real file fail
if (! (samTemplate.exists() && samTemplate.isFile())) {
throw new GradleException("The template: ${samTemplatePath} did not exist or was not a file")
}

return samTemplate.text
}

def deployProcessedTemplate(String region,
String stackName,
String templatePath,
Map<String, String> parameterOverrides,
boolean executeChangeSet) {

if (StringUtils.isBlank(stackName)) {
throw new GradleException("stackName cannot be blank")
}

CloudFormationDeployer deployer = new CloudFormationDeployer(
AmazonCloudFormationClient.builder()
.standard()
.withRegion(Regions.fromName(region))
.build() as AmazonCloudFormationClient
)

deployer.deployStack(stackName, templatePath, parameterOverrides, executeChangeSet)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.fieldju.gradle.plugins.lambdasam.tasks

import com.amazonaws.regions.Regions
import com.amazonaws.services.cloudformation.AmazonCloudFormationClient
import com.fieldju.gradle.plugins.lambdasam.services.PackageAndDeployTaskHelper
import com.fieldju.gradle.plugins.lambdasam.services.cloudformation.CloudFormationDeployer
import org.gradle.api.GradleException
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional

class DeploySamTask extends SamTask {

Expand All @@ -15,7 +17,8 @@ class DeploySamTask extends SamTask {
Map<String, String> parameterOverrides

@Input
String templatePath = "${project.buildDir.absolutePath}${File.separator}sam${File.separator}sam-deploy.yaml"
@Optional
String templatePath

@Input
boolean executeChangeSet = true
Expand All @@ -29,22 +32,9 @@ class DeploySamTask extends SamTask {
*/
@Override
void taskAction() {
CloudFormationDeployer deployer = new CloudFormationDeployer(
AmazonCloudFormationClient.builder()
.standard()
.withRegion(Regions.fromName(region))
.build() as AmazonCloudFormationClient
)

deployer.deployStack(getStackName(), templatePath, parameterOverrides, executeChangeSet)
}


def calculatedTemplatePath = templatePath ? templatePath : "${project.buildDir.absolutePath}${File.separator}sam${File.separator}sam-deploy-${region}.yaml"

private String getStackName() {
if (stackName == null || stackName == "") {
throw new GradleException("${stackName} is a required property")
}
return stackName
PackageAndDeployTaskHelper helper = new PackageAndDeployTaskHelper(logger)
helper.deployProcessedTemplate(region, stackName, calculatedTemplatePath, parameterOverrides, executeChangeSet)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.fieldju.gradle.plugins.lambdasam.tasks

import com.fieldju.gradle.plugins.lambdasam.services.PackageAndDeployTaskHelper
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction

class MultiRegionDeploySamTask extends DefaultTask {

@Input
@Optional
String templatePath

@Input
@Optional
def regionTemplatePathMap = [:]

@Input
def regions = []

@Input
String stackName

@Input
Map<String, Map<String, String>> regionToParameterOverridesMap = [:]

@Input
boolean executeChangeSet = true

@TaskAction
void taskAction() {
PackageAndDeployTaskHelper helper = new PackageAndDeployTaskHelper(logger)
helper.multiRegionDeploy(templatePath, regionTemplatePathMap, regions,
stackName, regionToParameterOverridesMap, executeChangeSet)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.fieldju.gradle.plugins.lambdasam.tasks

import com.fieldju.gradle.plugins.lambdasam.services.PackageAndDeployTaskHelper
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction

class MultiRegionPackageAndDeploySamTask extends DefaultTask {

@Input
def regions = []

@Input
String stackName

@Input
String templatePath

@Input
@Optional
Map<String, String> regionToS3BucketMap = [:]

@Input
@Optional
String s3Prefix

@Input
@Optional
Map<String, String> regionToKmsKeyIdMap = [:]

@Input
@Optional
boolean forceUploads

@Input
Map<String, String> tokenArtifactMap

@Input
Map<String, String> parameterOverrides = [:]

@Input
Map<String, Map<String, String>> regionToParameterOverridesMap = [:]

@Input
boolean executeChangeSet = true

@TaskAction
void taskAction() {
PackageAndDeployTaskHelper helper = new PackageAndDeployTaskHelper(logger)

regions.each { String region ->
logger.lifecycle("---- Processing region: ${region} ----")

String s3Bucket = regionToS3BucketMap."${region}"
String kmsKeyId = regionToKmsKeyIdMap."${region}"

def processedTemplatePath = helper.uploadArtifactsAndInjectS3UrlsIntoCopiedCFTemplate(region, s3Bucket,
s3Prefix, kmsKeyId, forceUploads, templatePath, tokenArtifactMap, project, ant)

Map<String, String> calculatedParameterOverrides = [:]
if (parameterOverrides.isEmpty()) {
calculatedParameterOverrides = parameterOverrides
} else if (regionToParameterOverridesMap.containsKey(region)) {
calculatedParameterOverrides = regionToParameterOverridesMap."${region}"
}

helper.deployProcessedTemplate(region, stackName, processedTemplatePath, calculatedParameterOverrides, executeChangeSet)
}
}

}

This file was deleted.

Loading

0 comments on commit bb5c2ad

Please sign in to comment.