diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets.json new file mode 100644 index 0000000000000..56663f5db4f93 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets.json @@ -0,0 +1,20 @@ +{ + "version": "48.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF Template", + "source": { + "path": "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region-d8d86b35": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101/Dockerfile b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101/index.py b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + + It works + +

Hello from the integ test container

+

This container got built and started as part of the integ test.

+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86/Dockerfile b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86/index.py b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + + It works + +

Hello from the integ test container

+

This container got built and started as part of the integ test.

+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.56600b8907fdf5fe1ae6fdf12b0c782ab2c6a2791aa43e69434147ecb75703e3/Dockerfile b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.56600b8907fdf5fe1ae6fdf12b0c782ab2c6a2791aa43e69434147ecb75703e3/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.56600b8907fdf5fe1ae6fdf12b0c782ab2c6a2791aa43e69434147ecb75703e3/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.56600b8907fdf5fe1ae6fdf12b0c782ab2c6a2791aa43e69434147ecb75703e3/index.py b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.56600b8907fdf5fe1ae6fdf12b0c782ab2c6a2791aa43e69434147ecb75703e3/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.56600b8907fdf5fe1ae6fdf12b0c782ab2c6a2791aa43e69434147ecb75703e3/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + + It works + +

Hello from the integ test container

+

This container got built and started as part of the integ test.

+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f/Dockerfile b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f/index.py b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + + It works + +

Hello from the integ test container

+

This container got built and started as part of the integ test.

+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c1bf4629db1d1a885f97f1564a08b49ede34928d386dd793b6a68dad2a260c7c/Dockerfile b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c1bf4629db1d1a885f97f1564a08b49ede34928d386dd793b6a68dad2a260c7c/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c1bf4629db1d1a885f97f1564a08b49ede34928d386dd793b6a68dad2a260c7c/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c1bf4629db1d1a885f97f1564a08b49ede34928d386dd793b6a68dad2a260c7c/index.py b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c1bf4629db1d1a885f97f1564a08b49ede34928d386dd793b6a68dad2a260c7c/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c1bf4629db1d1a885f97f1564a08b49ede34928d386dd793b6a68dad2a260c7c/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + + It works + +

Hello from the integ test container

+

This container got built and started as part of the integ test.

+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df/Dockerfile b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df/index.py b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + + It works + +

Hello from the integ test container

+

This container got built and started as part of the integ test.

+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/cdk.out new file mode 100644 index 0000000000000..523a9aac37cbf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"48.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ-custom-repository.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ-custom-repository.assets.json new file mode 100644 index 0000000000000..385ff0e25d324 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ-custom-repository.assets.json @@ -0,0 +1,47 @@ +{ + "version": "48.0.0", + "files": { + "55cf2121f502e1a39823b3817a2a54773ed7f294da1f0a09f2d5b81e489ec188": { + "displayName": "integ-custom-repository Template", + "source": { + "path": "integ-custom-repository.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region-5f17575b": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "55cf2121f502e1a39823b3817a2a54773ed7f294da1f0a09f2d5b81e489ec188.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": { + "c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df": { + "displayName": "DefaultRepoCustomTag", + "source": { + "directory": "asset.c25c167ce53bed796d130957066d4d9b138f8ee9e6162ad0575446a87c0252df" + }, + "destinations": { + "current_account-current_region-517b0fda": { + "repositoryName": "cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}", + "imageTag": "custom-tag-v2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-image-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f": { + "displayName": "DefaultRepoPrefixed", + "source": { + "directory": "asset.8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f" + }, + "destinations": { + "current_account-current_region-0d567ef7": { + "repositoryName": "cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}", + "imageTag": "feature-8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-image-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ-custom-repository.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ-custom-repository.template.json new file mode 100644 index 0000000000000..7e62ea0d6f5be --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ-custom-repository.template.json @@ -0,0 +1,119 @@ +{ + "Resources": { + "CustomRepo98CFBAD2": { + "Type": "AWS::ECR::Repository", + "Properties": { + "RepositoryName": "cdk-integ-custom-repo" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Outputs": { + "CustomRepoName": { + "Value": { + "Ref": "CustomRepo98CFBAD2" + } + }, + "BasicCustomAssetUri": { + "Value": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "CustomRepo98CFBAD2", + "Arn" + ] + } + ] + } + ] + }, + ".dkr.ecr.", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "CustomRepo98CFBAD2", + "Arn" + ] + } + ] + } + ] + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "CustomRepo98CFBAD2" + }, + ":05184bb384306c04f5f252e503170bc375f52d8a5803148b86e5c9188316a101" + ] + ] + } + }, + "TaggedCustomAssetTag": { + "Value": "v1.2.3" + }, + "PrefixedCustomAssetTag": { + "Value": "branch-main-0750f79ef76f117846eb2cb4bf6089ac5548979a34df919eda4aed0c03e58c86" + }, + "DefaultRepoCustomTagTag": { + "Value": "custom-tag-v2" + }, + "DefaultRepoPrefixedTag": { + "Value": "feature-8edfe7607498bbd5722848a8b2f76ea961ec87deca4159b67a764c643bb7ba5f" + }, + "PrecedenceTestTag": { + "Value": "explicit-wins" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ.json new file mode 100644 index 0000000000000..7a48a3259c0aa --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "48.0.0", + "testCases": { + "EcrCustomRepositoryTest/DefaultTest": { + "stacks": [ + "integ-custom-repository" + ], + "assertionStack": "EcrCustomRepositoryTest/DefaultTest/DeployAssert", + "assertionStackName": "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF" + } + }, + "minimumCliVersion": "2.1027.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/manifest.json new file mode 100644 index 0000000000000..bc4a5eda20968 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/manifest.json @@ -0,0 +1,654 @@ +{ + "version": "48.0.0", + "artifacts": { + "integ-custom-repository.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-custom-repository.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-custom-repository": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-custom-repository.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}/55cf2121f502e1a39823b3817a2a54773ed7f294da1f0a09f2d5b81e489ec188.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-custom-repository.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-custom-repository.assets" + ], + "metadata": { + "/integ-custom-repository/CustomRepo": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "repositoryName": "*", + "removalPolicy": "destroy" + } + } + ], + "/integ-custom-repository/CustomRepo/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomRepo98CFBAD2" + } + ], + "/integ-custom-repository/CustomRepoName": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomRepoName" + } + ], + "/integ-custom-repository/BasicCustomAssetUri": [ + { + "type": "aws:cdk:logicalId", + "data": "BasicCustomAssetUri" + } + ], + "/integ-custom-repository/TaggedCustomAssetTag": [ + { + "type": "aws:cdk:logicalId", + "data": "TaggedCustomAssetTag" + } + ], + "/integ-custom-repository/PrefixedCustomAssetTag": [ + { + "type": "aws:cdk:logicalId", + "data": "PrefixedCustomAssetTag" + } + ], + "/integ-custom-repository/DefaultRepoCustomTagTag": [ + { + "type": "aws:cdk:logicalId", + "data": "DefaultRepoCustomTagTag" + } + ], + "/integ-custom-repository/DefaultRepoPrefixedTag": [ + { + "type": "aws:cdk:logicalId", + "data": "DefaultRepoPrefixedTag" + } + ], + "/integ-custom-repository/PrecedenceTestTag": [ + { + "type": "aws:cdk:logicalId", + "data": "PrecedenceTestTag" + } + ], + "/integ-custom-repository/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-custom-repository/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-custom-repository" + }, + "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "EcrCustomRepositoryTestDefaultTestDeployAssert85D554DF.assets" + ], + "metadata": { + "/EcrCustomRepositoryTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/EcrCustomRepositoryTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "EcrCustomRepositoryTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-lib/feature-flag-report": { + "type": "cdk:feature-flag-report", + "properties": { + "module": "aws-cdk-lib", + "flags": { + "@aws-cdk/aws-signer:signingProfileNamePassedToCfn": { + "recommendedValue": true, + "explanation": "Pass signingProfileName to CfnSigningProfile" + }, + "@aws-cdk/core:newStyleStackSynthesis": { + "recommendedValue": true, + "explanation": "Switch to new stack synthesis method which enables CI/CD", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:stackRelativeExports": { + "recommendedValue": true, + "explanation": "Name exports based on the construct paths relative to the stack, rather than the global construct path", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": { + "recommendedValue": true, + "explanation": "Disable implicit openListener when custom security groups are provided" + }, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": { + "recommendedValue": true, + "explanation": "Force lowercasing of RDS Cluster names in CDK", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": { + "recommendedValue": true, + "explanation": "Allow adding/removing multiple UsagePlanKeys independently", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeVersionProps": { + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeLayerVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`." + }, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": { + "recommendedValue": true, + "explanation": "Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:checkSecretUsage": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations" + }, + "@aws-cdk/core:target-partitions": { + "recommendedValue": [ + "aws", + "aws-cn" + ], + "explanation": "What regions to include in lookup tables of environment agnostic stacks" + }, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": { + "userValue": true, + "recommendedValue": true, + "explanation": "ECS extensions will automatically add an `awslogs` driver if no logging is specified" + }, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names." + }, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": { + "userValue": true, + "recommendedValue": true, + "explanation": "ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID." + }, + "@aws-cdk/aws-iam:minimizePolicies": { + "userValue": true, + "recommendedValue": true, + "explanation": "Minimize IAM policies by combining Statements" + }, + "@aws-cdk/core:validateSnapshotRemovalPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Error on snapshot removal policies on resources that do not support it." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate key aliases that include the stack name" + }, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist." + }, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict KMS key policy for encrypted Queues a bit more" + }, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make default CloudWatch Role behavior safe for multiple API Gateways in one environment" + }, + "@aws-cdk/core:enablePartitionLiterals": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make ARNs concrete if AWS partition is known" + }, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": { + "userValue": true, + "recommendedValue": true, + "explanation": "Event Rules may only push to encrypted SQS queues in the same account" + }, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": { + "userValue": true, + "recommendedValue": true, + "explanation": "Avoid setting the \"ECS\" deployment controller when adding a circuit breaker" + }, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in." + }, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use S3 Bucket Policy instead of ACLs for Server Access Logging" + }, + "@aws-cdk/aws-route53-patters:useCertificate": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use the official `Certificate` resource instead of `DnsValidatedCertificate`" + }, + "@aws-cdk/customresources:installLatestAwsSdkDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "Whether to install the latest SDK by default in AwsCustomResource" + }, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use unique resource name for Database Proxy" + }, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Remove CloudWatch alarms from deployment group" + }, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include authorizer configuration in the calculation of the API deployment logical ID." + }, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": { + "userValue": true, + "recommendedValue": true, + "explanation": "Define user data for a launch template by default when a machine image is provided." + }, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": { + "userValue": true, + "recommendedValue": true, + "explanation": "SecretTargetAttachments uses the ResourcePolicy of the attached Secret." + }, + "@aws-cdk/aws-redshift:columnId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Whether to use an ID to track Redshift column changes" + }, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable AmazonEMRServicePolicy_v2 managed policies" + }, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict access to the VPC default security group" + }, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a unique id for each RequestValidator added to a method" + }, + "@aws-cdk/aws-kms:aliasNameRef": { + "userValue": true, + "recommendedValue": true, + "explanation": "KMS Alias name and keyArn will have implicit reference to KMS Key" + }, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable grant methods on Aliases imported by name to use kms:ResourceAliases condition" + }, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a launch template when creating an AutoScalingGroup" + }, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include the stack prefix in the stack name generation process" + }, + "@aws-cdk/aws-efs:denyAnonymousAccess": { + "userValue": true, + "recommendedValue": true, + "explanation": "EFS denies anonymous clients accesses" + }, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables support for Multi-AZ with Standby deployment for opensearch domains" + }, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default" + }, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, mount targets will have a stable logicalId that is linked to the associated subnet." + }, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change." + }, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id." + }, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials." + }, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the CodeCommit source action is using the default branch name 'main'." + }, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default value for crossAccountKeys to false." + }, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default pipeline type to V2." + }, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only." + }, + "@aws-cdk/pipelines:reduceAssetRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from PipelineAssetsFileRole trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-eks:nodegroupNameAttribute": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix." + }, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default volume type of the EBS volume will be GP3" + }, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, remove default deployment alarm settings" + }, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default" + }, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack." + }, + "@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": { + "recommendedValue": true, + "explanation": "When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:explicitStackTags": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, stack tags need to be assigned explicitly on a Stack." + }, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": { + "userValue": false, + "recommendedValue": false, + "explanation": "When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)**" + }, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)**" + }, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration" + }, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas" + }, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, initOptions.timeout and resourceSignalTimeout values will be summed together." + }, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a Lambda authorizer Permission created when using GraphqlApi will be properly scoped with a SourceArn." + }, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn`" + }, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values." + }, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, both `@aws-sdk` and `@smithy` packages will be excluded from the Lambda Node.js 18.x runtime to prevent version mismatches in bundled applications." + }, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN." + }, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the BastionHost construct will use the latest Amazon Linux 2023 AMI, instead of Amazon Linux 2." + }, + "@aws-cdk/core:aspectStabilization": { + "recommendedValue": true, + "explanation": "When enabled, a stabilization loop will be run when invoking Aspects during synthesis.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource." + }, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere" + }, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default behaviour of OIDC provider will reject unauthorized connections" + }, + "@aws-cdk/core:enableAdditionalMetadataCollection": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues." + }, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": { + "userValue": false, + "recommendedValue": false, + "explanation": "[Deprecated] When enabled, Lambda will create new inline policies with AddToRolePolicy instead of adding to the Default Policy Statement" + }, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will automatically generate a unique role name that is used for s3 object replication." + }, + "@aws-cdk/pipelines:reduceStageRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from Stage addActions trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-events:requireEventBusPolicySid": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, grantPutEventsTo() will use resource policies with Statement IDs for service principals." + }, + "@aws-cdk/core:aspectPrioritiesMutating": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, Aspects added by the construct library on your behalf will be given a priority of MUTATING." + }, + "@aws-cdk/aws-dynamodb:retainTableReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, table replica will be default to the removal policy of source table unless specified otherwise." + }, + "@aws-cdk/cognito:logUserPoolClientSecretValue": { + "recommendedValue": false, + "explanation": "When disabled, the value of the user pool client secret will not be logged in the custom resource lambda function logs." + }, + "@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope": { + "recommendedValue": true, + "explanation": "When enabled, scopes down the trust policy for the cross-account action role", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resultWriterV2 property of DistributedMap will be used insted of resultWriter" + }, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": { + "userValue": true, + "recommendedValue": true, + "explanation": "Add an S3 trust policy to a KMS key resource policy for SNS subscriptions." + }, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC." + }, + "@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration": { + "recommendedValue": false, + "explanation": "When enabled, use resource IDs for VPC V2 migration" + }, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined." + }, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK creates and manages loggroup for the lambda function" + }, + "@aws-cdk/aws-stepfunctions-tasks:httpInvokeDynamicJsonPathEndpoint": { + "recommendedValue": true, + "explanation": "When enabled, allows using a dynamic apiEndpoint with JSONPath format in HttpInvoke tasks.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-ecs-patterns:uniqueTargetGroupId": { + "recommendedValue": true, + "explanation": "When enabled, ECS patterns will generate unique target group IDs to prevent conflicts during load balancer replacement" + } + } + } + } + }, + "minimumCliVersion": "2.1027.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/tree.json new file mode 100644 index 0000000000000..1e0224f4b03f2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.js.snapshot/tree.json @@ -0,0 +1 @@ +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"},"children":{"integ-custom-repository":{"id":"integ-custom-repository","path":"integ-custom-repository","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"CustomRepo":{"id":"CustomRepo","path":"integ-custom-repository/CustomRepo","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr.Repository","version":"0.0.0","metadata":[{"repositoryName":"*","removalPolicy":"destroy"}]},"children":{"Resource":{"id":"Resource","path":"integ-custom-repository/CustomRepo/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr.CfnRepository","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ECR::Repository","aws:cdk:cloudformation:props":{"repositoryName":"cdk-integ-custom-repo"}}}}},"BasicCustomAsset":{"id":"BasicCustomAsset","path":"integ-custom-repository/BasicCustomAsset","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr_assets.DockerImageAsset","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"integ-custom-repository/BasicCustomAsset/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}}}},"TaggedCustomAsset":{"id":"TaggedCustomAsset","path":"integ-custom-repository/TaggedCustomAsset","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr_assets.DockerImageAsset","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"integ-custom-repository/TaggedCustomAsset/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}}}},"PrefixedCustomAsset":{"id":"PrefixedCustomAsset","path":"integ-custom-repository/PrefixedCustomAsset","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr_assets.DockerImageAsset","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"integ-custom-repository/PrefixedCustomAsset/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}}}},"DefaultRepoCustomTag":{"id":"DefaultRepoCustomTag","path":"integ-custom-repository/DefaultRepoCustomTag","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr_assets.DockerImageAsset","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"integ-custom-repository/DefaultRepoCustomTag/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}},"Repository":{"id":"Repository","path":"integ-custom-repository/DefaultRepoCustomTag/Repository","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr.RepositoryBase","version":"0.0.0","metadata":[]}}}},"DefaultRepoPrefixed":{"id":"DefaultRepoPrefixed","path":"integ-custom-repository/DefaultRepoPrefixed","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr_assets.DockerImageAsset","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"integ-custom-repository/DefaultRepoPrefixed/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}},"Repository":{"id":"Repository","path":"integ-custom-repository/DefaultRepoPrefixed/Repository","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr.RepositoryBase","version":"0.0.0","metadata":[]}}}},"PrecedenceTest":{"id":"PrecedenceTest","path":"integ-custom-repository/PrecedenceTest","constructInfo":{"fqn":"aws-cdk-lib.aws_ecr_assets.DockerImageAsset","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"integ-custom-repository/PrecedenceTest/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}}}},"CustomRepoName":{"id":"CustomRepoName","path":"integ-custom-repository/CustomRepoName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"BasicCustomAssetUri":{"id":"BasicCustomAssetUri","path":"integ-custom-repository/BasicCustomAssetUri","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"TaggedCustomAssetTag":{"id":"TaggedCustomAssetTag","path":"integ-custom-repository/TaggedCustomAssetTag","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"PrefixedCustomAssetTag":{"id":"PrefixedCustomAssetTag","path":"integ-custom-repository/PrefixedCustomAssetTag","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"DefaultRepoCustomTagTag":{"id":"DefaultRepoCustomTagTag","path":"integ-custom-repository/DefaultRepoCustomTagTag","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"DefaultRepoPrefixedTag":{"id":"DefaultRepoPrefixedTag","path":"integ-custom-repository/DefaultRepoPrefixedTag","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"PrecedenceTestTag":{"id":"PrecedenceTestTag","path":"integ-custom-repository/PrecedenceTestTag","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"BootstrapVersion":{"id":"BootstrapVersion","path":"integ-custom-repository/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"integ-custom-repository/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}},"EcrCustomRepositoryTest":{"id":"EcrCustomRepositoryTest","path":"EcrCustomRepositoryTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"},"children":{"DefaultTest":{"id":"DefaultTest","path":"EcrCustomRepositoryTest/DefaultTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"},"children":{"Default":{"id":"Default","path":"EcrCustomRepositoryTest/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"EcrCustomRepositoryTest/DefaultTest/DeployAssert","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"EcrCustomRepositoryTest/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"EcrCustomRepositoryTest/DefaultTest/DeployAssert/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}}}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}}}} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.ts new file mode 100644 index 0000000000000..0ae22b4f518a9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr-assets/test/integ.custom-repository.ts @@ -0,0 +1,89 @@ +#!/usr/bin/env node +import * as path from 'path'; +import * as ecr from 'aws-cdk-lib/aws-ecr'; +import * as cdk from 'aws-cdk-lib'; +import * as assets from 'aws-cdk-lib/aws-ecr-assets'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-custom-repository'); + +// Create a custom ECR repository +const customRepo = new ecr.Repository(stack, 'CustomRepo', { + repositoryName: 'cdk-integ-custom-repo', + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +// Test 1: Basic custom repository usage +const basicCustomAsset = new assets.DockerImageAsset(stack, 'BasicCustomAsset', { + directory: path.join(__dirname, 'demo-image'), + ecrRepository: customRepo, +}); + +// Test 2: Custom repository with explicit tag +const taggedCustomAsset = new assets.DockerImageAsset(stack, 'TaggedCustomAsset', { + directory: path.join(__dirname, 'demo-image'), + ecrRepository: customRepo, + imageTag: 'v1.2.3', +}); + +// Test 3: Custom repository with tag prefix +const prefixedCustomAsset = new assets.DockerImageAsset(stack, 'PrefixedCustomAsset', { + directory: path.join(__dirname, 'demo-image'), + ecrRepository: customRepo, + imageTagPrefix: 'branch-main-', +}); + +// Test 4: Default repository with custom tag +const defaultRepoCustomTag = new assets.DockerImageAsset(stack, 'DefaultRepoCustomTag', { + directory: path.join(__dirname, 'demo-image'), + imageTag: 'custom-tag-v2', +}); + +// Test 5: Default repository with tag prefix +const defaultRepoPrefixed = new assets.DockerImageAsset(stack, 'DefaultRepoPrefixed', { + directory: path.join(__dirname, 'demo-image'), + imageTagPrefix: 'feature-', +}); + +// Test 6: Verify imageTag takes precedence over imageTagPrefix +const precedenceTest = new assets.DockerImageAsset(stack, 'PrecedenceTest', { + directory: path.join(__dirname, 'demo-image'), + ecrRepository: customRepo, + imageTag: 'explicit-wins', + imageTagPrefix: 'ignored-', +}); + +// Output key values for verification +new cdk.CfnOutput(stack, 'CustomRepoName', { + value: customRepo.repositoryName, +}); + +new cdk.CfnOutput(stack, 'BasicCustomAssetUri', { + value: basicCustomAsset.imageUri, +}); + +new cdk.CfnOutput(stack, 'TaggedCustomAssetTag', { + value: taggedCustomAsset.imageTag, +}); + +new cdk.CfnOutput(stack, 'PrefixedCustomAssetTag', { + value: prefixedCustomAsset.imageTag, +}); + +new cdk.CfnOutput(stack, 'DefaultRepoCustomTagTag', { + value: defaultRepoCustomTag.imageTag, +}); + +new cdk.CfnOutput(stack, 'DefaultRepoPrefixedTag', { + value: defaultRepoPrefixed.imageTag, +}); + +new cdk.CfnOutput(stack, 'PrecedenceTestTag', { + value: precedenceTest.imageTag, +}); + +// Register the integration test +new IntegTest(app, 'EcrCustomRepositoryTest', { + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-ecr-assets/README.md b/packages/aws-cdk-lib/aws-ecr-assets/README.md index ce4096cd4210b..ee601a3e52e55 100644 --- a/packages/aws-cdk-lib/aws-ecr-assets/README.md +++ b/packages/aws-cdk-lib/aws-ecr-assets/README.md @@ -171,19 +171,95 @@ using alternative container runtimes like Finch. `DockerImageAsset` is designed for seamless build & consumption of image assets by CDK code deployed to multiple environments through the CDK CLI or through CI/CD workflows. To that end, the ECR repository behind this construct is controlled by the AWS CDK. -The mechanics of where these images are published and how are intentionally kept as an implementation detail, and the construct -does not support customizations such as specifying the ECR repository name or tags. +The mechanics of where these images are published and how are intentionally kept as an implementation detail, and by default the construct itself sets the ECR repository, name, and tags. -We are testing a new experimental synthesizer, the -[App Staging Synthesizer](https://docs.aws.amazon.com/cdk/api/v2/docs/app-staging-synthesizer-alpha-readme.html) that +### Using externally managed ECR repositories + +If you need to publish images to an **existing ECR repository that is managed outside of your CDK application**, +you can specify the `ecrRepository` property to reference an externally created repository. You can also customize +image tags using the `imageTag` or `imageTagPrefix` properties. + +> **Important**: The ECR repository **must already exist** before deploying your CDK application, as CDK publishes +> assets before stack deployment begins. The repository cannot be created in the same CDK application that uses it +> for Docker image assets. + +**Use cases for externally managed repositories:** + +- Using pre-existing ECR repositories with specific lifecycle policies and governance requirements +- Publishing to shared repositories managed by a central platform team +- Integrating with existing CI/CD workflows that require specific repository structures + +```ts +import * as ecr from 'aws-cdk-lib/aws-ecr'; + +// Reference an EXISTING ECR repository (created outside this CDK app) +// Option 1: Import by repository ARN +const existingRepo = ecr.Repository.fromRepositoryArn( + this, + 'ExistingRepo', + 'arn:aws:ecr:us-east-1:123456789012:repository/my-existing-repo' +); + +// Option 2: Import by repository name (must exist in the same account/region) +const existingRepo = ecr.Repository.fromRepositoryName( + this, + 'ExistingRepo', + 'my-existing-repo' +); + +// Use the existing repository for your Docker image asset +const asset = new DockerImageAsset(this, 'MyAsset', { + directory: path.join(__dirname, 'my-image'), + ecrRepository: existingRepo, // Use existing external repository + imageTag: 'v1.2.3', // Custom tag (optional) + // OR + imageTagPrefix: 'feature-branch-', // Tag prefix + asset hash (optional) +}); +``` + +> **Warning**: When using externally managed repositories, you are responsible for: +> - Ensuring the repository exists before deployment +> - Managing repository lifecycle policies +> - Configuring appropriate IAM permissions for CDK to push images +> - Handling image cleanup and retention + +### Custom image tags with CDK-managed repositories + +Even when using the default CDK-managed ECR repositories, you can customize the image tags: + +```ts +// Use custom tag with default CDK-managed repository +const asset = new DockerImageAsset(this, 'MyAsset', { + directory: path.join(__dirname, 'my-image'), + imageTag: 'v1.0.0', // Fixed tag +}); + +// Or use a tag prefix combined with the asset hash +const asset = new DockerImageAsset(this, 'MyAsset', { + directory: path.join(__dirname, 'my-image'), + imageTagPrefix: 'prod-', // Results in: prod- +}); +``` + +> **Note**: Custom tags affect the asset hash, so different tags will create different assets. + +### Alternative solutions for image lifecycle management + +If you need more advanced ECR repository management, consider these CDK-recommended approaches: + +**1. App Staging Synthesizer (Recommended for per-app isolation)** + +The [App Staging Synthesizer](https://docs.aws.amazon.com/cdk/api/v2/docs/app-staging-synthesizer-alpha-readme.html) creates separate support stacks for each CDK application. Unlike the default stack synthesizer, the App Staging Synthesizer creates unique ECR repositories for each `DockerImageAsset`, allowing lifecycle policies to only retain the last `n` images. This is a great way to keep your ECR repositories clean and reduce cost. You can learn more about this feature in [this blog post](https://aws.amazon.com/blogs/devops/enhancing-resource-isolation-in-aws-cdk-with-the-app-staging-synthesizer/). -Alternatively, If you are looking for a way to _publish_ image assets to an ECR repository in your control, you should consider using -[cdklabs/cdk-ecr-deployment], which is able to replicate an image asset from the CDK-controlled ECR repository to a repository of -your choice. +**2. CDK ECR Deployment (For copying images to well-known locations)** + +If you need to publish image assets to a specific ECR repository in your control (e.g., for consumption by other teams), +consider using [cdklabs/cdk-ecr-deployment], which can replicate an image asset from the CDK-controlled ECR repository +to a repository of your choice. Here an example from the [cdklabs/cdk-ecr-deployment] project: diff --git a/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts b/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts index 122c725171dbf..2640fce2f739d 100644 --- a/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts +++ b/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { Construct } from 'constructs'; import { FingerprintOptions, FollowMode, IAsset } from '../../assets'; import * as ecr from '../../aws-ecr'; -import { Annotations, AssetStaging, FeatureFlags, FileFingerprintOptions, IgnoreMode, Stack, SymlinkFollowMode, Token, Stage, CfnResource, Names, ValidationError, UnscopedValidationError } from '../../core'; +import { Annotations, AssetStaging, FeatureFlags, FileFingerprintOptions, IgnoreMode, Stack, SymlinkFollowMode, Token, Stage, CfnResource, Names, ValidationError, UnscopedValidationError, DockerImageAssetLocation, DockerImageAssetSource } from '../../core'; import { propertyInjectable } from '../../core/lib/prop-injectable'; import * as cxapi from '../../cx-api'; @@ -202,6 +202,27 @@ export interface DockerImageAssetOptions extends FingerprintOptions, FileFingerp */ readonly repositoryName?: string; + /** + * ECR repository where this image should be published + * + * @default - use the default ECR repository for CDK assets + */ + readonly ecrRepository?: ecr.IRepository; + + /** + * Custom Docker image tag + * + * @default - asset hash will be used as the image tag + */ + readonly imageTag?: string; + + /** + * Docker image tag prefix + * + * @default - no prefix, uses synthesizer default or empty string + */ + readonly imageTagPrefix?: string; + /** * Build args to pass to the `docker build` command. * @@ -477,7 +498,7 @@ export class DockerImageAsset extends Construct implements IAsset { const defaultIgnoreMode = FeatureFlags.of(this).isEnabled(cxapi.DOCKER_IGNORE_SUPPORT) ? IgnoreMode.DOCKER : IgnoreMode.GLOB; - let ignoreMode = props.ignoreMode ?? defaultIgnoreMode; + const ignoreMode = props.ignoreMode ?? defaultIgnoreMode; let exclude: string[] = props.exclude || []; @@ -518,6 +539,12 @@ export class DockerImageAsset extends Construct implements IAsset { if (props.invalidation?.platform !== false && props.platform) { extraHash.platform = props.platform; } if (props.invalidation?.outputs !== false && props.outputs) { extraHash.outputs = props.outputs; } + // Include custom repository and tagging properties in hash calculation for deterministic asset naming + // While these don't affect image content, they ensure consistent asset identification + if (props.ecrRepository) { extraHash.ecrRepository = props.ecrRepository.repositoryName; } + if (props.imageTag) { extraHash.imageTag = props.imageTag; } + if (props.imageTagPrefix) { extraHash.imageTagPrefix = props.imageTagPrefix; } + // 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). @@ -549,7 +576,9 @@ export class DockerImageAsset extends Construct implements IAsset { this.dockerCacheTo = props.cacheTo; this.dockerCacheDisabled = props.cacheDisabled; - const location = stack.synthesizer.addDockerImageAsset({ + // Handle custom ECR repository or use default synthesizer + let location: DockerImageAssetLocation; + const locationProps: DockerImageAssetSource = { directoryName: this.assetPath, assetName: this.assetName, dockerBuildArgs: this.dockerBuildArgs, @@ -565,9 +594,25 @@ export class DockerImageAsset extends Construct implements IAsset { dockerCacheTo: this.dockerCacheTo, dockerCacheDisabled: this.dockerCacheDisabled, displayName: props.displayName ?? props.assetName ?? Names.stackRelativeConstructPath(this), - }); + // Pass custom tag properties to synthesizer (only used if not using custom repository) + imageTag: props.ecrRepository ? undefined : props.imageTag, + imageTagPrefix: props.ecrRepository ? undefined : props.imageTagPrefix, + }; + if (props.ecrRepository) { + // Custom repository: create location manually + const customTag = props.imageTag ?? `${props.imageTagPrefix ?? ''}${this.assetHash}`; + location = { + repositoryName: props.ecrRepository.repositoryName, + imageUri: props.ecrRepository.repositoryUriForTag(customTag), + imageTag: customTag, + }; + this.repository = props.ecrRepository; + } else { + // Default repository: use synthesizer (which now respects per-asset imageTag/imageTagPrefix) + location = stack.synthesizer.addDockerImageAsset(locationProps); + this.repository = ecr.Repository.fromRepositoryName(this, 'Repository', location.repositoryName); + } - this.repository = ecr.Repository.fromRepositoryName(this, 'Repository', location.repositoryName); this.imageUri = location.imageUri; this.imageTag = location.imageTag ?? this.assetHash; } diff --git a/packages/aws-cdk-lib/aws-ecr-assets/test/custom-repository.test.ts b/packages/aws-cdk-lib/aws-ecr-assets/test/custom-repository.test.ts new file mode 100644 index 0000000000000..4d47c7bb838a7 --- /dev/null +++ b/packages/aws-cdk-lib/aws-ecr-assets/test/custom-repository.test.ts @@ -0,0 +1,298 @@ +import * as path from 'path'; +import { Template, Match } from '../../assertions'; +import * as ecr from '../../aws-ecr'; +import { App, Stack } from '../../core'; +import { DockerImageAsset } from '../lib'; + +describe('DockerImageAsset Custom Repository Support', () => { + let app: App; + let stack: Stack; + let repository: ecr.Repository; + + beforeEach(() => { + app = new App(); + stack = new Stack(app, 'TestStack'); + repository = new ecr.Repository(stack, 'CustomRepo', { + repositoryName: 'my-custom-repo', + }); + }); + + test('should support custom ECR repository', () => { + // GIVEN + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: repository, + }); + + // THEN + expect(asset.repository).toBe(repository); + expect(asset.imageTag).toBe(asset.assetHash); + + // Check CloudFormation template structure + const template = Template.fromStack(stack); + + // Should reference the custom repository in the template + template.hasResourceProperties('AWS::ECR::Repository', { + RepositoryName: 'my-custom-repo', + }); + + // Asset should be properly configured + expect(asset.imageUri).toBeDefined(); + expect(typeof asset.imageUri).toBe('string'); + }); + + test('should support custom image tag with custom repository', () => { + // GIVEN + const customTag = 'v1.2.3'; + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: repository, + imageTag: customTag, + }); + + // THEN + expect(asset.repository).toBe(repository); + expect(asset.imageTag).toBe(customTag); + + // Check CloudFormation template structure + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::ECR::Repository', { + RepositoryName: 'my-custom-repo', + }); + + // Asset should use the custom tag + expect(asset.imageUri).toBeDefined(); + expect(asset.imageTag).toBe('v1.2.3'); + }); + + test('should support custom image tag prefix with custom repository', () => { + // GIVEN + const tagPrefix = 'feature-'; + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: repository, + imageTagPrefix: tagPrefix, + }); + + // THEN + expect(asset.repository).toBe(repository); + expect(asset.imageTag).toBe(`${tagPrefix}${asset.assetHash}`); + + // Check CloudFormation template structure + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::ECR::Repository', { + RepositoryName: 'my-custom-repo', + }); + + // Asset should use the prefixed tag + expect(asset.imageUri).toBeDefined(); + expect(asset.imageTag.startsWith(tagPrefix)).toBe(true); + }); + + test('should support custom tag prefix with default repository', () => { + // GIVEN + const tagPrefix = 'branch-main-'; + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTagPrefix: tagPrefix, + }); + + // THEN + expect(asset.imageTag).toBe(`${tagPrefix}${asset.assetHash}`); + expect(asset.imageTag.startsWith(tagPrefix)).toBe(true); + + // Asset should use the prefixed tag + expect(asset.imageUri).toBeDefined(); + expect(typeof asset.imageUri).toBe('string'); + }); + + test('should support custom tag with default repository', () => { + // GIVEN + const customTag = 'release-candidate'; + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTag: customTag, + }); + + // THEN + expect(asset.imageTag).toBe(customTag); + + // Asset should use the custom tag + expect(asset.imageUri).toBeDefined(); + expect(typeof asset.imageUri).toBe('string'); + }); + + test('should include custom repository in asset hash', () => { + // GIVEN + const asset1 = new DockerImageAsset(stack, 'Asset1', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + }); + + const asset2 = new DockerImageAsset(stack, 'Asset2', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: repository, + }); + + // THEN - assets should have different hashes due to different repositories + expect(asset1.assetHash).not.toBe(asset2.assetHash); + }); + + test('should include custom tag in asset hash', () => { + // GIVEN + const asset1 = new DockerImageAsset(stack, 'Asset1', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTag: 'tag1', + }); + + const asset2 = new DockerImageAsset(stack, 'Asset2', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTag: 'tag2', + }); + + // THEN - assets should have different hashes due to different tags + expect(asset1.assetHash).not.toBe(asset2.assetHash); + }); + + test('should include custom tag prefix in asset hash', () => { + // GIVEN + const asset1 = new DockerImageAsset(stack, 'Asset1', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTagPrefix: 'prefix1-', + }); + + const asset2 = new DockerImageAsset(stack, 'Asset2', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTagPrefix: 'prefix2-', + }); + + // THEN - assets should have different hashes due to different tag prefixes + expect(asset1.assetHash).not.toBe(asset2.assetHash); + }); + + test('imageTag takes precedence over imageTagPrefix', () => { + // GIVEN + const customTag = 'explicit-tag'; + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: repository, + imageTag: customTag, + imageTagPrefix: 'should-be-ignored-', + }); + + // THEN + expect(asset.imageTag).toBe(customTag); + expect(asset.imageUri).toContain(`:${customTag}`); + }); + + test('should maintain backward compatibility', () => { + // GIVEN + const asset = new DockerImageAsset(stack, 'MyAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + }); + + // THEN - should work exactly like before + expect(asset.repository).toBeDefined(); + expect(asset.imageUri).toBeDefined(); + expect(asset.imageTag).toBe(asset.assetHash); + }); + + test('should handle complex repository names and tags', () => { + // GIVEN + const complexStack = new Stack(new App(), 'ComplexStack'); + const complexRepo = new ecr.Repository(complexStack, 'ComplexRepo', { + repositoryName: 'my-org/my-project/backend-api', + }); + + const complexTag = 'v2.1.0-beta.1+build.123'; + const asset = new DockerImageAsset(complexStack, 'ComplexAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: complexRepo, + imageTag: complexTag, + }); + + // THEN + expect(asset.repository).toBe(complexRepo); + expect(asset.imageTag).toBe(complexTag); + + // Check CloudFormation template structure + const template = Template.fromStack(complexStack); + template.hasResourceProperties('AWS::ECR::Repository', { + RepositoryName: 'my-org/my-project/backend-api', + }); + }); + + test('should handle edge case: empty tag prefix', () => { + // GIVEN + const asset = new DockerImageAsset(stack, 'EmptyPrefixAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + imageTagPrefix: '', + }); + + // THEN - empty prefix should behave like no prefix + expect(asset.imageTag).toBe(asset.assetHash); + expect(asset.imageUri).toBeDefined(); + }); + + test('should handle cross-stack repository references', () => { + // GIVEN + const repoStack = new Stack(app, 'RepoStack'); + const assetStack = new Stack(app, 'AssetStack'); + + const crossStackRepo = new ecr.Repository(repoStack, 'CrossStackRepo', { + repositoryName: 'cross-stack-repo', + }); + + const asset = new DockerImageAsset(assetStack, 'CrossStackAsset', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: crossStackRepo, + imageTag: 'cross-stack-tag', + }); + + // THEN + expect(asset.repository).toBe(crossStackRepo); + expect(asset.imageTag).toBe('cross-stack-tag'); + + // Both stacks should be valid + const repoTemplate = Template.fromStack(repoStack); + const assetTemplate = Template.fromStack(assetStack); + + repoTemplate.hasResourceProperties('AWS::ECR::Repository', { + RepositoryName: 'cross-stack-repo', + }); + + // Asset stack should reference the repository from the other stack + expect(asset.imageUri).toBeDefined(); + }); + + test('should properly isolate different assets with same repository', () => { + // GIVEN + const sharedRepo = new ecr.Repository(stack, 'SharedRepo', { + repositoryName: 'shared-repo', + }); + + const asset1 = new DockerImageAsset(stack, 'Asset1', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: sharedRepo, + imageTag: 'version-1', + }); + + const asset2 = new DockerImageAsset(stack, 'Asset2', { + directory: path.join(__dirname, 'fixtures/custom-dockerfile'), + ecrRepository: sharedRepo, + imageTag: 'version-2', + }); + + // THEN - should be different assets with different tags but same repo + expect(asset1.repository).toBe(sharedRepo); + expect(asset2.repository).toBe(sharedRepo); + expect(asset1.imageTag).toBe('version-1'); + expect(asset2.imageTag).toBe('version-2'); + expect(asset1.imageUri).not.toBe(asset2.imageUri); + + // Template should have one repository but references to both assets + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::ECR::Repository', { + RepositoryName: 'shared-repo', + }); + }); +}); diff --git a/packages/aws-cdk-lib/aws-ecr-assets/test/fixtures/custom-dockerfile/Dockerfile b/packages/aws-cdk-lib/aws-ecr-assets/test/fixtures/custom-dockerfile/Dockerfile new file mode 100644 index 0000000000000..44cbbb71939a2 --- /dev/null +++ b/packages/aws-cdk-lib/aws-ecr-assets/test/fixtures/custom-dockerfile/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +# Minimal test Dockerfile for aws-ecr-assets custom repository tests diff --git a/packages/aws-cdk-lib/core/lib/assets.ts b/packages/aws-cdk-lib/core/lib/assets.ts index e6c443ef3ac3b..df1d067167a4f 100644 --- a/packages/aws-cdk-lib/core/lib/assets.ts +++ b/packages/aws-cdk-lib/core/lib/assets.ts @@ -311,6 +311,28 @@ export interface DockerImageAssetSource { * @default - The asset hash is used to display the asset */ readonly displayName?: string; + + /** + * The tag to use when tagging the Docker image. + * + * When specified, this tag will be used instead of the default tag (asset hash). + * This is useful for creating meaningful tags for container image scanning and management. + * + * @default - The asset hash is used as the image tag + */ + readonly imageTag?: string; + + /** + * The prefix to prepend to the asset hash to create the Docker image tag. + * + * When specified, the image will be tagged with `${imageTagPrefix}${assetHash}`. + * This is useful for adding contextual information to image tags. + * + * If both `imageTag` and `imageTagPrefix` are specified, `imageTag` takes precedence. + * + * @default - No prefix is used + */ + readonly imageTagPrefix?: string; } /** diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index 1b8bcc9a23649..cdb0bb18e32e7 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts @@ -63,7 +63,9 @@ export class AssetManifestBuilder { options?: AddDockerImageAssetOptions, ) { validateDockerImageAssetSource(asset); - const imageTag = `${target.dockerTagPrefix ?? ''}${asset.sourceHash}`; + + // Use per-asset tag if specified, otherwise use prefix + hash + const imageTag = asset.imageTag ?? `${asset.imageTagPrefix ?? target.dockerTagPrefix ?? ''}${asset.sourceHash}`; // Add to manifest const sourceHash = asset.assetName ? `${asset.assetName}-${asset.sourceHash}` : asset.sourceHash;