From 209b1e43e43ef7391d08f7b527cbe83e920b8732 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Wed, 15 Jan 2020 16:41:16 +0200 Subject: [PATCH 1/2] feat(ecr-assets): simplify docker asset publishing As part of our work on [continuous delivery for CDK apps], we decided to store all docker assets in a single ECR repository per environment. This is consistent with file assets, which are stored in a single S3 bucket which is part of the bootstrap stack. This environment-specific ECR repository uses a well-known physical name `aws-cdk/assets` and will be automatically created if needed. In the future, this repository will be created as part of the environment bootstrapping process. The primary change is that the location of docker image assets will be fully determined by the framework. The ECR repository name will be hard-coded to `aws-cdk/assets` and the image tag will be based on the source hash of the docker asset. This basically means that we could get rid of the CloudFormation parameter that the CLI used to assign with the image name which helps to reduce the number of asset parameters (#3463). Since from now on the asset ECR repository will contain different types of images (and not versions of the same image), there is no concept of "latest" image anymore and the optimization that was triggered by the `--ci` flag in the CLI is no longer relevant (pull the "latest"). Luckily CodeBuild now supports docker image layer caching, so this should be the preferred way to optimize docker build times. The `--ci` feature of the CLI is no longer doing anything. Furthermore, before this change, in order to clean up ECR repositories, a custom resource called `AdoptedRepository` was automatically added to the stack for each asset. The purpose of this resource was to remove the asset's ECR repository it if the asset was no longer referenced by this stack. To address this need with the centralized repository, we plan to introduce a garbage collection capability that users will be able to invoke in order to clean up unused assets both from ECR and S3. We will introduce a way to customize asset repository names as part of the CI/CD project. In the meantime, if you need to override the default "aws-cdk/assets" name, you can specify a repo name through the context key `assets-ecr-repository-name` (`--context` in the CLI, `cdk.json`, `new App({ context })` or `stack.setContext`). BACKWARDS COMPATIBILITY As per our guidelines for backwards compatibility, the CLI must be backwards compatible with apps from before this change. However, apps that use CDK >= 1.21.0 will require an upgrade of the CLI. Therefore, to introduce this change, we have made the following non-breaking changes in cx-api: 1. Make `imageNameParameter` optional. If it is specified, the CLI will continue ti 2. Add an optional `imageTag` which instructs the CLI what tag to use for the image. If omitted (by previous versions), `latest` will be used as before. To make it easy to reason about the behavior for old apps, the CLI now has a new implementations for `prepareContainerAsset` called `prepareContainerImageAssetNew`. This new code path is triggered when the asset metadata *does not include* `imageNameParameter`. The new implementation requires that both `repositoryName` and `imageTag` will be defined. The old code path was only modified to support the new optional `imageTag` property (although it is unlikely to be exercised). Additional backwards compatibility concerns: - New ECR repositories the CLI creates will not have the lifecycle policy that retains only the last 5 docker images. This should not have a functional impact on users, but goes back to the imminent garbage collection project. - The removal of the `AdoptedRepository` resource from all stacks will result in the deletion of all ECR previously created ECR repositories (this is what the AdoptedRepository resource is designed to do). This can be harmful since these repositories are being referenced by the stack. To address this, we invalidate the image ID by salting the source hash. This means that after this change, all container images will have a new ID, which is not maintained by the removed adopted repository resource. TESTING - Unit tests for `prepareContainerImage` were duplicated and extended to exercise the new code path while preserving tests for old path. - All CLI integration tests were executed successfully against the new version. - Manually tested that the new CLI works with old apps. This change also fixes #5807 so that custom docker file names are relative and not absolute paths. [continuous delivery for CDK apps]: https://github.com/aws/aws-cdk/pull/3437 BREAKING CHANGE: all docker image assets are now pushed to a single ECR repository named `aws-cdk/assets` with an image tag based on the hash of the docker build source directory (the directory where your `Dockerfile` resides). See PR #5733 for details and discussion. --- .../lib/adopt-repository/.gitignore | 1 - .../lib/adopt-repository/handler.js | 131 ------- .../aws-ecr-assets/lib/adopted-repository.ts | 94 ----- .../aws-ecr-assets/lib/image-asset.ts | 29 +- .../test/integ.assets-docker.expected.json | 203 +--------- .../test/integ.assets-docker.ts | 9 + .../integ.nested-stacks-docker.expected.json | 45 +-- .../test/integ.nested-stacks-docker.ts | 4 + .../aws-ecr-assets/test/test.adpot-repo.ts | 371 ------------------ .../aws-ecr-assets/test/test.image-asset.ts | 121 ++---- packages/@aws-cdk/core/lib/assets.ts | 1 + .../core/lib/private/asset-parameters.ts | 13 - packages/@aws-cdk/core/lib/stack.ts | 50 ++- packages/@aws-cdk/core/test/test.assets.ts | 56 ++- packages/@aws-cdk/cx-api/lib/assets.ts | 31 +- packages/@aws-cdk/cx-api/lib/versioning.ts | 9 +- .../__snapshots__/cloud-assembly.test.js.snap | 2 +- packages/aws-cdk/bin/cdk.ts | 3 +- packages/aws-cdk/lib/api/deploy-stack.ts | 3 +- packages/aws-cdk/lib/api/deployment-target.ts | 2 - packages/aws-cdk/lib/api/toolkit-info.ts | 73 +--- packages/aws-cdk/lib/assets.ts | 8 +- packages/aws-cdk/lib/cdk-toolkit.ts | 8 - packages/aws-cdk/lib/docker.ts | 84 +++- packages/aws-cdk/test/assets.test.ts | 4 +- packages/aws-cdk/test/docker-new.test.ts | 304 ++++++++++++++ packages/aws-cdk/test/docker.test.ts | 70 +++- 27 files changed, 667 insertions(+), 1062 deletions(-) delete mode 100644 packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/.gitignore delete mode 100644 packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/handler.js delete mode 100644 packages/@aws-cdk/aws-ecr-assets/lib/adopted-repository.ts delete mode 100644 packages/@aws-cdk/aws-ecr-assets/test/test.adpot-repo.ts create mode 100644 packages/aws-cdk/test/docker-new.test.ts diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/.gitignore b/packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/.gitignore deleted file mode 100644 index d4aa116a26c73..0000000000000 --- a/packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.js diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/handler.js b/packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/handler.js deleted file mode 100644 index 3d0cb009c8773..0000000000000 --- a/packages/@aws-cdk/aws-ecr-assets/lib/adopt-repository/handler.js +++ /dev/null @@ -1,131 +0,0 @@ -const AWS = require('aws-sdk'); -const ecr = new AWS.ECR(); - -exports.handler = async function(event, context, _callback, respond) { - respond = respond || respondCFN; - try { - console.log(JSON.stringify(event)); - - const markerStatement = { - Sid: event.StackId, - Effect: "Deny", - Action: "OwnedBy:CDKStack", - Principal: "*" - }; - - // The repository must already exist - async function getAdopter(name) { - try { - const policyResponse = await ecr.getRepositoryPolicy({ repositoryName: name }).promise(); - const policy = JSON.parse(policyResponse.policyText); - // Search the policy for an adopter marker - return (policy.Statement || []).find((x) => x.Action === markerStatement.Action) || {}; - } catch (e) { - if (e.code !== 'RepositoryPolicyNotFoundException') { throw e; } - return {}; - } - } - - let repo = event.ResourceProperties.RepositoryName; - if (!repo) { - throw new Error('Missing required property "RepositoryName"'); - } - const isRepoUri = repo.match(/^(\d+\.dkr\.ecr\.[^.]+\.[^/]+\/)(.+)$/i); - if (isRepoUri) { - repo = isRepoUri[2]; - } - - const adopter = await getAdopter(repo); - if (event.RequestType === 'Delete') { - if (!adopter.Sid) { - console.log(`Repository '${repo}' not found. Delete is no-op`); - } else { - if (adopter.Sid !== markerStatement.Sid) { - throw new Error(`This repository is already owned by another stack: ${adopter.Sid}`); - } - try { - console.log('Deleting', repo); - const ids = (await ecr.listImages({ repositoryName: repo }).promise()).imageIds; - await ecr.batchDeleteImage({ repositoryName: repo, imageIds: ids }).promise(); - await ecr.deleteRepository({ repositoryName: repo }).promise(); - } catch(e) { - if (e.code !== 'RepositoryNotFoundException') { throw e; } - } - } - } - - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - if (adopter.Sid !== undefined && adopter.Sid !== markerStatement.Sid) { - throw new Error(`This repository is already owned by another stack: ${adopter.Sid}`); - } - console.log('Adopting', repo); - - const policy = event.ResourceProperties.PolicyDocument || { - Version: '2008-10-17', - Statement: [ ] - }; - - if (!policy.Version) { - policy.Version = '2008-10-17'; - } - - if (!policy.Statement) { - policy.Statement = [ ]; - } - - if (!Array.isArray(policy.Statement)) { - policy.Statement = [ policy.Statement ]; - } - - policy.Statement.push(markerStatement); - - console.log('policy document:', JSON.stringify(policy, undefined, 2)); - - await ecr.setRepositoryPolicy({ repositoryName: repo, policyText: JSON.stringify(policy) }).promise(); - } - - // we reflect back the repository name as a resource attribute - // this will allow taking an implicit dependency in this custom resource by - // referencing this attribute via { "Fn::GetAtt": [ ID, "RepositoryName" ] } - await respond("SUCCESS", "OK", repo, { - RepositoryName: repo - }); - } catch (e) { - console.log(e); - await respond("FAILED", e.message, context.logStreamName, {}); - } - - function respondCFN(responseStatus, reason, physId, data) { - const responseBody = JSON.stringify({ - Status: responseStatus, - Reason: reason, - PhysicalResourceId: physId, - StackId: event.StackId, - RequestId: event.RequestId, - LogicalResourceId: event.LogicalResourceId, - NoEcho: false, - Data: data - }); - - console.log('Responding', JSON.stringify(responseBody)); - - const parsedUrl = require('url').parse(event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: "PUT", - headers: { "content-type": "", "content-length": responseBody.length } - }; - - return new Promise((resolve, reject) => { - try { - const request = require('https').request(requestOptions, resolve); - request.on("error", reject); - request.write(responseBody); - request.end(); - } catch (e) { - reject(e); - } - }); - } -} diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/adopted-repository.ts b/packages/@aws-cdk/aws-ecr-assets/lib/adopted-repository.ts deleted file mode 100644 index 6c68e1df64b6f..0000000000000 --- a/packages/@aws-cdk/aws-ecr-assets/lib/adopted-repository.ts +++ /dev/null @@ -1,94 +0,0 @@ -import * as cfn from '@aws-cdk/aws-cloudformation'; -import * as ecr from '@aws-cdk/aws-ecr'; -import * as iam from '@aws-cdk/aws-iam'; -import * as lambda from '@aws-cdk/aws-lambda'; -import * as cdk from '@aws-cdk/core'; -import * as path from 'path'; - -interface AdoptedRepositoryProps { - /** - * An ECR repository to adopt. Once adopted, the repository will - * practically become part of this stack, so it will be removed when - * the stack is deleted. - */ - repositoryName: string; -} - -/** - * An internal class used to adopt an ECR repository used for the locally built - * image into the stack. - * - * Since the repository is not created by the stack (but by the CDK toolkit), - * adopting will make the repository "owned" by the stack. It will be cleaned - * up when the stack gets deleted, to avoid leaving orphaned repositories on - * stack cleanup. - */ -export class AdoptedRepository extends ecr.RepositoryBase { - public readonly repositoryName: string; - public readonly repositoryArn: string; - - private readonly policyDocument = new iam.PolicyDocument(); - - constructor(scope: cdk.Construct, id: string, private readonly props: AdoptedRepositoryProps) { - super(scope, id); - - const fn = new lambda.SingletonFunction(this, 'Function', { - runtime: lambda.Runtime.NODEJS_10_X, - lambdaPurpose: 'AdoptEcrRepository', - handler: 'handler.handler', - code: lambda.Code.fromAsset(path.join(__dirname, 'adopt-repository')), - uuid: 'dbc60def-c595-44bc-aa5c-28c95d68f62c', - timeout: cdk.Duration.minutes(5) - }); - - fn.addToRolePolicy(new iam.PolicyStatement({ - resources: [ecr.Repository.arnForLocalRepository(props.repositoryName, this)], - actions: [ - 'ecr:GetRepositoryPolicy', - 'ecr:SetRepositoryPolicy', - 'ecr:DeleteRepository', - 'ecr:ListImages', - 'ecr:BatchDeleteImage' - ], - })); - - const adopter = new cfn.CustomResource(this, 'Resource', { - resourceType: 'Custom::ECRAdoptedRepository', - provider: cfn.CustomResourceProvider.lambda(fn), - properties: { - RepositoryName: props.repositoryName, - PolicyDocument: this.policyDocument - } - }); - if (fn.role) { - // Need to explicitly depend on the role's policies, so they are applied before we try to use them - adopter.node.addDependency(fn.role); - } - - // we use the Fn::GetAtt with the RepositoryName returned by the custom - // resource in order to implicitly create a dependency between consumers - // and the custom resource. - this.repositoryName = adopter.getAtt('RepositoryName').toString(); - - // this this repository is "local" to the stack (in the same region/account) - // we can render it's ARN from it's name. - this.repositoryArn = ecr.Repository.arnForLocalRepository(this.repositoryName, this); - } - - /** - * Export this repository from the stack - */ - public export() { - return this.props; - } - - /** - * Adds a statement to the repository resource policy. - * - * Contrary to normal imported repositories, which no-op here, we can - * use the custom resource to modify the ECR resource policy if needed. - */ - public addToResourcePolicy(statement: iam.PolicyStatement) { - this.policyDocument.addStatements(statement); - } -} diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts index cc011d5b928be..3a0cd48f2315c 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -3,7 +3,6 @@ import * as ecr from '@aws-cdk/aws-ecr'; import { Construct, Stack, Token } from '@aws-cdk/core'; import * as fs from 'fs'; import * as path from 'path'; -import { AdoptedRepository } from './adopted-repository'; export interface DockerImageAssetProps extends assets.FingerprintOptions { /** @@ -18,7 +17,10 @@ export interface DockerImageAssetProps extends assets.FingerprintOptions { * from a Kubernetes Pod. Note, this is only the repository name, without the * registry and the tag parts. * - * @default - automatically derived from the asset's ID. + * @default - the default ECR repository for CDK assets + * @deprecated to control the location of docker image assets, please override + * `Stack.addDockerImageAsset`. this feature will be removed in future + * releases. */ readonly repositoryName?: string; @@ -93,6 +95,10 @@ export class DockerImageAsset extends Construct implements assets.IAsset { exclude = [...exclude, ...fs.readFileSync(ignore).toString().split('\n').filter(e => !!e)]; } + if (props.repositoryName) { + this.node.addWarning(`DockerImageAsset.repositoryName is deprecated. Override "core.Stack.addDockerImageAsset" to control asset locations`); + } + // include build context in "extra" so it will impact the hash const extraHash: { [field: string]: any } = { }; if (props.extraHash) { extraHash.user = props.extraHash; } @@ -101,6 +107,11 @@ export class DockerImageAsset extends Construct implements assets.IAsset { if (props.file) { extraHash.file = props.file; } if (props.repositoryName) { extraHash.repositoryName = props.repositoryName; } + // add "salt" to the hash in order to invalidate the image in the upgrade to + // 1.21.0 which removes the AdoptedRepository resource (and will cause the + // deletion of the ECR repository the app used). + extraHash.version = '1.21.0'; + const staging = new assets.Staging(this, 'Staging', { ...props, exclude, @@ -117,18 +128,12 @@ export class DockerImageAsset extends Construct implements assets.IAsset { directoryName: staging.stagedPath, dockerBuildArgs: props.buildArgs, dockerBuildTarget: props.target, - dockerFile: file, - repositoryName: props.repositoryName || `cdk/${this.node.uniqueId.replace(/[:/]/g, '-').toLowerCase()}`, - sourceHash: staging.sourceHash + dockerFile: props.file, + repositoryName: props.repositoryName, + sourceHash: staging.sourceHash, }); - // Require that repository adoption happens first, so we route the - // input ARN into the Custom Resource and then get the URI which we use to - // refer to the image FROM the Custom Resource. - // - // If adoption fails (because the repository might be twice-adopted), we - // haven't already started using the image. - this.repository = new AdoptedRepository(this, 'AdoptRepository', { repositoryName: location.repositoryName }); + this.repository = ecr.Repository.fromRepositoryName(this, 'Repository', location.repositoryName); this.imageUri = location.imageUri; } } diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json index 01c97c292d139..1b289472b4cc1 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json @@ -1,78 +1,18 @@ { "Resources": { - "DockerImageAdoptRepositoryA86481BC": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } + "MyUserDC45028B": { + "Type": "AWS::IAM::User" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { + "MyUserDefaultPolicy7B897426": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { "Statement": [ { "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" ], "Effect": "Allow", "Resource": { @@ -91,108 +31,26 @@ { "Ref": "AWS::AccountId" }, - ":repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } + ":repository/aws-cdk/assets" ] ] } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" } ], "Version": "2012-10-17" }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ + "PolicyName": "MyUserDefaultPolicy7B897426", + "Users": [ { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" + "Ref": "MyUserDC45028B" } ] } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - } - }, - "Parameters": { - "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } }, "Outputs": { @@ -212,37 +70,10 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } + "/aws-cdk/assets:baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540" ] ] } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts index e9123cd842d84..b5d9347de5cc6 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as path from 'path'; import * as assets from '../lib'; @@ -9,6 +10,14 @@ const asset = new assets.DockerImageAsset(stack, 'DockerImage', { directory: path.join(__dirname, 'demo-image'), }); +const asset2 = new assets.DockerImageAsset(stack, 'DockerImage2', { + directory: path.join(__dirname, 'demo-image'), +}); + +const user = new iam.User(stack, 'MyUser'); +asset.repository.grantPull(user); +asset2.repository.grantPull(user); + new cdk.CfnOutput(stack, 'ImageUri', { value: asset.imageUri }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json index 39ad3e28fc6ce..0a189b5e6d229 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json @@ -17,7 +17,7 @@ }, "/", { - "Ref": "AssetParametersce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddcS3Bucket9C1F8CF3" + "Ref": "AssetParameters3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775S3Bucket6CE4FC69" }, "/", { @@ -27,7 +27,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddcS3VersionKeyD34A6061" + "Ref": "AssetParameters3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775S3VersionKey5FE1DA58" } ] } @@ -40,7 +40,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddcS3VersionKeyD34A6061" + "Ref": "AssetParameters3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775S3VersionKey5FE1DA58" } ] } @@ -48,49 +48,22 @@ } ] ] - }, - "Parameters": { - "referencetonestedstacksdockerAssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName6A10D714Ref": { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - }, - "referencetonestedstacksdockerAssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketDC016D33Ref": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "referencetonestedstacksdockerAssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey3A8B3E45Ref": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } } } } }, "Parameters": { - "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { + "AssetParameters3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775S3Bucket6CE4FC69": { "Type": "String", - "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" + "Description": "S3 bucket for asset \"3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775\"" }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { + "AssetParameters3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775S3VersionKey5FE1DA58": { "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + "Description": "S3 key for asset version \"3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775\"" }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { + "AssetParameters3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775ArtifactHash5D96BEC9": { "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddcS3Bucket9C1F8CF3": { - "Type": "String", - "Description": "S3 bucket for asset \"ce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddc\"" - }, - "AssetParametersce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddcS3VersionKeyD34A6061": { - "Type": "String", - "Description": "S3 key for asset version \"ce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddc\"" - }, - "AssetParametersce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddcArtifactHash3F66D1AC": { - "Type": "String", - "Description": "Artifact hash for asset \"ce471cbd591a9b42ec50b0bb34562bf37cf4e3c95f4ba7d4cf79a4a6b7d11ddc\"" + "Description": "Artifact hash for asset \"3e9c40a6bf877a4b814d5ca4817c057400e4f8f661cd52c8951bf40f5763b775\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts index 16a334a2e9e03..69484c7da852a 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts @@ -1,4 +1,5 @@ import * as cfn from '@aws-cdk/aws-cloudformation'; +import * as iam from '@aws-cdk/aws-iam'; import { App, CfnOutput, Construct, Stack, StackProps } from '@aws-cdk/core'; import * as path from 'path'; import * as ecr_assets from '../lib'; @@ -11,6 +12,9 @@ class TheNestedStack extends cfn.NestedStack { directory: path.join(__dirname, 'demo-image') }); + const user = new iam.User(this, 'User'); + asset.repository.grantPull(user); + new CfnOutput(this, 'output', { value: asset.imageUri }); } } diff --git a/packages/@aws-cdk/aws-ecr-assets/test/test.adpot-repo.ts b/packages/@aws-cdk/aws-ecr-assets/test/test.adpot-repo.ts deleted file mode 100644 index 8ea9e093cd766..0000000000000 --- a/packages/@aws-cdk/aws-ecr-assets/test/test.adpot-repo.ts +++ /dev/null @@ -1,371 +0,0 @@ -import { Test } from 'nodeunit'; -import * as path from 'path'; -import * as proxyquire from 'proxyquire'; - -let ecrMock: any; - -export = { - async 'exercise handler create with policy'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { - '@noCallThru': true, - "ECR": ECRWithEmptyPolicy, - } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - PolicyDocument: { - Version: '2008-10-01', - My: 'Document' - } - }, - RequestType: 'Create', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(JSON.parse(ecrMock.lastSetRepositoryPolicyRequest.policyText), { - My: "Document", - Version: '2008-10-01', - Statement: [ - { Sid: "StackId", Effect: "Deny", Action: "OwnedBy:CDKStack", Principal: "*" } - ] - }); - - test.deepEqual(output, { - responseStatus: 'SUCCESS', - reason: 'OK', - physId: 'RepositoryName', - data: { - RepositoryName: 'RepositoryName' - } - }); - - test.done(); - }, - - async 'exercise handler create with policy with object statement'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { - '@noCallThru': true, - "ECR": ECRWithEmptyPolicy, - } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - PolicyDocument: { - Statement: { Action: 'boom' } - } - }, - RequestType: 'Create', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(JSON.parse(ecrMock.lastSetRepositoryPolicyRequest.policyText), { - Version: '2008-10-17', - Statement: [ - { Action: 'boom' }, - { Sid: "StackId", Effect: "Deny", Action: "OwnedBy:CDKStack", Principal: "*" } - ] - }); - - test.deepEqual(output, { - responseStatus: 'SUCCESS', - reason: 'OK', - physId: 'RepositoryName', - data: { - RepositoryName: 'RepositoryName' - } - }); - - test.done(); - }, - - async 'exercise handler create with policy with array statement'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { - '@noCallThru': true, - "ECR": ECRWithEmptyPolicy, - } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - PolicyDocument: { - Statement: [ { Action: 'boom' }, { Resource: "foo" } ] - } - }, - RequestType: 'Create', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(JSON.parse(ecrMock.lastSetRepositoryPolicyRequest.policyText), { - Version: '2008-10-17', - Statement: [ - { Action: "boom" }, - { Resource: "foo" }, - { Sid: "StackId", Effect: "Deny", Action: "OwnedBy:CDKStack", Principal: "*" } - ] - }); - - test.deepEqual(output, { - responseStatus: 'SUCCESS', - reason: 'OK', - physId: 'RepositoryName', - data: { - RepositoryName: 'RepositoryName' - } - }); - - test.done(); - }, - - async 'exercise handler create'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { - '@noCallThru': true, - "ECR": ECRWithEmptyPolicy, - } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - }, - RequestType: 'Create', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(output, { - responseStatus: 'SUCCESS', - reason: 'OK', - physId: 'RepositoryName', - data: { - RepositoryName: 'RepositoryName' - } - }); - - test.done(); - }, - - async 'exercise handler delete'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { '@noCallThru': true, "ECR": ECRWithOwningPolicy } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - }, - RequestType: 'Delete', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(output, { - responseStatus: 'SUCCESS', - reason: 'OK', - physId: 'RepositoryName', - data: { - RepositoryName: 'RepositoryName' - } - }); - - test.done(); - }, - - async 'exercise "delete" handler when repository doesnt exist'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { '@noCallThru': true, "ECR": ECRWithRepositoryNotFound } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - }, - RequestType: 'Delete', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(output, { - responseStatus: 'SUCCESS', - reason: 'OK', - physId: 'RepositoryName', - data: { - RepositoryName: 'RepositoryName' - } - }); - - test.done(); - }, - - async 'exercise "create" handler when repository doesnt exist'(test: Test) { - const handler = proxyquire(path.resolve(__dirname, '..', 'lib', 'adopt-repository', 'handler'), { - 'aws-sdk': { '@noCallThru': true, "ECR": ECRWithRepositoryNotFound } - }); - - let output; - async function response(responseStatus: string, reason: string, physId: string, data: any) { - output = { responseStatus, reason, physId, data }; - } - - await handler.handler({ - StackId: 'StackId', - ResourceProperties: { - RepositoryName: 'RepositoryName', - }, - RequestType: 'Create', - ResponseURL: 'https://localhost/test' - }, { - logStreamName: 'xyz', - }, undefined, response); - - test.deepEqual(output, { - responseStatus: 'FAILED', - reason: 'Simulated RepositoryPolicyNotFoundException', - physId: 'xyz', - data: { } - }); - - test.done(); - }, -}; - -function ECRWithEmptyPolicy() { - ecrMock = new ECR({ asdf: 'asdf' }); - return ecrMock; -} - -function ECRWithOwningPolicy() { - return new ECR({ - Statement: [ - { - Sid: 'StackId', - Effect: "Deny", - Action: "OwnedBy:CDKStack", - Principal: "*" - } - ] - }); -} - -function ECRWithRepositoryNotFound() { - const ecr = new ECR({}); - ecr.shouldThrowNotFound = true; - return ecr; -} - -class ECR { - public lastSetRepositoryPolicyRequest: any; - public shouldThrowNotFound = false; - - public constructor(private policy: any) { - } - - public getRepositoryPolicy() { - const self = this; - return { - async promise() { - if (self.shouldThrowNotFound) { return self.throwNotFound(); } - return { policyText: JSON.stringify(self.policy) }; - } - }; - } - - public setRepositoryPolicy(req: any) { - const self = this; - this.lastSetRepositoryPolicyRequest = req; - - return { - async promise() { - if (self.shouldThrowNotFound) { return self.throwNotFound(); } - return; - } - }; - } - - public listImages() { - return { - async promise() { - return { imageIds: [] }; - } - }; - } - - public batchDeleteImage() { - const self = this; - return { - async promise() { - if (self.shouldThrowNotFound) { return self.throwNotFound(); } - return {}; - } - }; - } - - public deleteRepository() { - const self = this; - return { - async promise() { - if (self.shouldThrowNotFound) { return self.throwNotFound(); } - return {}; - } - }; - } - - private throwNotFound() { - const err = new Error('Simulated RepositoryPolicyNotFoundException'); - (err as any).code = 'RepositoryPolicyNotFoundException'; - throw err; - } -} diff --git a/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts index 5da0630ee6705..d704c3f00f5c7 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts @@ -1,6 +1,6 @@ -import { expect, haveResource, SynthUtils } from '@aws-cdk/assert'; +import { expect, haveResource } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; -import { App, Construct, Lazy, Resource, Stack } from '@aws-cdk/core'; +import { App, Lazy, Stack } from '@aws-cdk/core'; import { ASSET_METADATA } from '@aws-cdk/cx-api'; import * as fs from 'fs'; import { Test } from 'nodeunit'; @@ -12,7 +12,8 @@ import { DockerImageAsset } from '../lib'; export = { 'test instantiating Asset Image'(test: Test) { // GIVEN - const stack = new Stack(); + const app = new App(); + const stack = new Stack(app, 'test-stack'); // WHEN new DockerImageAsset(stack, 'Image', { @@ -20,33 +21,19 @@ export = { }); // THEN - const template = SynthUtils.synthesize(stack).template; - test.deepEqual(template.Parameters.AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3, { - Type: 'String', - Description: 'ECR repository name and tag for asset "1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c"' - }); - - test.done(); - }, - - 'repository name is derived from node unique id'(test: Test) { - // GIVEN - const stack = new Stack(); - class CoolConstruct extends Resource { - constructor(scope: Construct, id: string) { - super(scope, id); + const asm = app.synth(); + const artifact = asm.getStackArtifact(stack.artifactId); + test.deepEqual(artifact.template, {}, 'template is empty'); + test.deepEqual(artifact.assets, [ + { + repositoryName: 'aws-cdk/assets', + imageTag: 'baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540', + id: 'baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540', + packaging: 'container-image', + path: 'asset.baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540', + sourceHash: 'baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540' } - } - const coolConstruct = new CoolConstruct(stack, 'CoolConstruct'); - - // WHEN - new DockerImageAsset(coolConstruct, 'Image', { - directory: path.join(__dirname, 'demo-image'), - }); - - // THEN - const assetMetadata = stack.node.metadata.find(({ type }) => type === ASSET_METADATA); - test.deepEqual(assetMetadata && assetMetadata.data.repositoryName, 'cdk/coolconstructimage78ab38fc'); + ]); test.done(); }, @@ -100,7 +87,7 @@ export = { // THEN const assetMetadata = stack.node.metadata.find(({ type }) => type === ASSET_METADATA); - test.deepEqual(assetMetadata && assetMetadata.data.file, path.join(directoryPath, 'Dockerfile.Custom')); + test.deepEqual(assetMetadata && assetMetadata.data.file, 'Dockerfile.Custom'); test.done(); }, @@ -131,13 +118,18 @@ export = { "", [ "arn:", - { "Ref": "AWS::Partition" }, + { + "Ref": "AWS::Partition" + }, ":ecr:", - { "Ref": "AWS::Region" }, + { + "Ref": "AWS::Region" + }, ":", - { "Ref": "AWS::AccountId" }, - ":repository/", - { "Fn::GetAtt": ["ImageAdoptRepositoryE1E84E35", "RepositoryName"] } + { + "Ref": "AWS::AccountId" + }, + ":repository/aws-cdk/assets" ] ] } @@ -161,51 +153,6 @@ export = { test.done(); }, - 'asset.repository.addToResourcePolicy can be used to modify the ECR resource policy via the adoption custom resource'(test: Test) { - // GIVEN - const stack = new Stack(); - const asset = new DockerImageAsset(stack, 'Image', { - directory: path.join(__dirname, 'demo-image') - }); - - // WHEN - asset.repository.addToResourcePolicy(new iam.PolicyStatement({ - actions: ['BAM:BOOM'], - principals: [new iam.ServicePrincipal('test.service')] - })); - - // THEN - expect(stack).to(haveResource('Custom::ECRAdoptedRepository', { - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "BAM:BOOM", - "Effect": "Allow", - "Principal": { - "Service": "test.service" - } - } - ], - "Version": "2012-10-17" - } - })); - - test.done(); - }, - 'fails if the directory does not exist'(test: Test) { // GIVEN const stack = new Stack(); @@ -350,13 +297,13 @@ export = { const asset6 = new DockerImageAsset(stack, 'Asset6', { directory, extraHash: 'random-extra' }); const asset7 = new DockerImageAsset(stack, 'Asset7', { directory, repositoryName: 'foo' }); - test.deepEqual(asset1.sourceHash, 'b84a5001da0f5714e484134e2471213d7e987e22ee6219469029f1779370cc2a'); - test.deepEqual(asset2.sourceHash, 'c6568a7946e92a408c60278f70834b901638e71237d470ed1e5e6d707c55c0c9'); - test.deepEqual(asset3.sourceHash, '963a5329c170c54bc667fddab8d9cc4cec4bffb65ce3a1f323bb5fbc1d268732'); - test.deepEqual(asset4.sourceHash, '0e3eb87273509e0f0d45d67d40fa3080566aa22abd7f976e1ce7ea60a8ccd0a8'); - test.deepEqual(asset5.sourceHash, 'de0fd4b2bff8c9f180351fd59c6f2e9409fa21366453e1e0b75fedbd93dda1fc'); - test.deepEqual(asset6.sourceHash, '00879adf80f97271bf6d7e214b4fac8a043fc6e2661912cbf4d898ccb317d46c'); - test.deepEqual(asset7.sourceHash, 'b8abda995e51bd1a47b2705fa40021f3e9619a334bddb96866e808b09303eff7'); + test.deepEqual(asset1.sourceHash, '31959f03fcdf1bddec1420d315dddedfccf559e87e95c85854a01f2cac103fc8'); + test.deepEqual(asset2.sourceHash, 'a42cd51ab2bc5e2a4399c4bc41f7df761ff19877b54bce52d69c6e8d628f16fd'); + test.deepEqual(asset3.sourceHash, '9efbc91d5c2f43782e49b27e784caad32d8619a0cecf806a6e55cf70f1cfbc22'); + test.deepEqual(asset4.sourceHash, '2907224e59cb720ba5810a624f1c1267f547377e8a23c50d72286dd81dc8435e'); + test.deepEqual(asset5.sourceHash, 'd718928111c650564240141b156188ebdbb93d3c5f1448dd1afe823ae7f742a5'); + test.deepEqual(asset6.sourceHash, 'fe3ef82c91b6321ac17bc3a14c75845fa1ddbafe550e0a04d5cf680015903a2d'); + test.deepEqual(asset7.sourceHash, '80f586ed82faacec8a285dd99c7ee52e06525fee1721bc80bc846d1a8266fe36'); test.done(); } }; diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts index ed2e59cd94d0f..a397a8aa6624a 100644 --- a/packages/@aws-cdk/core/lib/assets.ts +++ b/packages/@aws-cdk/core/lib/assets.ts @@ -72,6 +72,7 @@ export interface DockerImageAssetSource { * registry and the tag parts. * * @default - automatically derived from the asset's ID. + * @deprecated repository name should be specified at the environment-level and not at the image level */ readonly repositoryName?: string; } diff --git a/packages/@aws-cdk/core/lib/private/asset-parameters.ts b/packages/@aws-cdk/core/lib/private/asset-parameters.ts index cc74067bccc37..727491de55159 100644 --- a/packages/@aws-cdk/core/lib/private/asset-parameters.ts +++ b/packages/@aws-cdk/core/lib/private/asset-parameters.ts @@ -29,16 +29,3 @@ export class FileAssetParameters extends Construct { }); } } - -export class DockerImageAssetParameters extends Construct { - public readonly imageNameParameter: CfnParameter; - - constructor(scope: Construct, id: string) { - super(scope, id); - - this.imageNameParameter = new CfnParameter(this, 'ImageName', { - type: 'String', - description: `ECR repository name and tag for asset "${id}"`, - }); - } -} diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 1683063020163..30a0119cbebd8 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -6,7 +6,7 @@ import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation , F import { Construct, ConstructNode, IConstruct, ISynthesisSession } from './construct'; import { ContextProvider } from './context-provider'; import { Environment } from './environment'; -import { DockerImageAssetParameters, FileAssetParameters } from './private/asset-parameters'; +import { FileAssetParameters } from './private/asset-parameters'; import { CLOUDFORMATION_TOKEN_RESOLVER, CloudFormationLang } from './private/cloudformation-lang'; import { LogicalIDs } from './private/logical-id'; import { findTokens , resolve } from './private/resolve'; @@ -17,6 +17,21 @@ const MY_STACK_CACHE = Symbol.for('@aws-cdk/core.Stack.myStack'); const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/; +/** + * The well-known name for the docker image asset ECR repository. All docker + * image assets will be pushed into this repository with an image tag based on + * the source hash. + */ +const ASSETS_ECR_REPOSITORY_NAME = "aws-cdk/assets"; + +/** + * This allows users to work around the fact that the ECR repository is + * (currently) not configurable by setting this context key to their desired + * repository name. The CLI will auto-create this ECR repository if it's not + * already created. + */ +const ASSETS_ECR_REPOSITORY_NAME_OVERRIDE_CONTEXT_KEY = "assets-ecr-repository-name"; + export interface StackProps { /** * A description of the stack. @@ -220,6 +235,12 @@ export class Stack extends Construct implements ITaggable { private _templateUrl?: string; private readonly _stackName: string; + /** + * The image ID of all the docker image assets that were already added to this + * stack (to avoid duplication). + */ + private readonly addedImageAssets = new Set(); + /** * Creates a new stack. * @@ -551,34 +572,33 @@ export class Stack extends Construct implements ITaggable { return this.nestedStackParent.addDockerImageAsset(asset); } - let params = this.assetParameters.node.tryFindChild(asset.sourceHash) as DockerImageAssetParameters; - if (!params) { - params = new DockerImageAssetParameters(this.assetParameters, asset.sourceHash); + // check if we have an override from context + const repositoryNameOverride = this.node.tryGetContext(ASSETS_ECR_REPOSITORY_NAME_OVERRIDE_CONTEXT_KEY); + const repositoryName = asset.repositoryName ?? repositoryNameOverride ?? ASSETS_ECR_REPOSITORY_NAME; + const imageTag = asset.sourceHash; + const assetId = asset.sourceHash; + // only add every image (identified by source hash) once for each stack that uses it. + if (!this.addedImageAssets.has(assetId)) { const metadata: cxapi.ContainerImageAssetMetadataEntry = { - id: asset.sourceHash, + repositoryName, + imageTag, + id: assetId, packaging: 'container-image', path: asset.directoryName, sourceHash: asset.sourceHash, - imageNameParameter: params.imageNameParameter.logicalId, - repositoryName: asset.repositoryName, buildArgs: asset.dockerBuildArgs, target: asset.dockerBuildTarget, file: asset.dockerFile, }; this.node.addMetadata(cxapi.ASSET_METADATA, metadata); + this.addedImageAssets.add(assetId); } - // Parse repository name and tag from the parameter (@sha256:) - // Example: cdk/cdkexampleimageb2d7f504@sha256:72c4f956379a43b5623d529ddd969f6826dde944d6221f445ff3e7add9875500 - const components = Fn.split('@sha256:', params.imageNameParameter.valueAsString); - const repositoryName = Fn.select(0, components).toString(); - const imageSha = Fn.select(1, components).toString(); - const imageUri = `${this.account}.dkr.ecr.${this.region}.${this.urlSuffix}/${repositoryName}@sha256:${imageSha}`; - return { - imageUri, repositoryName + imageUri: `${this.account}.dkr.ecr.${this.region}.${this.urlSuffix}/${repositoryName}:${imageTag}`, + repositoryName }; } diff --git a/packages/@aws-cdk/core/test/test.assets.ts b/packages/@aws-cdk/core/test/test.assets.ts index 4ad3d906147ec..f29f264a4837d 100644 --- a/packages/@aws-cdk/core/test/test.assets.ts +++ b/packages/@aws-cdk/core/test/test.assets.ts @@ -44,7 +44,7 @@ export = { }, - 'addDockerImageAsset correctly sets metadata and creates an ECR parameter'(test: Test) { + 'addDockerImageAsset correctly sets metadata'(test: Test) { // GIVEN const stack = new Stack(); @@ -62,16 +62,56 @@ export = { test.equal(assetMetadata && assetMetadata.data.path, 'directory-name'); test.equal(assetMetadata && assetMetadata.data.sourceHash, 'source-hash'); test.equal(assetMetadata && assetMetadata.data.repositoryName, 'repository-name'); + test.equal(assetMetadata && assetMetadata.data.imageTag, 'source-hash'); - test.deepEqual(toCloudFormation(stack), { - Parameters: { - AssetParameterssourcehashImageName3B572B12: { - Type: 'String', - Description: 'ECR repository name and tag for asset "source-hash"' - } - } + test.deepEqual(toCloudFormation(stack), { }); + test.done(); + }, + + 'addDockerImageAsset uses the default repository name'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + stack.addDockerImageAsset({ + sourceHash: 'source-hash', + directoryName: 'directory-name', }); + // THEN + const assetMetadata = stack.node.metadata.find(({ type }) => type === cxapi.ASSET_METADATA); + + test.equal(assetMetadata && assetMetadata.data.packaging, 'container-image'); + test.equal(assetMetadata && assetMetadata.data.path, 'directory-name'); + test.equal(assetMetadata && assetMetadata.data.sourceHash, 'source-hash'); + test.equal(assetMetadata && assetMetadata.data.repositoryName, 'aws-cdk/assets'); + test.equal(assetMetadata && assetMetadata.data.imageTag, 'source-hash'); + + test.deepEqual(toCloudFormation(stack), { }); + test.done(); + }, + + 'addDockerImageAsset supports overriding repository name through a context key as a workaround until we have API for that'(test: Test) { + // GIVEN + const stack = new Stack(); + stack.node.setContext('assets-ecr-repository-name', 'my-custom-repo-name'); + + // WHEN + stack.addDockerImageAsset({ + sourceHash: 'source-hash', + directoryName: 'directory-name', + }); + + // THEN + const assetMetadata = stack.node.metadata.find(({ type }) => type === cxapi.ASSET_METADATA); + + test.equal(assetMetadata && assetMetadata.data.packaging, 'container-image'); + test.equal(assetMetadata && assetMetadata.data.path, 'directory-name'); + test.equal(assetMetadata && assetMetadata.data.sourceHash, 'source-hash'); + test.equal(assetMetadata && assetMetadata.data.repositoryName, 'my-custom-repo-name'); + test.equal(assetMetadata && assetMetadata.data.imageTag, 'source-hash'); + + test.deepEqual(toCloudFormation(stack), { }); test.done(); }, }; diff --git a/packages/@aws-cdk/cx-api/lib/assets.ts b/packages/@aws-cdk/cx-api/lib/assets.ts index b19730973daf7..9e99f95eed9c5 100644 --- a/packages/@aws-cdk/cx-api/lib/assets.ts +++ b/packages/@aws-cdk/cx-api/lib/assets.ts @@ -78,21 +78,36 @@ export interface ContainerImageAssetMetadataEntry extends BaseAssetMetadataEntry readonly packaging: 'container-image'; /** - * ECR Repository name and repo digest (separated by "@sha256:") where this image is stored. + * ECR Repository name and repo digest (separated by "@sha256:") where this + * image is stored. + * + * @default undefined If not specified, `repositoryName` and `imageTag` are + * required because otherwise how will the stack know where to find the asset, + * ha? + * @deprecated specify `repositoryName` and `imageTag` instead, and then you + * know where the image will go. */ - readonly imageNameParameter: string; + readonly imageNameParameter?: string; /** - * ECR repository name, if omitted a default name based on the asset's - * ID is used instead. Specify this property if you need to statically - * address the image, e.g. from a Kubernetes Pod. - * Note, this is only the repository name, without the registry and - * the tag parts. + * ECR repository name, if omitted a default name based on the asset's ID is + * used instead. Specify this property if you need to statically address the + * image, e.g. from a Kubernetes Pod. Note, this is only the repository name, + * without the registry and the tag parts. * - * @default automatically derived from the asset's ID. + * @default - this parameter is REQUIRED after 1.21.0 */ readonly repositoryName?: string; + /** + * The docker image tag to use for tagging pushed images. This field is + * required if `imageParameterName` is ommited (otherwise, the app won't be + * able to find the image). + * + * @default - this parameter is REQUIRED after 1.21.0 + */ + readonly imageTag?: string; + /** * Build args to pass to the `docker build` command * diff --git a/packages/@aws-cdk/cx-api/lib/versioning.ts b/packages/@aws-cdk/cx-api/lib/versioning.ts index e5ab25656305e..a35747216548d 100644 --- a/packages/@aws-cdk/cx-api/lib/versioning.ts +++ b/packages/@aws-cdk/cx-api/lib/versioning.ts @@ -31,7 +31,7 @@ import { AssemblyManifest } from './cloud-assembly'; * Note that the versions are not compared in a semver way, they are used as * opaque ordered tokens. */ -export const CLOUD_ASSEMBLY_VERSION = '1.16.0'; +export const CLOUD_ASSEMBLY_VERSION = '1.21.0'; /** * Look at the type of response we get and upgrade it to the latest expected version @@ -71,6 +71,13 @@ export function upgradeAssemblyManifest(manifest: AssemblyManifest): AssemblyMan manifest = justUpgradeVersion(manifest, '1.16.0'); } + if (manifest.version === '1.16.0') { + // Backwards compatible changes to ContainerImageAssetMetadataEntry: + // * Make `imageNameParameter` optional (new apps do not require it anymore because container images go to a well-known repository) + // * Add optional `imageTag` to allow apps to specify exactly where to store the image (required if `imageNameParameter` is not defined) + manifest = justUpgradeVersion(manifest, '1.21.0'); + } + return manifest; } diff --git a/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap b/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap index 3ae076caa47ba..3889ac3a1825e 100644 --- a/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap +++ b/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap @@ -48,7 +48,7 @@ Array [ exports[`empty assembly 1`] = ` Object { - "version": "1.16.0", + "version": "1.21.0", } `; diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index e96084d5c2c3b..1a0b5204b88c4 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -61,7 +61,7 @@ async function parseCommandLineArguments() { .option('build-exclude', { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times.', default: [] }) .option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' }) .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' }) - .option('ci', { type: 'boolean', desc: 'Force CI detection. Use --no-ci to disable CI autodetection.', default: process.env.CI !== undefined }) + .option('ci', { type: 'boolean', desc: 'Force CI detection (deprecated)', default: process.env.CI !== undefined }) .option('notification-arns', {type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true}) .option('tags', { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE)', nargs: 1, requiresArg: true }) .option('execute', {type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true}) @@ -216,7 +216,6 @@ async function initCommandLine() { roleArn: args.roleArn, notificationArns: args.notificationArns, requireApproval: configuration.settings.get(['requireApproval']), - ci: args.ci, reuseAssets: args['build-exclude'], tags: configuration.settings.get(['tags']), sdk: aws, diff --git a/packages/aws-cdk/lib/api/deploy-stack.ts b/packages/aws-cdk/lib/api/deploy-stack.ts index a25e1e51e9214..a9fcd8cff4763 100644 --- a/packages/aws-cdk/lib/api/deploy-stack.ts +++ b/packages/aws-cdk/lib/api/deploy-stack.ts @@ -34,7 +34,6 @@ export interface DeployStackOptions { notificationArns?: string[]; deployName?: string; quiet?: boolean; - ci?: boolean; reuseAssets?: string[]; tags?: Tag[]; @@ -53,7 +52,7 @@ export async function deployStack(options: DeployStackOptions): Promise { + public async prepareEcrRepository(repositoryName: string): Promise { const ecr = await this.props.sdk.ecr(this.props.environment.account, this.props.environment.region, Mode.ForWriting); - let repositoryName; - if (asset.repositoryName) { - // Repository name provided by user - repositoryName = asset.repositoryName; - } else { - // Repository name based on asset id - const assetId = asset.id; - repositoryName = 'cdk/' + assetId.replace(/[:/]/g, '-').toLowerCase(); - } - let repository; + // check if repo already exists try { - debug(`${repositoryName}: checking for repository.`); + debug(`${repositoryName}: checking if ECR repository already exists`); const describeResponse = await ecr.describeRepositories({ repositoryNames: [repositoryName] }).promise(); - repository = describeResponse.repositories![0]; + const existingRepositoryUri = describeResponse.repositories![0]?.repositoryUri; + if (existingRepositoryUri) { + return { repositoryUri: existingRepositoryUri }; + } } catch (e) { if (e.code !== 'RepositoryNotFoundException') { throw e; } } - if (repository) { - return { - repositoryUri: repository.repositoryUri!, - repositoryName - }; + // create the repo (tag it so it will be easier to garbage collect in the future) + debug(`${repositoryName}: creating ECR repository`); + const assetTag = { Key: 'awscdk:asset', Value: 'true' }; + const response = await ecr.createRepository({ repositoryName, tags: [ assetTag ] }).promise(); + const repositoryUri = response.repository?.repositoryUri; + if (!repositoryUri) { + throw new Error(`CreateRepository did not return a repository URI for ${repositoryUri}`); } - debug(`${repositoryName}: creating`); - const response = await ecr.createRepository({ repositoryName }).promise(); - repository = response.repository!; - - // Better put a lifecycle policy on this so as to not cost too much money - await ecr.putLifecyclePolicy({ - repositoryName, - lifecyclePolicyText: JSON.stringify(DEFAULT_REPO_LIFECYCLE) - }).promise(); - - // Configure image scanning on push (helps in identifying software vulnerabilities, no additional charge) - await ecr.putImageScanningConfiguration({ - repositoryName, - imageScanningConfiguration: { - scanOnPush: true - } - }).promise(); + // configure image scanning on push (helps in identifying software vulnerabilities, no additional charge) + debug(`${repositoryName}: enable image scanning`); + await ecr.putImageScanningConfiguration({ repositoryName, imageScanningConfiguration: { scanOnPush: true } }).promise(); - return { - repositoryUri: repository.repositoryUri!, - repositoryName - }; + return { repositoryUri }; } /** @@ -201,7 +180,6 @@ export class ToolkitInfo { /** @experimental */ export interface EcrRepositoryInfo { repositoryUri: string; - repositoryName: string; } /** @experimental */ @@ -251,18 +229,3 @@ function getOutputValue(stack: aws.CloudFormation.Stack, output: string): string } return result; } - -export const DEFAULT_REPO_LIFECYCLE = { - rules: [ - { - rulePriority: 100, - description: 'Retain only 5 images', - selection: { - tagStatus: 'any', - countType: 'imageCountMoreThan', - countNumber: 5, - }, - action: { type: 'expire' } - } - ] -}; diff --git a/packages/aws-cdk/lib/assets.ts b/packages/aws-cdk/lib/assets.ts index 2d35c2acb99da..30cc111866460 100644 --- a/packages/aws-cdk/lib/assets.ts +++ b/packages/aws-cdk/lib/assets.ts @@ -11,7 +11,7 @@ import { prepareContainerAsset } from './docker'; import { debug, success } from './logging'; // tslint:disable-next-line:max-line-length -export async function prepareAssets(stack: cxapi.CloudFormationStackArtifact, toolkitInfo?: ToolkitInfo, ci?: boolean, reuse?: string[]): Promise { +export async function prepareAssets(stack: cxapi.CloudFormationStackArtifact, toolkitInfo?: ToolkitInfo, reuse?: string[]): Promise { reuse = reuse || []; const assets = stack.assets; @@ -41,21 +41,21 @@ export async function prepareAssets(stack: cxapi.CloudFormationStackArtifact, to } const assemblyDir = stack.assembly.directory; - params = params.concat(await prepareAsset(assemblyDir, asset, toolkitInfo, reuseAsset, ci)); + params = params.concat(await prepareAsset(assemblyDir, asset, toolkitInfo, reuseAsset)); } return params; } // tslint:disable-next-line:max-line-length -async function prepareAsset(assemblyDir: string, asset: cxapi.AssetMetadataEntry, toolkitInfo: ToolkitInfo, reuse: boolean, ci?: boolean): Promise { +async function prepareAsset(assemblyDir: string, asset: cxapi.AssetMetadataEntry, toolkitInfo: ToolkitInfo, reuse: boolean): Promise { switch (asset.packaging) { case 'zip': return await prepareZipAsset(assemblyDir, asset, toolkitInfo, reuse); case 'file': return await prepareFileAsset(assemblyDir, asset, toolkitInfo, reuse); case 'container-image': - return await prepareContainerAsset(assemblyDir, asset, toolkitInfo, reuse, ci); + return await prepareContainerAsset(assemblyDir, asset, toolkitInfo, reuse); default: // tslint:disable-next-line:max-line-length throw new Error(`Unsupported packaging type: ${(asset as any).packaging}. You might need to upgrade your aws-cdk toolkit to support this asset type.`); diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 690a7e45ba82a..f9a3717b30071 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -138,7 +138,6 @@ export class CdkToolkit { stack, deployName: stack.stackName, roleArn: options.roleArn, - ci: options.ci, toolkitStackName: options.toolkitStackName, reuseAssets: options.reuseAssets, notificationArns: options.notificationArns, @@ -288,13 +287,6 @@ export interface DeployOptions { */ requireApproval?: RequireApproval; - /** - * Whether we're in CI mode - * - * @default false - */ - ci?: boolean; - /** * Reuse the assets with the given asset IDs */ diff --git a/packages/aws-cdk/lib/docker.ts b/packages/aws-cdk/lib/docker.ts index 5d229460642b3..d8efbd707f7fd 100644 --- a/packages/aws-cdk/lib/docker.ts +++ b/packages/aws-cdk/lib/docker.ts @@ -31,7 +31,14 @@ export async function prepareContainerAsset(assemblyDir: string, asset: ContainerImageAssetMetadataEntry, toolkitInfo: ToolkitInfo, reuse: boolean, - ci?: boolean): Promise<[CloudFormation.Parameter]> { + ci?: boolean): Promise { + + // following 1.21.0, image asset location (repositoryName and imageTag) is fully determined by the + // app, and therefore there is no need to wire the image name through a cloudformation parameter. + if (!asset.imageNameParameter) { + await prepareContainerAssetNew(assemblyDir, asset, toolkitInfo); + return [ ]; + } if (reuse) { return [ @@ -44,8 +51,10 @@ export async function prepareContainerAsset(assemblyDir: string, debug(' 👑 Preparing Docker image asset:', contextPath); try { - const ecr = await toolkitInfo.prepareEcrRepository(asset); - const latest = `${ecr.repositoryUri}:latest`; + const repositoryName = asset.repositoryName ?? 'cdk/' + asset.id.replace(/[:/]/g, '-').toLowerCase(); + const ecr = await toolkitInfo.prepareEcrRepository(repositoryName); + const imageTag = asset.imageTag ?? 'latest'; + const latest = `${ecr.repositoryUri}:${imageTag}`; let loggedIn = false; @@ -103,7 +112,7 @@ export async function prepareContainerAsset(assemblyDir: string, } return [ - { ParameterKey: asset.imageNameParameter, ParameterValue: repoDigest.replace(ecr.repositoryUri, ecr.repositoryName) }, + { ParameterKey: asset.imageNameParameter, ParameterValue: repoDigest.replace(ecr.repositoryUri, repositoryName) }, ]; } catch (e) { if (e.code === 'ENOENT') { @@ -114,6 +123,71 @@ export async function prepareContainerAsset(assemblyDir: string, } } +/** + * Build and upload a Docker image + */ +export async function prepareContainerAssetNew(assemblyDir: string, + asset: ContainerImageAssetMetadataEntry, + toolkitInfo: ToolkitInfo) { + + if (asset.imageNameParameter || !asset.repositoryName || !asset.imageTag) { + throw new Error(`invalid docker image asset configuration. "repositoryName" and "imageTag" are required and "imageNameParameter" is not allowed`); + } + + const contextPath = path.isAbsolute(asset.path) ? asset.path : path.join(assemblyDir, asset.path); + + debug(' 👑 Preparing Docker image asset:', contextPath); + const ecr = await toolkitInfo.prepareEcrRepository(asset.repositoryName); + + // if both repo name and image tag are explicitly defined, we assume the + // image is immutable and can skip build & push. + debug(`${asset.repositoryName}:${asset.imageTag}: checking if image already exists`); + if (await toolkitInfo.checkEcrImage(asset.repositoryName, asset.imageTag)) { + print(`${asset.repositoryName}:${asset.imageTag}: image already exists, skipping build and push`); + return; + } + + // we use "latest" for image tag for backwards compatibility with pre-1.21.0 apps. + const fullImageName = `${ecr.repositoryUri}:${asset.imageTag}`; + + // render "docker build" command + + const buildCommand = [ 'docker', 'build' ]; + + buildCommand.push('--tag', fullImageName); + + if (asset.target) { + buildCommand.push('--target', asset.target); + } + + if (asset.file) { + buildCommand.push('--file', asset.file); + } + + for (const [ key, value ] of Object.entries(asset.buildArgs || {})) { + buildCommand.push(`--build-arg`, `${key}=${value}`); + } + + buildCommand.push(contextPath); + + try { + await shell(buildCommand); + } catch (e) { + if (e.code === 'ENOENT') { + throw new Error('Unable to execute "docker" in order to build a container asset. Please install "docker" and try again.'); + } + throw e; + } + + // login to ECR + await dockerLogin(toolkitInfo); + + // There's no way to make this quiet, so we can't use a PleaseHold. Print a header message. + print(` ⌛ Pushing Docker image for ${contextPath}; this may take a while.`); + await shell(['docker', 'push', fullImageName]); + debug(` 👑 Docker image for ${contextPath} pushed.`); +} + /** * Get credentials from ECR and run docker login */ @@ -123,4 +197,4 @@ async function dockerLogin(toolkitInfo: ToolkitInfo) { '--username', credentials.username, '--password', credentials.password, credentials.endpoint]); -} +} \ No newline at end of file diff --git a/packages/aws-cdk/test/assets.test.ts b/packages/aws-cdk/test/assets.test.ts index 563811e135524..f1fb2a4974ebb 100644 --- a/packages/aws-cdk/test/assets.test.ts +++ b/packages/aws-cdk/test/assets.test.ts @@ -67,7 +67,7 @@ test('prepare assets with reuse', async () => { const toolkit = new FakeToolkit(); // WHEN - const params = await prepareAssets(stack, toolkit as any, undefined, ['SomeStackSomeResource4567']); + const params = await prepareAssets(stack, toolkit as any, ['SomeStackSomeResource4567']); // THEN expect(params).toEqual([ @@ -101,7 +101,7 @@ test('prepare container asset with reuse', async () => { const toolkit = new FakeToolkit(); // WHEN - const params = await prepareAssets(stack, toolkit as any, undefined, ['SomeStackSomeResource4567']); + const params = await prepareAssets(stack, toolkit as any, ['SomeStackSomeResource4567']); // THEN expect(params).toEqual([ diff --git a/packages/aws-cdk/test/docker-new.test.ts b/packages/aws-cdk/test/docker-new.test.ts new file mode 100644 index 0000000000000..b8469e512c71c --- /dev/null +++ b/packages/aws-cdk/test/docker-new.test.ts @@ -0,0 +1,304 @@ +import * as cxapi from '@aws-cdk/cx-api'; +import * as sinon from 'sinon'; +import { ToolkitInfo } from '../lib'; +import { prepareContainerAsset } from '../lib/docker'; +import * as os from '../lib/os'; +import { MockSDK } from './util/mock-sdk'; + +test('fails if "repositoryName" and "imageTag" are not specified', async () => { + // GIVEN + const toolkit = newMockToolkitInfo(); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: '/foo', + sourceHash: '0123456789abcdef', + }; + + // THEN + await expect(prepareContainerAsset('.', asset, toolkit, false)) + .rejects.toEqual(new Error(`invalid docker image asset configuration\. "repositoryName" and "imageTag" are required and "imageNameParameter" is not allowed`)); +}); + +test('fails if "repositoryName" is not specified', async () => { + // GIVEN + const toolkit = newMockToolkitInfo(); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + repositoryName: 'repository-name', + packaging: 'container-image', + path: '/foo', + sourceHash: '0123456789abcdef', + }; + + // THEN + await expect(prepareContainerAsset('.', asset, toolkit, false)) + .rejects.toEqual(new Error(`invalid docker image asset configuration\. "repositoryName" and "imageTag" are required and "imageNameParameter" is not allowed`)); +}); + +test('creates repository with given name', async () => { + // GIVEN + const sdk = new MockSDK(); + const toolkit = newMockToolkitInfo(sdk); + let createdName; + + sdk.stubEcr({ + describeRepositories: () => ({ repositories: [] }), + createRepository: req => { + createdName = req.repositoryName; + + // Stop the test so that we don't actually docker build + throw new Error('STOPTEST'); + }, + }); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: '/foo', + repositoryName: 'some-name', + imageTag: 'image-tag', + sourceHash: '0123456789abcdef', + }; + + try { + await prepareContainerAsset('.', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + // THEN + expect(createdName).toBe('some-name'); +}); + +test('configures image scanning', async () => { + // GIVEN + let putImageScanningConfigurationParams; + + const sdk = new MockSDK(); + sdk.stubEcr({ + describeRepositories: () => ({ repositories: [] }), + createRepository: () => ({ repository: { repositoryUri: 'uri' } }), + putImageScanningConfiguration: params => { + putImageScanningConfigurationParams = params; + + // Stop the test so that we don't actually docker build + throw new Error('STOPTEST'); + } + }); + + const toolkit = newMockToolkitInfo(sdk); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: '/foo', + repositoryName: 'some-name', + imageTag: 'some-tag', + sourceHash: '0123456789abcdef', + }; + + try { + await prepareContainerAsset('.', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + expect(putImageScanningConfigurationParams).toEqual({ + repositoryName: 'some-name', + imageScanningConfiguration: { + scanOnPush: true + } + }); +}); + +test('passes the correct target to docker build', async () => { + // GIVEN + const toolkit = newMockToolkitInfo(); + + const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri' }); + const checkEcrImageStub = sinon.stub(toolkit, 'checkEcrImage').resolves(false); + const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: '/foo', + sourceHash: '1234567890abcdef', + repositoryName: 'some-name', + imageTag: 'some-tag', + buildArgs: { + a: 'b', + c: 'd' + }, + target: 'a-target', + }; + + try { + await prepareContainerAsset('.', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + // THEN + const command = [ 'docker', 'build', '--tag', `uri:some-tag`, '--target', 'a-target', '--build-arg', 'a=b', '--build-arg', 'c=d', '/foo' ]; + sinon.assert.calledWith(shellStub, command); + + prepareEcrRepositoryStub.restore(); + shellStub.restore(); + checkEcrImageStub.restore(); +}); + +test('passes the correct args to docker build', async () => { + // GIVEN + const toolkit = newMockToolkitInfo(); + + const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri' }); + const checkEcrImageStub = sinon.stub(toolkit, 'checkEcrImage').resolves(false); + const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: '/foo', + sourceHash: '1234567890abcdef', + repositoryName: 'some-name', + imageTag: 'some-tag', + buildArgs: { + a: 'b', + c: 'd' + } + }; + + try { + await prepareContainerAsset('.', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + // THEN + const command = ['docker', 'build', '--tag', `uri:some-tag`, '--build-arg', 'a=b', '--build-arg', 'c=d', '/foo']; + sinon.assert.calledWith(shellStub, command); + + prepareEcrRepositoryStub.restore(); + checkEcrImageStub.restore(); + shellStub.restore(); +}); + +test('passes the correct docker file name if specified', async () => { + const toolkit = newMockToolkitInfo(); + const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri' }); + const checkEcrImageStub = sinon.stub(toolkit, 'checkEcrImage').resolves(false); + const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: '/foo', + sourceHash: '1234567890abcdef', + repositoryName: 'some-name', + imageTag: 'some-tag', + file: 'CustomDockerfile', + buildArgs: { + a: 'b', + c: 'd' + } + }; + + try { + await prepareContainerAsset('.', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + // THEN + const command = ['docker', 'build', '--tag', `uri:some-tag`, '--file', 'CustomDockerfile', '--build-arg', 'a=b', '--build-arg', 'c=d', '/foo']; + sinon.assert.calledWith(shellStub, command); + + prepareEcrRepositoryStub.restore(); + checkEcrImageStub.restore(); + shellStub.restore(); +}); + +test('relative path', async () => { + // GIVEN + const toolkit = newMockToolkitInfo(); + const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri' }); + const checkEcrImageStub = sinon.stub(toolkit, 'checkEcrImage').resolves(false); + const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: 'relative-to-assembly', + sourceHash: '1234567890abcdef', + repositoryName: 'some-name', + imageTag: 'some-tag', + buildArgs: { + a: 'b', + c: 'd' + } + }; + + try { + await prepareContainerAsset('/assembly/dir/root', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + // THEN + const command = ['docker', 'build', '--tag', `uri:some-tag`, '--build-arg', 'a=b', '--build-arg', 'c=d', '/assembly/dir/root/relative-to-assembly']; + sinon.assert.calledWith(shellStub, command); + + prepareEcrRepositoryStub.restore(); + checkEcrImageStub.restore(); + shellStub.restore(); +}); + +test('skips build & push if image already exists in the ECR repo', async () => { + // GIVEN + const toolkit = newMockToolkitInfo(); + const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository'); + const shellStub = sinon.stub(os, 'shell'); + const checkEcrImageStub = sinon.stub(toolkit, 'checkEcrImage').resolves(true); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + packaging: 'container-image', + path: 'relative-to-assembly', + sourceHash: '1234567890abcdef', + repositoryName: 'some-name', + imageTag: 'some-tag', + buildArgs: { + a: 'b', + c: 'd' + } + }; + + await prepareContainerAsset('/assembly/dir/root', asset, toolkit, false); + + // THEN + sinon.assert.calledOnce(prepareEcrRepositoryStub); + sinon.assert.calledOnce(checkEcrImageStub); + sinon.assert.notCalled(shellStub); +}); + +function newMockToolkitInfo(sdk: MockSDK = new MockSDK()) { + return new ToolkitInfo({ + sdk, + bucketName: 'BUCKET_NAME', + bucketEndpoint: 'BUCKET_ENDPOINT', + environment: { name: 'env', account: '1234', region: 'abc' } + }); +} \ No newline at end of file diff --git a/packages/aws-cdk/test/docker.test.ts b/packages/aws-cdk/test/docker.test.ts index 0d903b62d80d7..bf8bdcf79bd4b 100644 --- a/packages/aws-cdk/test/docker.test.ts +++ b/packages/aws-cdk/test/docker.test.ts @@ -1,6 +1,6 @@ import * as cxapi from '@aws-cdk/cx-api'; import * as sinon from 'sinon'; -import { DEFAULT_REPO_LIFECYCLE, ToolkitInfo } from '../lib'; +import { ToolkitInfo } from '../lib'; import { prepareContainerAsset } from '../lib/docker'; import * as os from '../lib/os'; import { MockSDK } from './util/mock-sdk'; @@ -96,9 +96,8 @@ test('derives repository name from asset id', async () => { expect(createdName).toBe('cdk/stack-construct-abc123'); }); -test('configures lifecycle policy and image scanning', async () => { +test('configures image scanning', async () => { // GIVEN - let putLifecyclePolicyParams; let putImageScanningConfigurationParams; const sdk = new MockSDK(); @@ -115,11 +114,6 @@ test('configures lifecycle policy and image scanning', async () => { }; }, - putLifecyclePolicy(params) { - putLifecyclePolicyParams = params; - return {}; - }, - putImageScanningConfiguration(params) { putImageScanningConfigurationParams = params; @@ -151,12 +145,6 @@ test('configures lifecycle policy and image scanning', async () => { if (!/STOPTEST/.test(e.toString())) { throw e; } } - // THEN - expect(putLifecyclePolicyParams).toEqual({ - repositoryName: 'some-name', - lifecyclePolicyText: JSON.stringify(DEFAULT_REPO_LIFECYCLE) - }); - expect(putImageScanningConfigurationParams).toEqual({ repositoryName: 'some-name', imageScanningConfiguration: { @@ -176,7 +164,6 @@ test('passes the correct target to docker build', async () => { const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri', - repositoryName: 'name' }); const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); @@ -197,7 +184,7 @@ test('passes the correct target to docker build', async () => { }; try { - await prepareContainerAsset('.', asset, toolkit, false, false); + await prepareContainerAsset('.', asset, toolkit, false); } catch (e) { if (!/STOPTEST/.test(e.toString())) { throw e; } } @@ -221,7 +208,6 @@ test('passes the correct args to docker build', async () => { const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri', - repositoryName: 'name' }); const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); @@ -265,7 +251,6 @@ test('relative path', async () => { const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ repositoryUri: 'uri', - repositoryName: 'name' }); const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); @@ -297,3 +282,52 @@ test('relative path', async () => { prepareEcrRepositoryStub.restore(); shellStub.restore(); }); + +// since "imageNameParameter" is present, this means we are pre 1.21.0, which +// implies which is before "imageTag" was supported. still, for the sake of +// correctness of the protocol we added support for specifying image tag even if +// it's probably not going to be used. +test('"imageTag" is used instead of "latest"', async () => { + // GIVEN + const toolkit = new ToolkitInfo({ + sdk: new MockSDK(), + bucketName: 'BUCKET_NAME', + bucketEndpoint: 'BUCKET_ENDPOINT', + environment: { name: 'env', account: '1234', region: 'abc' } + }); + + const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({ + repositoryUri: 'uri', + }); + + const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST'); + + // WHEN + const asset: cxapi.ContainerImageAssetMetadataEntry = { + id: 'assetId', + imageNameParameter: 'MyParameter', + packaging: 'container-image', + path: '/foo', + sourceHash: '1234567890abcdef', + repositoryName: 'some-name', + imageTag: 'image-tag', + buildArgs: { + a: 'b', + c: 'd' + }, + target: 'a-target', + }; + + try { + await prepareContainerAsset('.', asset, toolkit, false); + } catch (e) { + if (!/STOPTEST/.test(e.toString())) { throw e; } + } + + // THEN + const command = ['docker', 'build', '--build-arg', 'a=b', '--build-arg', 'c=d', '--tag', `uri:image-tag`, '/foo', '--target', 'a-target']; + sinon.assert.calledWith(shellStub, command); + + prepareEcrRepositoryStub.restore(); + shellStub.restore(); +}); \ No newline at end of file From 3654d5df13fbcad645e6909c4dfe049a14434a1c Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Wed, 15 Jan 2020 23:59:54 +0200 Subject: [PATCH 2/2] update test expectations --- .../test/test.nested-stack.ts | 33 ++- .../test/integ.docker-asset.lit.expected.json | 233 +----------------- .../fargate/integ.asset-image.expected.json | 231 +---------------- ...g.scheduled-fargate-task.lit.expected.json | 231 +---------------- .../test/ec2/test.ec2-task-definition.ts | 29 +-- .../integ.event-ec2-task.lit.expected.json | 231 +---------------- .../integ.event-fargate-task.expected.json | 231 +---------------- .../test/integ.ec2-task.expected.json | 223 +---------------- .../test/integ.fargate-task.expected.json | 225 +---------------- packages/aws-cdk/test/docker.test.ts | 8 +- packages/decdk/test/fixture/tsconfig.json | 4 +- 11 files changed, 45 insertions(+), 1634 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts index f2748ad57d607..91a1a00e575dc 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts @@ -788,19 +788,28 @@ export = { }); // THEN - const parentParams = SynthUtils.toCloudFormation(parent).Parameters; - const nestedParams = SynthUtils.toCloudFormation(nested).Parameters; - test.ok(parentParams.AssetParametershashofsourceImageName1CFB7817); - test.ok(nestedParams.referencetomystackAssetParametershashofsourceImageName7D5F0882Ref); - - // verify parameter is passed to nested stack - expect(parent).to(haveResource('AWS::CloudFormation::Stack', { - Parameters: { - referencetomystackAssetParametershashofsourceImageName7D5F0882Ref: { - Ref: "AssetParametershashofsourceImageName1CFB7817" - } + const asm = app.synth(); + test.deepEqual(asm.getStackArtifact(parent.artifactId).assets, [ + { + repositoryName: 'aws-cdk/assets', + imageTag: 'hash-of-source', + id: 'hash-of-source', + packaging: 'container-image', + path: 'my-image', + sourceHash: 'hash-of-source', + buildArgs: { key: 'value', boom: 'bam' }, + target: 'buildTarget' + }, + { + path: 'mystacknestedstackFAE12FB5.nested.template.json', + id: 'fcdaee79eb79f37eca3a9b1cc0cc9ba150e4eea8c5d6d0c343cb6cd9dc68e2e5', + packaging: 'file', + sourceHash: 'fcdaee79eb79f37eca3a9b1cc0cc9ba150e4eea8c5d6d0c343cb6cd9dc68e2e5', + s3BucketParameter: 'AssetParametersfcdaee79eb79f37eca3a9b1cc0cc9ba150e4eea8c5d6d0c343cb6cd9dc68e2e5S3Bucket67A749F8', + s3KeyParameter: 'AssetParametersfcdaee79eb79f37eca3a9b1cc0cc9ba150e4eea8c5d6d0c343cb6cd9dc68e2e5S3VersionKeyE1E6A8D4', + artifactHashParameter: 'AssetParametersfcdaee79eb79f37eca3a9b1cc0cc9ba150e4eea8c5d6d0c343cb6cd9dc68e2e5ArtifactHash0AEDBE8A' } - })); + ]); test.done(); }, diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json index 6f0652023ea13..561aeb8f22f9a 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json @@ -1,181 +1,5 @@ { "Resources": { - "MyImageAdoptRepository6CA902F6": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - }, "MyProjectRole9BBE5233": { "Type": "AWS::IAM::Role", "Properties": { @@ -221,13 +45,7 @@ { "Ref": "AWS::AccountId" }, - ":repository/", - { - "Fn::GetAtt": [ - "MyImageAdoptRepository6CA902F6", - "RepositoryName" - ] - } + ":repository/aws-cdk/assets" ] ] } @@ -328,34 +146,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" - } - ] - } - ] - } + "/aws-cdk/assets:ee9ebbb592f461ed4638ea9ea64fab9fd384fd2f890c4fef981f9938d82419f4" ] ] }, @@ -375,23 +166,5 @@ } } } - }, - "Parameters": { - "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2b\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json index 7cf939ff66ebb..63e74d7cadb80 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json @@ -491,34 +491,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } + "/aws-cdk/assets:baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540" ] ] }, @@ -564,36 +537,6 @@ } } }, - "FargateServiceTaskDefwebAssetImageAdoptRepositoryCDAFD419": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "FargateServiceTaskDefwebLogGroup71FAF541": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", @@ -644,13 +587,7 @@ { "Ref": "AWS::AccountId" }, - ":repository/", - { - "Fn::GetAtt": [ - "FargateServiceTaskDefwebAssetImageAdoptRepositoryCDAFD419", - "RepositoryName" - ] - } + ":repository/aws-cdk/assets" ] ] } @@ -773,152 +710,6 @@ }, "ToPort": 8000 } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] } }, "Outputs": { @@ -954,23 +745,5 @@ ] } } - }, - "Parameters": { - "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json index d7456b3e52042..8b53d3042133f 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json @@ -293,34 +293,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } + "/aws-cdk/assets:baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540" ] ] }, @@ -360,36 +333,6 @@ } } }, - "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageAdoptRepository49B45957": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "ScheduledFargateTaskScheduledTaskDefScheduledContainerLogGroup4134B16C": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", @@ -440,13 +383,7 @@ { "Ref": "AWS::AccountId" }, - ":repository/", - { - "Fn::GetAtt": [ - "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageAdoptRepository49B45957", - "RepositoryName" - ] - } + ":repository/aws-cdk/assets" ] ] } @@ -565,170 +502,6 @@ } ] } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - } - }, - "Parameters": { - "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts index a7328ef2a3545..95399d83229b7 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts @@ -542,34 +542,7 @@ export = { { Ref: "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - Ref: "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - Ref: "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" - } - ] - } - ] - } + "/aws-cdk/assets:baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540" ] ] }, diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index 88cdef93cf952..00d6fb582078a 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -678,34 +678,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } + "/aws-cdk/assets:1f37178655533422f6654c973b99eadec99a723c7181c912e4fb0976187c687c" ] ] }, @@ -744,36 +717,6 @@ } } }, - "TaskDefTheContainerAssetImageAdoptRepository997406C3": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "TaskDefTheContainerLogGroupD94C8EF5": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", @@ -824,13 +767,7 @@ { "Ref": "AWS::AccountId" }, - ":repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } + ":repository/aws-cdk/assets" ] ] } @@ -924,152 +861,6 @@ ] } }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - }, "Rule4C995B7F": { "Type": "AWS::Events::Rule", "Properties": { @@ -1106,22 +897,6 @@ "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" - }, - "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json index 6cffa13533c6c..4039dbafddaf1 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json @@ -237,34 +237,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } + "/aws-cdk/assets:1f37178655533422f6654c973b99eadec99a723c7181c912e4fb0976187c687c" ] ] }, @@ -304,36 +277,6 @@ } } }, - "TaskDefTheContainerAssetImageAdoptRepository997406C3": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "TaskDefTheContainerLogGroupD94C8EF5": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", @@ -384,13 +327,7 @@ { "Ref": "AWS::AccountId" }, - ":repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } + ":repository/aws-cdk/assets" ] ] } @@ -510,152 +447,6 @@ ] } }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - }, "Rule4C995B7F": { "Type": "AWS::Events::Rule", "Properties": { @@ -706,23 +497,5 @@ ] } } - }, - "Parameters": { - "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json index 879684a397616..0a57d22dc1fb6 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json @@ -465,34 +465,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } + "/aws-cdk/assets:1f37178655533422f6654c973b99eadec99a723c7181c912e4fb0976187c687c" ] ] }, @@ -529,36 +502,6 @@ } } }, - "TaskDefTheContainerAssetImageAdoptRepository997406C3": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "TaskDefTheContainerLogGroupD94C8EF5": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", @@ -601,13 +544,7 @@ { "Ref": "AWS::Partition" }, - ":ecr:test-region:12345678:repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } + ":ecr:test-region:12345678:repository/aws-cdk/assets" ] ] } @@ -641,144 +578,6 @@ ] } }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:test-region:12345678:repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - }, "StateMachineRoleB840431D": { "Type": "AWS::IAM::Role", "Properties": { @@ -908,22 +707,6 @@ "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" - }, - "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json index 8a4cde77c2fcd..c99f991489a69 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json @@ -34,34 +34,7 @@ { "Ref": "AWS::URLSuffix" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } + "/aws-cdk/assets:1f37178655533422f6654c973b99eadec99a723c7181c912e4fb0976187c687c" ] ] }, @@ -100,36 +73,6 @@ } } }, - "TaskDefTheContainerAssetImageAdoptRepository997406C3": { - "Type": "Custom::ECRAdoptedRepository", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", - "Arn" - ] - }, - "RepositoryName": { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "TaskDefTheContainerLogGroupD94C8EF5": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", @@ -172,13 +115,7 @@ { "Ref": "AWS::Partition" }, - ":ecr:test-region:12345678:repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } + ":ecr:test-region:12345678:repository/aws-cdk/assets" ] ] } @@ -212,144 +149,6 @@ ] } }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetRepositoryPolicy", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepository", - "ecr:ListImages", - "ecr:BatchDeleteImage" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:test-region:12345678:repository/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "@sha256:", - { - "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" - } - ] - } - ] - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "Roles": [ - { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - } - ] - } - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" - }, - "S3Key": { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" - } - ] - } - ] - } - ] - ] - } - }, - "Handler": "handler.handler", - "Role": { - "Fn::GetAtt": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "Arn" - ] - }, - "Runtime": "nodejs10.x", - "Timeout": 300 - }, - "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" - ] - }, "FargateTaskSecurityGroup0BBB27CB": { "Type": "AWS::EC2::SecurityGroup", "Properties": { @@ -495,23 +294,5 @@ "StateMachineRoleB840431D" ] } - }, - "Parameters": { - "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { - "Type": "String", - "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { - "Type": "String", - "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { - "Type": "String", - "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - }, - "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { - "Type": "String", - "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" - } } -} +} \ No newline at end of file diff --git a/packages/aws-cdk/test/docker.test.ts b/packages/aws-cdk/test/docker.test.ts index ceacde1b42854..be13712c49aff 100644 --- a/packages/aws-cdk/test/docker.test.ts +++ b/packages/aws-cdk/test/docker.test.ts @@ -306,7 +306,6 @@ test('passes the correct file to docker build', async () => { path: '/foo', sourceHash: '1234567890abcdef', repositoryName: 'some-name', - imageTag: 'image-tag', buildArgs: { a: 'b', c: 'd' @@ -321,9 +320,8 @@ test('passes the correct file to docker build', async () => { if (!/STOPTEST/.test(e.toString())) { throw e; } } - const command = ['docker', 'build', '--tag', `uri:latest`, '/foo', '--file', 'some-file']; - - expect(shellStub.calledWith(command)).toBeTruthy(); + const command = ['docker', 'build', '--build-arg', 'a=b', '--build-arg', 'c=d', '--tag', 'uri:latest', '/foo', '--target', 'a-target', '--file', 'some-file']; + sinon.assert.calledWith(shellStub, command); prepareEcrRepositoryStub.restore(); shellStub.restore(); @@ -372,7 +370,7 @@ test('"imageTag" is used instead of "latest"', async () => { } // THEN - const command = ['docker', 'build', '--build-arg', 'a=b', '--build-arg', 'c=d', '--tag', `uri:image-tag`, '/foo', '--target', 'a-target']; + const command = ['docker', 'build', '--build-arg', 'a=b', '--build-arg', 'c=d', '--tag', 'uri:image-tag', '/foo', '--target', 'a-target', '--file', 'some-file']; sinon.assert.calledWith(shellStub, command); prepareEcrRepositoryStub.restore(); diff --git a/packages/decdk/test/fixture/tsconfig.json b/packages/decdk/test/fixture/tsconfig.json index ff97fed386484..07afb1f64860c 100644 --- a/packages/decdk/test/fixture/tsconfig.json +++ b/packages/decdk/test/fixture/tsconfig.json @@ -7,7 +7,7 @@ "inlineSourceMap": true, "inlineSources": true, "lib": [ - "es2016" + "es2018" ], "module": "CommonJS", "noEmitOnError": true, @@ -22,7 +22,7 @@ "strictNullChecks": true, "strictPropertyInitialization": true, "stripInternal": true, - "target": "ES2017" + "target": "ES2018" }, "include": [ "**/*.ts"