diff --git a/gradle.properties b/gradle.properties index c19f287..3562b60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group=com.fieldju artifactId=gradle-aws-sam-deployer-plugin -version=1.4.1 +version=1.5.0 diff --git a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/services/PackageAndDeployTaskHelper.groovy b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/services/PackageAndDeployTaskHelper.groovy new file mode 100644 index 0000000..4f439d4 --- /dev/null +++ b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/services/PackageAndDeployTaskHelper.groovy @@ -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 regionTemplatePathMap, + List regions, + String stackName, + Map> regionToParameterOverridesMap, + boolean executeChangeSet) { + + regions.each { String region -> + Map 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 tokenArtifactMap, + Project project, + AntBuilder ant) { + Map 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 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) + } +} diff --git a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/DeploySamTask.groovy b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/DeploySamTask.groovy index c6a33fa..cad6755 100644 --- a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/DeploySamTask.groovy +++ b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/DeploySamTask.groovy @@ -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 { @@ -15,7 +17,8 @@ class DeploySamTask extends SamTask { Map parameterOverrides @Input - String templatePath = "${project.buildDir.absolutePath}${File.separator}sam${File.separator}sam-deploy.yaml" + @Optional + String templatePath @Input boolean executeChangeSet = true @@ -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) } } diff --git a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionDeploySamTask.groovy b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionDeploySamTask.groovy new file mode 100644 index 0000000..dc180e3 --- /dev/null +++ b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionDeploySamTask.groovy @@ -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> regionToParameterOverridesMap = [:] + + @Input + boolean executeChangeSet = true + + @TaskAction + void taskAction() { + PackageAndDeployTaskHelper helper = new PackageAndDeployTaskHelper(logger) + helper.multiRegionDeploy(templatePath, regionTemplatePathMap, regions, + stackName, regionToParameterOverridesMap, executeChangeSet) + } +} diff --git a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionPackageAndDeploySamTask.groovy b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionPackageAndDeploySamTask.groovy new file mode 100644 index 0000000..5e4fb08 --- /dev/null +++ b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionPackageAndDeploySamTask.groovy @@ -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 regionToS3BucketMap = [:] + + @Input + @Optional + String s3Prefix + + @Input + @Optional + Map regionToKmsKeyIdMap = [:] + + @Input + @Optional + boolean forceUploads + + @Input + Map tokenArtifactMap + + @Input + Map parameterOverrides = [:] + + @Input + Map> 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 calculatedParameterOverrides = [:] + if (parameterOverrides.isEmpty()) { + calculatedParameterOverrides = parameterOverrides + } else if (regionToParameterOverridesMap.containsKey(region)) { + calculatedParameterOverrides = regionToParameterOverridesMap."${region}" + } + + helper.deployProcessedTemplate(region, stackName, processedTemplatePath, calculatedParameterOverrides, executeChangeSet) + } + } + +} diff --git a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionSamDeployTask.groovy b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionSamDeployTask.groovy deleted file mode 100644 index f6843e7..0000000 --- a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/MultiRegionSamDeployTask.groovy +++ /dev/null @@ -1,67 +0,0 @@ -package com.fieldju.gradle.plugins.lambdasam.tasks - -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 org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction - -class MultiRegionSamDeployTask extends DefaultTask { - - @Input - @Optional - String templatePath - - @Input - @Optional - def regionTemplatePathMap = [:] - - @Input - def regions = [] - - @Input - String stackName - - @Input - Map> regionToParameterOverridesMap = [:] - - @Input - boolean executeChangeSet = true - - @TaskAction - void taskAction() { - regions.each { String region -> - Map 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 templatePath - if (StringUtils.isNotBlank(this.templatePath)) { - templatePath = this.templatePath - } else if (regionTemplatePathMap.containsKey(region)) { - templatePath = 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, templatePath, parameterOverrides, executeChangeSet) - } - } -} diff --git a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/PackageSamTask.groovy b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/PackageSamTask.groovy index 226f156..399038d 100644 --- a/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/PackageSamTask.groovy +++ b/src/main/groovy/com/fieldju/gradle/plugins/lambdasam/tasks/PackageSamTask.groovy @@ -1,7 +1,6 @@ package com.fieldju.gradle.plugins.lambdasam.tasks -import com.fieldju.gradle.plugins.lambdasam.services.s3.S3Uploader -import org.gradle.api.GradleException +import com.fieldju.gradle.plugins.lambdasam.services.PackageAndDeployTaskHelper import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional @@ -38,56 +37,8 @@ class PackageSamTask extends SamTask { */ @Override void taskAction() { - - Map tokenArtifactMap = tokenArtifactMap - Map 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.yaml") - dest.write(getSamTemplateAsString()) - // 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) - } - } - - private String getSamTemplateAsString() { - 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 + PackageAndDeployTaskHelper helper = new PackageAndDeployTaskHelper(logger) + helper.uploadArtifactsAndInjectS3UrlsIntoCopiedCFTemplate(region, s3Bucket, s3Prefix, kmsKeyId, forceUploads, + samTemplatePath, tokenArtifactMap, project, ant) } }