diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json index de9244e843078..b4291f6796366 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json @@ -1,7 +1,7 @@ { - "version": "31.0.0", + "version": "35.0.0", "files": { - "a047e78171779d23d25e3fc35f2b3ce7ff7313e616a588b6f8773b9360f12b26": { + "5496c737b6cd337f0e41f8e73a155aca194554cafe08f2937b95abcb5807a636": { "source": { "path": "aws-ecr-integ-stack.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "a047e78171779d23d25e3fc35f2b3ce7ff7313e616a588b6f8773b9360f12b26.json", + "objectKey": "5496c737b6cd337f0e41f8e73a155aca194554cafe08f2937b95abcb5807a636.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json index fea2eae1cf611..a9eed9841f45e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json @@ -65,6 +65,14 @@ } ] } + }, + "RepoWithEmptyOnDeleteCA5C67FA": { + "Type": "AWS::ECR::Repository", + "Properties": { + "EmptyOnDelete": true + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Outputs": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out index 7925065efbcc4..c5cb2e5de6344 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"31.0.0"} \ No newline at end of file +{"version":"35.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json index fbfe3f9089f79..4a4a176d22fed 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json @@ -1,5 +1,5 @@ { - "version": "31.0.0", + "version": "35.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json index 8ded05fd44287..530efded1be3b 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "31.0.0", + "version": "35.0.0", "testCases": { "cdk-ecr-integ-test-basic/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json index 299140c835cd5..e011369a944fa 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "31.0.0", + "version": "35.0.0", "artifacts": { "aws-ecr-integ-stack.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "aws-ecr-integ-stack.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/a047e78171779d23d25e3fc35f2b3ce7ff7313e616a588b6f8773b9360f12b26.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/5496c737b6cd337f0e41f8e73a155aca194554cafe08f2937b95abcb5807a636.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -51,6 +52,12 @@ "data": "MyUserDefaultPolicy7B897426" } ], + "/aws-ecr-integ-stack/RepoWithEmptyOnDelete/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "RepoWithEmptyOnDeleteCA5C67FA" + } + ], "/aws-ecr-integ-stack/RepositoryURI": [ { "type": "aws:cdk:logicalId", @@ -85,6 +92,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json index 5278e08ffe6e2..35e356fa8be0c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json @@ -127,6 +127,30 @@ "version": "0.0.0" } }, + "RepoWithEmptyOnDelete": { + "id": "RepoWithEmptyOnDelete", + "path": "aws-ecr-integ-stack/RepoWithEmptyOnDelete", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecr-integ-stack/RepoWithEmptyOnDelete/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECR::Repository", + "aws:cdk:cloudformation:props": { + "emptyOnDelete": true + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr.Repository", + "version": "0.0.0" + } + }, "RepositoryURI": { "id": "RepositoryURI", "path": "aws-ecr-integ-stack/RepositoryURI", @@ -170,7 +194,7 @@ "path": "cdk-ecr-integ-test-basic/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.270" + "version": "10.3.0" } }, "DeployAssert": { @@ -216,7 +240,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.270" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts index 3830660077478..eb0c05a04e971 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts @@ -17,6 +17,11 @@ const user = new iam.User(stack, 'MyUser'); repo.grantRead(user); repo.grantPullPush(user); +new ecr.Repository(stack, 'RepoWithEmptyOnDelete', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + emptyOnDelete: true, +}); + new cdk.CfnOutput(stack, 'RepositoryURI', { value: repo.repositoryUri, }); diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md index 87fd5b0c576ce..ab4fca8e865cb 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md @@ -253,7 +253,7 @@ const app = new App({ By default, the staging resources will be cleaned up on stack deletion. That means that the S3 Bucket and ECR Repositories are set to `RemovalPolicy.DESTROY` and have `autoDeleteObjects` -or `autoDeleteImages` turned on. This creates custom resources under the hood to facilitate +or `emptyOnDelete` turned on. This creates custom resources under the hood to facilitate cleanup. To turn this off, specify `autoDeleteStagingAssets: false`. ```ts diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts index 3b3469cdbd827..3e98b3458280c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts @@ -429,7 +429,7 @@ export class DefaultStagingStack extends Stack implements IStagingResources { }], ...(this.autoDeleteStagingAssets ? { removalPolicy: RemovalPolicy.DESTROY, - autoDeleteImages: true, + emptyOnDelete: true, } : { removalPolicy: RemovalPolicy.RETAIN, }), diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json index ca7bac420615b..fd7794208d185 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json @@ -568,175 +568,26 @@ "defaultresourcesmaxecrasset13112F7F9": { "Type": "AWS::ECR::Repository", "Properties": { + "EmptyOnDelete": true, "ImageTagMutability": "IMMUTABLE", "LifecyclePolicy": { "LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "RepositoryName": "default-resourcesmax/ecr-asset/1", - "Tags": [ - { - "Key": "aws-cdk:auto-delete-images", - "Value": "true" - } - ] + "RepositoryName": "default-resourcesmax/ecr-asset/1" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5": { - "Type": "Custom::ECRAutoDeleteImages", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030", - "Arn" - ] - }, - "RepositoryName": { - "Ref": "defaultresourcesmaxecrasset13112F7F9" - } - }, - "DependsOn": [ - "defaultresourcesmaxecrasset13112F7F9" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ] - }, - "ManagedPolicyArns": [ - { - "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } - ], - "Policies": [ - { - "PolicyName": "Inline", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ecr:BatchDeleteImage", - "ecr:DescribeRepositories", - "ecr:ListImages", - "ecr:ListTagsForResource" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/*" - ] - ] - } - ], - "Condition": { - "StringEquals": { - "ecr:ResourceTag/aws-cdk:auto-delete-images": "true" - } - } - } - ] - } - } - ] - } - }, - "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "\"use strict\";var C=Object.create;var c=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var L=(e,t)=>{for(var o in t)c(e,o,{get:t[o],enumerable:!0})},d=(e,t,o,s)=>{if(t&&typeof t==\"object\"||typeof t==\"function\")for(let r of w(t))!P.call(e,r)&&r!==o&&c(e,r,{get:()=>t[r],enumerable:!(s=S(t,r))||s.enumerable});return e};var p=(e,t,o)=>(o=e!=null?C(A(e)):{},d(t||!e||!e.__esModule?c(o,\"default\",{value:e,enumerable:!0}):o,e)),D=e=>d(c({},\"__esModule\",{value:!0}),e);var W={};L(W,{autoDeleteHandler:()=>I,handler:()=>k});module.exports=D(W);var h=require(\"@aws-sdk/client-ecr\");var m=p(require(\"https\")),R=p(require(\"url\")),n={sendHttpRequest:x,log:N,includeStackTraces:!0,userHandlerIndex:\"./index\"},l=\"AWSCDK::CustomResourceProviderFramework::CREATE_FAILED\",b=\"AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID\";function y(e){return async(t,o)=>{let s={...t,ResponseURL:\"...\"};if(n.log(JSON.stringify(s,void 0,2)),t.RequestType===\"Delete\"&&t.PhysicalResourceId===l){n.log(\"ignoring DELETE event caused by a failed CREATE event\"),await u(\"SUCCESS\",t);return}try{let r=await e(s,o),a=T(t,r);await u(\"SUCCESS\",a)}catch(r){let a={...t,Reason:n.includeStackTraces?r.stack:r.message};a.PhysicalResourceId||(t.RequestType===\"Create\"?(n.log(\"CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored\"),a.PhysicalResourceId=l):n.log(`ERROR: Malformed event. \"PhysicalResourceId\" is required: ${JSON.stringify(t)}`)),await u(\"FAILED\",a)}}}function T(e,t={}){let o=t.PhysicalResourceId??e.PhysicalResourceId??e.RequestId;if(e.RequestType===\"Delete\"&&o!==e.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from \"${e.PhysicalResourceId}\" to \"${t.PhysicalResourceId}\" during deletion`);return{...e,...t,PhysicalResourceId:o}}async function u(e,t){let o={Status:e,Reason:t.Reason??e,StackId:t.StackId,RequestId:t.RequestId,PhysicalResourceId:t.PhysicalResourceId||b,LogicalResourceId:t.LogicalResourceId,NoEcho:t.NoEcho,Data:t.Data};n.log(\"submit response to cloudformation\",o);let s=JSON.stringify(o),r=R.parse(t.ResponseURL),a={hostname:r.hostname,path:r.path,method:\"PUT\",headers:{\"content-type\":\"\",\"content-length\":Buffer.byteLength(s,\"utf8\")}};await H({attempts:5,sleep:1e3},n.sendHttpRequest)(a,s)}async function x(e,t){return new Promise((o,s)=>{try{let r=m.request(e,a=>o());r.on(\"error\",s),r.write(t),r.end()}catch(r){s(r)}})}function N(e,...t){console.log(e,...t)}function H(e,t){return async(...o)=>{let s=e.attempts,r=e.sleep;for(;;)try{return await t(...o)}catch(a){if(s--<=0)throw a;await F(Math.floor(Math.random()*r)),r*=2}}}async function F(e){return new Promise(t=>setTimeout(t,e))}var g=\"aws-cdk:auto-delete-images\",i=new h.ECR({}),k=y(I);async function I(e){switch(e.RequestType){case\"Create\":break;case\"Update\":return _(e);case\"Delete\":return f(e.ResourceProperties?.RepositoryName)}}async function _(e){let t=e,o=t.OldResourceProperties?.RepositoryName,s=t.ResourceProperties?.RepositoryName;if(s&&o&&s!==o)return f(o)}async function E(e){let t=await i.listImages(e),o=[],s=[];(t.imageIds??[]).forEach(a=>{\"imageTag\"in a?s.push(a):o.push(a)});let r=t.nextToken??null;o.length===0&&s.length===0||(s.length!==0&&await i.batchDeleteImage({repositoryName:e.repositoryName,imageIds:s}),o.length!==0&&await i.batchDeleteImage({repositoryName:e.repositoryName,imageIds:o}),r&&await E({...e,nextToken:r}))}async function f(e){if(!e)throw new Error(\"No RepositoryName was provided.\");let o=(await i.describeRepositories({repositoryNames:[e]})).repositories?.find(s=>s.repositoryName===e);if(!await q(o?.repositoryArn)){process.stdout.write(`Repository does not have '${g}' tag, skipping cleaning.\n`);return}try{await E({repositoryName:e})}catch(s){if(s.name!==\"RepositoryNotFoundException\")throw s}}async function q(e){return(await i.listTagsForResource({resourceArn:e})).tags?.some(o=>o.Key===g&&o.Value===\"true\")}0&&(module.exports={autoDeleteHandler,handler});\n" - }, - "Timeout": 900, - "MemorySize": 128, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773", - "Arn" - ] - }, - "Runtime": "nodejs18.x", - "Description": { - "Fn::Join": [ - "", - [ - "Lambda function for auto-deleting images in ", - { - "Ref": "defaultresourcesmaxecrasset13112F7F9" - }, - " repository." - ] - ] - } - }, - "DependsOn": [ - "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773" - ] - }, "defaultresourcesmaxecrasset2904B88A7": { "Type": "AWS::ECR::Repository", "Properties": { + "EmptyOnDelete": true, "ImageTagMutability": "IMMUTABLE", "LifecyclePolicy": { "LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "RepositoryName": "default-resourcesmax/ecr-asset-2", - "Tags": [ - { - "Key": "aws-cdk:auto-delete-images", - "Value": "true" - } - ] - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1": { - "Type": "Custom::ECRAutoDeleteImages", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030", - "Arn" - ] - }, - "RepositoryName": { - "Ref": "defaultresourcesmaxecrasset2904B88A7" - } + "RepositoryName": "default-resourcesmax/ecr-asset-2" }, - "DependsOn": [ - "defaultresourcesmaxecrasset2904B88A7" - ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" } diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/cdk.out index 2313ab5436501..c5cb2e5de6344 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/cdk.out +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"35.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integ.json index f835193b821e8..4000c99e6da28 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integ.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "testCases": { "integ-tests/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json index 8bb5bda32b900..5c520eaba3f94 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json index a985e1faa32a9..04aceb69e51d6 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "artifacts": { "synthesize-default-resources.assets": { "type": "cdk:asset-manifest", @@ -155,34 +155,46 @@ "data": "defaultresourcesmaxecrasset13112F7F9" } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset--1/AutoDeleteImagesCustomResource/Default": [ + "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/Resource": [ { "type": "aws:cdk:logicalId", - "data": "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5" + "data": "defaultresourcesmaxecrasset2904B88A7" } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Role": [ + "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5": [ { "type": "aws:cdk:logicalId", - "data": "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773" + "data": "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Handler": [ + "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773": [ { "type": "aws:cdk:logicalId", - "data": "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030" + "data": "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/Resource": [ + "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030": [ { "type": "aws:cdk:logicalId", - "data": "defaultresourcesmaxecrasset2904B88A7" + "data": "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/AutoDeleteImagesCustomResource/Default": [ + "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1": [ { "type": "aws:cdk:logicalId", - "data": "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1" + "data": "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ] }, diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json index bf99b2118f8c8..07e6912263a9c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "files": { "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { "source": { diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json index 39db05942102e..4aee681c070e0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json @@ -1083,41 +1083,18 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::ECR::Repository", "aws:cdk:cloudformation:props": { + "emptyOnDelete": true, "imageTagMutability": "IMMUTABLE", "lifecyclePolicy": { "lifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "repositoryName": "default-resourcesmax/ecr-asset/1", - "tags": [ - { - "key": "aws-cdk:auto-delete-images", - "value": "true" - } - ] + "repositoryName": "default-resourcesmax/ecr-asset/1" } }, "constructInfo": { "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", "version": "0.0.0" } - }, - "AutoDeleteImagesCustomResource": { - "id": "AutoDeleteImagesCustomResource", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset--1/AutoDeleteImagesCustomResource", - "children": { - "Default": { - "id": "Default", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset--1/AutoDeleteImagesCustomResource/Default", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.CustomResource", - "version": "0.0.0" - } } }, "constructInfo": { @@ -1125,32 +1102,6 @@ "version": "0.0.0" } }, - "Custom::ECRAutoDeleteImagesCustomResourceProvider": { - "id": "Custom::ECRAutoDeleteImagesCustomResourceProvider", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider", - "children": { - "Role": { - "id": "Role", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Role", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - }, - "Handler": { - "id": "Handler", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Handler", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.CustomResourceProvider", - "version": "0.0.0" - } - }, "default-resourcesmax--ecr-asset-2": { "id": "default-resourcesmax--ecr-asset-2", "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2", @@ -1161,41 +1112,18 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::ECR::Repository", "aws:cdk:cloudformation:props": { + "emptyOnDelete": true, "imageTagMutability": "IMMUTABLE", "lifecyclePolicy": { "lifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "repositoryName": "default-resourcesmax/ecr-asset-2", - "tags": [ - { - "key": "aws-cdk:auto-delete-images", - "value": "true" - } - ] + "repositoryName": "default-resourcesmax/ecr-asset-2" } }, "constructInfo": { "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", "version": "0.0.0" } - }, - "AutoDeleteImagesCustomResource": { - "id": "AutoDeleteImagesCustomResource", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/AutoDeleteImagesCustomResource", - "children": { - "Default": { - "id": "Default", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/AutoDeleteImagesCustomResource/Default", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.CustomResource", - "version": "0.0.0" - } } }, "constructInfo": { @@ -1222,7 +1150,7 @@ "path": "integ-tests/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -1268,7 +1196,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json index ca7bac420615b..fd7794208d185 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/StagingStack-default-resourcesmax-ACCOUNT-REGION.template.json @@ -568,175 +568,26 @@ "defaultresourcesmaxecrasset13112F7F9": { "Type": "AWS::ECR::Repository", "Properties": { + "EmptyOnDelete": true, "ImageTagMutability": "IMMUTABLE", "LifecyclePolicy": { "LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "RepositoryName": "default-resourcesmax/ecr-asset/1", - "Tags": [ - { - "Key": "aws-cdk:auto-delete-images", - "Value": "true" - } - ] + "RepositoryName": "default-resourcesmax/ecr-asset/1" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5": { - "Type": "Custom::ECRAutoDeleteImages", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030", - "Arn" - ] - }, - "RepositoryName": { - "Ref": "defaultresourcesmaxecrasset13112F7F9" - } - }, - "DependsOn": [ - "defaultresourcesmaxecrasset13112F7F9" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ] - }, - "ManagedPolicyArns": [ - { - "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } - ], - "Policies": [ - { - "PolicyName": "Inline", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ecr:BatchDeleteImage", - "ecr:DescribeRepositories", - "ecr:ListImages", - "ecr:ListTagsForResource" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/*" - ] - ] - } - ], - "Condition": { - "StringEquals": { - "ecr:ResourceTag/aws-cdk:auto-delete-images": "true" - } - } - } - ] - } - } - ] - } - }, - "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "\"use strict\";var C=Object.create;var c=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var L=(e,t)=>{for(var o in t)c(e,o,{get:t[o],enumerable:!0})},d=(e,t,o,s)=>{if(t&&typeof t==\"object\"||typeof t==\"function\")for(let r of w(t))!P.call(e,r)&&r!==o&&c(e,r,{get:()=>t[r],enumerable:!(s=S(t,r))||s.enumerable});return e};var p=(e,t,o)=>(o=e!=null?C(A(e)):{},d(t||!e||!e.__esModule?c(o,\"default\",{value:e,enumerable:!0}):o,e)),D=e=>d(c({},\"__esModule\",{value:!0}),e);var W={};L(W,{autoDeleteHandler:()=>I,handler:()=>k});module.exports=D(W);var h=require(\"@aws-sdk/client-ecr\");var m=p(require(\"https\")),R=p(require(\"url\")),n={sendHttpRequest:x,log:N,includeStackTraces:!0,userHandlerIndex:\"./index\"},l=\"AWSCDK::CustomResourceProviderFramework::CREATE_FAILED\",b=\"AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID\";function y(e){return async(t,o)=>{let s={...t,ResponseURL:\"...\"};if(n.log(JSON.stringify(s,void 0,2)),t.RequestType===\"Delete\"&&t.PhysicalResourceId===l){n.log(\"ignoring DELETE event caused by a failed CREATE event\"),await u(\"SUCCESS\",t);return}try{let r=await e(s,o),a=T(t,r);await u(\"SUCCESS\",a)}catch(r){let a={...t,Reason:n.includeStackTraces?r.stack:r.message};a.PhysicalResourceId||(t.RequestType===\"Create\"?(n.log(\"CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored\"),a.PhysicalResourceId=l):n.log(`ERROR: Malformed event. \"PhysicalResourceId\" is required: ${JSON.stringify(t)}`)),await u(\"FAILED\",a)}}}function T(e,t={}){let o=t.PhysicalResourceId??e.PhysicalResourceId??e.RequestId;if(e.RequestType===\"Delete\"&&o!==e.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from \"${e.PhysicalResourceId}\" to \"${t.PhysicalResourceId}\" during deletion`);return{...e,...t,PhysicalResourceId:o}}async function u(e,t){let o={Status:e,Reason:t.Reason??e,StackId:t.StackId,RequestId:t.RequestId,PhysicalResourceId:t.PhysicalResourceId||b,LogicalResourceId:t.LogicalResourceId,NoEcho:t.NoEcho,Data:t.Data};n.log(\"submit response to cloudformation\",o);let s=JSON.stringify(o),r=R.parse(t.ResponseURL),a={hostname:r.hostname,path:r.path,method:\"PUT\",headers:{\"content-type\":\"\",\"content-length\":Buffer.byteLength(s,\"utf8\")}};await H({attempts:5,sleep:1e3},n.sendHttpRequest)(a,s)}async function x(e,t){return new Promise((o,s)=>{try{let r=m.request(e,a=>o());r.on(\"error\",s),r.write(t),r.end()}catch(r){s(r)}})}function N(e,...t){console.log(e,...t)}function H(e,t){return async(...o)=>{let s=e.attempts,r=e.sleep;for(;;)try{return await t(...o)}catch(a){if(s--<=0)throw a;await F(Math.floor(Math.random()*r)),r*=2}}}async function F(e){return new Promise(t=>setTimeout(t,e))}var g=\"aws-cdk:auto-delete-images\",i=new h.ECR({}),k=y(I);async function I(e){switch(e.RequestType){case\"Create\":break;case\"Update\":return _(e);case\"Delete\":return f(e.ResourceProperties?.RepositoryName)}}async function _(e){let t=e,o=t.OldResourceProperties?.RepositoryName,s=t.ResourceProperties?.RepositoryName;if(s&&o&&s!==o)return f(o)}async function E(e){let t=await i.listImages(e),o=[],s=[];(t.imageIds??[]).forEach(a=>{\"imageTag\"in a?s.push(a):o.push(a)});let r=t.nextToken??null;o.length===0&&s.length===0||(s.length!==0&&await i.batchDeleteImage({repositoryName:e.repositoryName,imageIds:s}),o.length!==0&&await i.batchDeleteImage({repositoryName:e.repositoryName,imageIds:o}),r&&await E({...e,nextToken:r}))}async function f(e){if(!e)throw new Error(\"No RepositoryName was provided.\");let o=(await i.describeRepositories({repositoryNames:[e]})).repositories?.find(s=>s.repositoryName===e);if(!await q(o?.repositoryArn)){process.stdout.write(`Repository does not have '${g}' tag, skipping cleaning.\n`);return}try{await E({repositoryName:e})}catch(s){if(s.name!==\"RepositoryNotFoundException\")throw s}}async function q(e){return(await i.listTagsForResource({resourceArn:e})).tags?.some(o=>o.Key===g&&o.Value===\"true\")}0&&(module.exports={autoDeleteHandler,handler});\n" - }, - "Timeout": 900, - "MemorySize": 128, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773", - "Arn" - ] - }, - "Runtime": "nodejs18.x", - "Description": { - "Fn::Join": [ - "", - [ - "Lambda function for auto-deleting images in ", - { - "Ref": "defaultresourcesmaxecrasset13112F7F9" - }, - " repository." - ] - ] - } - }, - "DependsOn": [ - "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773" - ] - }, "defaultresourcesmaxecrasset2904B88A7": { "Type": "AWS::ECR::Repository", "Properties": { + "EmptyOnDelete": true, "ImageTagMutability": "IMMUTABLE", "LifecyclePolicy": { "LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "RepositoryName": "default-resourcesmax/ecr-asset-2", - "Tags": [ - { - "Key": "aws-cdk:auto-delete-images", - "Value": "true" - } - ] - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1": { - "Type": "Custom::ECRAutoDeleteImages", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030", - "Arn" - ] - }, - "RepositoryName": { - "Ref": "defaultresourcesmaxecrasset2904B88A7" - } + "RepositoryName": "default-resourcesmax/ecr-asset-2" }, - "DependsOn": [ - "defaultresourcesmaxecrasset2904B88A7" - ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" } diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/cdk.out index 2313ab5436501..c5cb2e5de6344 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/cdk.out +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"35.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integ.json index f835193b821e8..4000c99e6da28 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integ.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "testCases": { "integ-tests/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json index 8bb5bda32b900..5c520eaba3f94 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/manifest.json index a985e1faa32a9..04aceb69e51d6 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "artifacts": { "synthesize-default-resources.assets": { "type": "cdk:asset-manifest", @@ -155,34 +155,46 @@ "data": "defaultresourcesmaxecrasset13112F7F9" } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset--1/AutoDeleteImagesCustomResource/Default": [ + "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/Resource": [ { "type": "aws:cdk:logicalId", - "data": "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5" + "data": "defaultresourcesmaxecrasset2904B88A7" } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Role": [ + "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5": [ { "type": "aws:cdk:logicalId", - "data": "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773" + "data": "defaultresourcesmaxecrasset1AutoDeleteImagesCustomResource0FD7F0F5", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Handler": [ + "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773": [ { "type": "aws:cdk:logicalId", - "data": "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030" + "data": "CustomECRAutoDeleteImagesCustomResourceProviderRole665F2773", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/Resource": [ + "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030": [ { "type": "aws:cdk:logicalId", - "data": "defaultresourcesmaxecrasset2904B88A7" + "data": "CustomECRAutoDeleteImagesCustomResourceProviderHandler8D89C030", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/AutoDeleteImagesCustomResource/Default": [ + "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1": [ { "type": "aws:cdk:logicalId", - "data": "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1" + "data": "defaultresourcesmaxecrasset2AutoDeleteImagesCustomResource708714C1", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ] }, diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/synthesize-default-resources.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/synthesize-default-resources.assets.json index bf99b2118f8c8..07e6912263a9c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/synthesize-default-resources.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/synthesize-default-resources.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "35.0.0", "files": { "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { "source": { diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/tree.json index 7600a467a9d64..3429236c8462f 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts.snapshot/tree.json @@ -13,7 +13,7 @@ "path": "synthesize-default-resources/UsingAppStagingSynthesizer--synthesize-default-resources", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "lambda-s3": { @@ -1083,41 +1083,18 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::ECR::Repository", "aws:cdk:cloudformation:props": { + "emptyOnDelete": true, "imageTagMutability": "IMMUTABLE", "lifecyclePolicy": { "lifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "repositoryName": "default-resourcesmax/ecr-asset/1", - "tags": [ - { - "key": "aws-cdk:auto-delete-images", - "value": "true" - } - ] + "repositoryName": "default-resourcesmax/ecr-asset/1" } }, "constructInfo": { "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", "version": "0.0.0" } - }, - "AutoDeleteImagesCustomResource": { - "id": "AutoDeleteImagesCustomResource", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset--1/AutoDeleteImagesCustomResource", - "children": { - "Default": { - "id": "Default", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset--1/AutoDeleteImagesCustomResource/Default", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.CustomResource", - "version": "0.0.0" - } } }, "constructInfo": { @@ -1125,32 +1102,6 @@ "version": "0.0.0" } }, - "Custom::ECRAutoDeleteImagesCustomResourceProvider": { - "id": "Custom::ECRAutoDeleteImagesCustomResourceProvider", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider", - "children": { - "Role": { - "id": "Role", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Role", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - }, - "Handler": { - "id": "Handler", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/Custom::ECRAutoDeleteImagesCustomResourceProvider/Handler", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.CustomResourceProvider", - "version": "0.0.0" - } - }, "default-resourcesmax--ecr-asset-2": { "id": "default-resourcesmax--ecr-asset-2", "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2", @@ -1161,41 +1112,18 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::ECR::Repository", "aws:cdk:cloudformation:props": { + "emptyOnDelete": true, "imageTagMutability": "IMMUTABLE", "lifecyclePolicy": { "lifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" }, - "repositoryName": "default-resourcesmax/ecr-asset-2", - "tags": [ - { - "key": "aws-cdk:auto-delete-images", - "value": "true" - } - ] + "repositoryName": "default-resourcesmax/ecr-asset-2" } }, "constructInfo": { "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", "version": "0.0.0" } - }, - "AutoDeleteImagesCustomResource": { - "id": "AutoDeleteImagesCustomResource", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/AutoDeleteImagesCustomResource", - "children": { - "Default": { - "id": "Default", - "path": "StagingStack-default-resourcesmax-ACCOUNT-REGION/default-resourcesmax--ecr-asset-2/AutoDeleteImagesCustomResource/Default", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.CustomResource", - "version": "0.0.0" - } } }, "constructInfo": { @@ -1222,7 +1150,7 @@ "path": "integ-tests/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -1268,7 +1196,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/aws-cdk-lib/aws-ecr/README.md b/packages/aws-cdk-lib/aws-ecr/README.md index 288b91f214fe1..7ad75d94215c4 100644 --- a/packages/aws-cdk-lib/aws-ecr/README.md +++ b/packages/aws-cdk-lib/aws-ecr/README.md @@ -176,12 +176,13 @@ policy is set to `RemovalPolicy.DESTROY`, the repository will be deleted as long as it does not contain any images. To override this and force all images to get deleted during repository deletion, -enable the`autoDeleteImages` option. +enable the `emptyOnDelete` option as well as setting the removal policy to +`RemovalPolicy.DESTROY`. ```ts const repository = new ecr.Repository(this, 'MyTempRepo', { - removalPolicy: RemovalPolicy.DESTROY, - autoDeleteImages: true, + removalPolicy: RemovalPolicy.DESTROY, + emptyOnDelete: true, }); ``` diff --git a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts index 008753dbd567d..666702a9c462a 100644 --- a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts +++ b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts @@ -565,8 +565,16 @@ export interface RepositoryProps { * Requires the `removalPolicy` to be set to `RemovalPolicy.DESTROY`. * * @default false + * @deprecated Use `emptyOnDelete` instead. */ readonly autoDeleteImages?: boolean; + + /** + * If true, deleting the repository force deletes the contents of the repository. If false, the repository must be empty before attempting to delete it. + * + * @default false + */ + readonly emptyOnDelete?: boolean; } export interface RepositoryAttributes { @@ -704,6 +712,7 @@ export class Repository extends RepositoryBase { imageScanningConfiguration: props.imageScanOnPush !== undefined ? { scanOnPush: props.imageScanOnPush } : undefined, imageTagMutability: props.imageTagMutability || undefined, encryptionConfiguration: this.parseEncryption(props), + emptyOnDelete: props.emptyOnDelete, }); this._resource = resource; @@ -721,7 +730,9 @@ export class Repository extends RepositoryBase { resourceName: this.physicalName, }); - if (props.autoDeleteImages) { + if (props.emptyOnDelete && props.removalPolicy !== RemovalPolicy.DESTROY) { + throw new Error('Cannot use \'emptyOnDelete\' property on a repository without setting removal policy to \'DESTROY\'.'); + } else if (props.emptyOnDelete == undefined && props.autoDeleteImages) { if (props.removalPolicy !== RemovalPolicy.DESTROY) { throw new Error('Cannot use \'autoDeleteImages\' property on a repository without setting removal policy to \'DESTROY\'.'); } diff --git a/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts b/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts index 002c347688457..2bd65a3a79ae6 100644 --- a/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts +++ b/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts @@ -77,6 +77,28 @@ describe('repository', () => { }); }); + test('emptyOnDelete can be set', () => { + // GIVEN + const stack = new cdk.Stack(); + new ecr.Repository(stack, 'Repo', { emptyOnDelete: true, removalPolicy: cdk.RemovalPolicy.DESTROY }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { + EmptyOnDelete: true, + }); + }); + + test('emptyOnDelete requires \'RemovalPolicy.DESTROY\'', () => { + // GIVEN + const stack = new cdk.Stack(); + + // THEN + expect( () => { + new ecr.Repository(stack, 'Repo', { emptyOnDelete: true }); + }, + ).toThrow('Cannot use \'emptyOnDelete\' property on a repository without setting removal policy to \'DESTROY\'.'); + }); + test('add day-based lifecycle policy', () => { // GIVEN const stack = new cdk.Stack(); @@ -978,6 +1000,24 @@ describe('repository', () => { }); describe('when auto delete images is set to true', () => { + test('it is ignored if emptyOnDelete is set', () => { + const stack = new cdk.Stack(); + + new ecr.Repository(stack, 'Repo1', { + autoDeleteImages: true, + emptyOnDelete: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + new ecr.Repository(stack, 'Repo2', { + autoDeleteImages: true, + emptyOnDelete: false, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 0); + }); + test('permissions are correctly for multiple ecr repos', () => { const stack = new cdk.Stack(); new ecr.Repository(stack, 'Repo1', {