diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b1ba8d9db8932..6cfe86ced9ba7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,7 +8,16 @@ Closes #. ### Description of changes - + + +### Describe any new or updated permissions being added + + + ### Description of how you validated changes diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/AutoRetryLimitDefaultTestDeployAssertF296FC2B.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/AutoRetryLimitDefaultTestDeployAssertF296FC2B.assets.json new file mode 100644 index 0000000000000..3fee61e77a369 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/AutoRetryLimitDefaultTestDeployAssertF296FC2B.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "AutoRetryLimitDefaultTestDeployAssertF296FC2B.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "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-codebuild/test/integ.project-auto-retry-limit.js.snapshot/AutoRetryLimitDefaultTestDeployAssertF296FC2B.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/AutoRetryLimitDefaultTestDeployAssertF296FC2B.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/AutoRetryLimitDefaultTestDeployAssertF296FC2B.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-codebuild/test/integ.project-auto-retry-limit.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c6e612584e352 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/codebuild-auto-retry-limit.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/codebuild-auto-retry-limit.assets.json new file mode 100644 index 0000000000000..58903dcec1d83 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/codebuild-auto-retry-limit.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "e02fc46c37820bb0bd35de3aab438414b03b5d3ca755f27a5e59d394a99400eb": { + "source": { + "path": "codebuild-auto-retry-limit.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "e02fc46c37820bb0bd35de3aab438414b03b5d3ca755f27a5e59d394a99400eb.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-codebuild/test/integ.project-auto-retry-limit.js.snapshot/codebuild-auto-retry-limit.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/codebuild-auto-retry-limit.template.json new file mode 100644 index 0000000000000..b7a2c71fba536 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/codebuild-auto-retry-limit.template.json @@ -0,0 +1,237 @@ +{ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyProjectRole9BBE5233": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyProjectRoleDefaultPolicyB19B7C29": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/path/to/my/source.zip" + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "MyProject39F7B0AE" + }, + "-*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyProjectRoleDefaultPolicyB19B7C29", + "Roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "MyProject39F7B0AE": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "AutoRetryLimit": 2, + "Cache": { + "Type": "NO_CACHE" + }, + "EncryptionKey": "alias/aws/s3", + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:7.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Source": { + "Location": { + "Fn::Join": [ + "", + [ + { + "Ref": "MyBucketF68F3FF0" + }, + "/path/to/my/source.zip" + ] + ] + }, + "Type": "S3" + } + } + } + }, + "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-codebuild/test/integ.project-auto-retry-limit.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/integ.json new file mode 100644 index 0000000000000..26c66e4a55629 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "38.0.1", + "testCases": { + "AutoRetryLimit/DefaultTest": { + "stacks": [ + "codebuild-auto-retry-limit" + ], + "stackUpdateWorkflow": true, + "assertionStack": "AutoRetryLimit/DefaultTest/DeployAssert", + "assertionStackName": "AutoRetryLimitDefaultTestDeployAssertF296FC2B" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/manifest.json new file mode 100644 index 0000000000000..12da44b4727db --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "version": "38.0.1", + "artifacts": { + "codebuild-auto-retry-limit.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "codebuild-auto-retry-limit.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "codebuild-auto-retry-limit": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "codebuild-auto-retry-limit.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}/e02fc46c37820bb0bd35de3aab438414b03b5d3ca755f27a5e59d394a99400eb.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "codebuild-auto-retry-limit.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": [ + "codebuild-auto-retry-limit.assets" + ], + "metadata": { + "/codebuild-auto-retry-limit/MyBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyBucketF68F3FF0" + } + ], + "/codebuild-auto-retry-limit/MyProject/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProjectRole9BBE5233" + } + ], + "/codebuild-auto-retry-limit/MyProject/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProjectRoleDefaultPolicyB19B7C29" + } + ], + "/codebuild-auto-retry-limit/MyProject/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProject39F7B0AE" + } + ], + "/codebuild-auto-retry-limit/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/codebuild-auto-retry-limit/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "codebuild-auto-retry-limit" + }, + "AutoRetryLimitDefaultTestDeployAssertF296FC2B.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "AutoRetryLimitDefaultTestDeployAssertF296FC2B.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "AutoRetryLimitDefaultTestDeployAssertF296FC2B": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "AutoRetryLimitDefaultTestDeployAssertF296FC2B.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": [ + "AutoRetryLimitDefaultTestDeployAssertF296FC2B.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": [ + "AutoRetryLimitDefaultTestDeployAssertF296FC2B.assets" + ], + "metadata": { + "/AutoRetryLimit/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/AutoRetryLimit/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "AutoRetryLimit/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/tree.json new file mode 100644 index 0000000000000..9559cec701ebd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.js.snapshot/tree.json @@ -0,0 +1,379 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "codebuild-auto-retry-limit": { + "id": "codebuild-auto-retry-limit", + "path": "codebuild-auto-retry-limit", + "children": { + "MyBucket": { + "id": "MyBucket", + "path": "codebuild-auto-retry-limit/MyBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "codebuild-auto-retry-limit/MyBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" + } + }, + "MyProject": { + "id": "MyProject", + "path": "codebuild-auto-retry-limit/MyProject", + "children": { + "Role": { + "id": "Role", + "path": "codebuild-auto-retry-limit/MyProject/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "codebuild-auto-retry-limit/MyProject/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "codebuild-auto-retry-limit/MyProject/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "codebuild-auto-retry-limit/MyProject/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "codebuild-auto-retry-limit/MyProject/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/path/to/my/source.zip" + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "MyProject39F7B0AE" + }, + "-*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "MyProjectRoleDefaultPolicyB19B7C29", + "roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "codebuild-auto-retry-limit/MyProject/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeBuild::Project", + "aws:cdk:cloudformation:props": { + "artifacts": { + "type": "NO_ARTIFACTS" + }, + "autoRetryLimit": 2, + "cache": { + "type": "NO_CACHE" + }, + "encryptionKey": "alias/aws/s3", + "environment": { + "type": "LINUX_CONTAINER", + "image": "aws/codebuild/standard:7.0", + "imagePullCredentialsType": "CODEBUILD", + "privilegedMode": false, + "computeType": "BUILD_GENERAL1_SMALL" + }, + "serviceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "source": { + "type": "S3", + "location": { + "Fn::Join": [ + "", + [ + { + "Ref": "MyBucketF68F3FF0" + }, + "/path/to/my/source.zip" + ] + ] + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_codebuild.CfnProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_codebuild.Project", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "codebuild-auto-retry-limit/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "codebuild-auto-retry-limit/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "AutoRetryLimit": { + "id": "AutoRetryLimit", + "path": "AutoRetryLimit", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "AutoRetryLimit/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "AutoRetryLimit/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "AutoRetryLimit/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "AutoRetryLimit/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "AutoRetryLimit/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.ts new file mode 100644 index 0000000000000..44aebde6dd008 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.project-auto-retry-limit.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node +import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as codebuild from 'aws-cdk-lib/aws-codebuild'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App(); + +class AutoRetryLimitStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const bucket = new s3.Bucket(this, 'MyBucket', { + removalPolicy: RemovalPolicy.DESTROY, + }); + new codebuild.Project(this, 'MyProject', { + source: codebuild.Source.s3({ + bucket, + path: 'path/to/my/source.zip', + }), + autoRetryLimit: 2, + }); + } +} + +const autoRetryLimitTest = new AutoRetryLimitStack(app, 'codebuild-auto-retry-limit'); + +new IntegTest(app, 'AutoRetryLimit', { + testCases: [autoRetryLimitTest], + stackUpdateWorkflow: true, +}); diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53-targets/test/integ.elastic-beanstalk-environment-target.js.snapshot/asset.02e8c4fbaddae67c5789ab6e8ef26eb226f048244bda7fdeb2ecaf8bda19eb9a.elastic-beanstalk-environment-target-assets/package-lock.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53-targets/test/integ.elastic-beanstalk-environment-target.js.snapshot/asset.02e8c4fbaddae67c5789ab6e8ef26eb226f048244bda7fdeb2ecaf8bda19eb9a.elastic-beanstalk-environment-target-assets/package-lock.json index f7b7b98ab1f09..5a501173cbb5d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-route53-targets/test/integ.elastic-beanstalk-environment-target.js.snapshot/asset.02e8c4fbaddae67c5789ab6e8ef26eb226f048244bda7fdeb2ecaf8bda19eb9a.elastic-beanstalk-environment-target-assets/package-lock.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53-targets/test/integ.elastic-beanstalk-environment-target.js.snapshot/asset.02e8c4fbaddae67c5789ab6e8ef26eb226f048244bda7fdeb2ecaf8bda19eb9a.elastic-beanstalk-environment-target-assets/package-lock.json @@ -196,9 +196,10 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -219,7 +220,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -234,6 +235,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { @@ -487,9 +492,10 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/proxy-addr": { "version": "2.0.7", diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index beab2ec9a1cad..56215590f4295 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -23,8 +23,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-cdk/aws-service-spec": "^0.1.37", - "@aws-cdk/service-spec-types": "^0.0.104", + "@aws-cdk/aws-service-spec": "^0.1.38", + "@aws-cdk/service-spec-types": "^0.0.105", "chalk": "^4", "diff": "^5.2.0", "fast-deep-equal": "^3.1.3", diff --git a/packages/@aws-cdk/integ-runner/package.json b/packages/@aws-cdk/integ-runner/package.json index ea18202dc0dd9..898120152b47f 100644 --- a/packages/@aws-cdk/integ-runner/package.json +++ b/packages/@aws-cdk/integ-runner/package.json @@ -74,7 +74,7 @@ "@aws-cdk/cloud-assembly-schema": "^38.0.0", "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "@aws-cdk/aws-service-spec": "^0.1.37", + "@aws-cdk/aws-service-spec": "^0.1.38", "cdk-assets": "3.0.0-rc.32", "@aws-cdk/cdk-cli-wrapper": "0.0.0", "aws-cdk": "0.0.0", diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts index 35f081faafc6b..9bed65f41f48e 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts @@ -44,6 +44,13 @@ export interface IDistribution extends IResource { */ readonly distributionId: string; + /** + * The distribution ARN for this distribution. + * + * @attribute + */ + readonly distributionArn: string; + /** * Adds an IAM policy statement associated with this distribution to an IAM * principal's policy. @@ -291,6 +298,9 @@ export class Distribution extends Resource implements IDistribution { this.distributionId = attrs.distributionId; } + public get distributionArn(): string { + return formatDistributionArn(this); + } public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { return iam.Grant.addToPrincipal({ grantee, actions, resourceArns: [formatDistributionArn(this)] }); } @@ -389,6 +399,10 @@ export class Distribution extends Resource implements IDistribution { } } + public get distributionArn(): string { + return formatDistributionArn(this); + } + /** * Return the given named metric for this Distribution */ diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts index 530d6ac77149c..fccd2fa9e6020 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts @@ -761,6 +761,9 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu this.distributionId = attrs.distributionId; } + public get distributionArn(): string { + return formatDistributionArn(this); + } public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { return iam.Grant.addToPrincipal({ grantee, actions, resourceArns: [formatDistributionArn(this)] }); } @@ -992,6 +995,10 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu this.distributionId = distribution.ref; } + public get distributionArn(): string { + return formatDistributionArn(this); + } + /** * Adds an IAM policy statement associated with this distribution to an IAM * principal's policy. diff --git a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts index 470af4c0a0c09..927ba5861b368 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts @@ -6,7 +6,7 @@ import * as iam from '../../aws-iam'; import * as kinesis from '../../aws-kinesis'; import * as lambda from '../../aws-lambda'; import * as s3 from '../../aws-s3'; -import { App, Duration, Stack } from '../../core'; +import { App, Aws, Duration, Stack } from '../../core'; import { CfnDistribution, Distribution, @@ -36,7 +36,7 @@ beforeEach(() => { test('minimal example renders correctly', () => { const origin = defaultOrigin(); - new Distribution(stack, 'MyDist', { defaultBehavior: { origin } }); + const dist = new Distribution(stack, 'MyDist', { defaultBehavior: { origin } }); Template.fromStack(stack).hasResourceProperties('AWS::CloudFront::Distribution', { DistributionConfig: { @@ -58,6 +58,19 @@ test('minimal example renders correctly', () => { }], }, }); + + expect(dist.distributionArn).toEqual(`arn:${Aws.PARTITION}:cloudfront::1234:distribution/${dist.distributionId}`); +}); + +test('existing distributions can be imported', () => { + const dist = Distribution.fromDistributionAttributes(stack, 'ImportedDist', { + domainName: 'd111111abcdef8.cloudfront.net', + distributionId: '012345ABCDEF', + }); + + expect(dist.distributionDomainName).toEqual('d111111abcdef8.cloudfront.net'); + expect(dist.distributionId).toEqual('012345ABCDEF'); + expect(dist.distributionArn).toEqual(`arn:${Aws.PARTITION}:cloudfront::1234:distribution/012345ABCDEF`); }); test('exhaustive example of props renders correctly and SSL method sni-only', () => { diff --git a/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts b/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts index 7202b28052e84..5da37d06e8280 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts @@ -133,7 +133,7 @@ describe('web distribution', () => { const stack = new cdk.Stack(); const sourceBucket = new s3.Bucket(stack, 'Bucket'); - new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { + const dist = new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { originConfigs: [ { s3OriginSource: { @@ -204,6 +204,7 @@ describe('web distribution', () => { }, }); + expect(dist.distributionArn).toEqual(`arn:${cdk.Aws.PARTITION}:cloudfront::${cdk.Aws.ACCOUNT_ID}:distribution/${dist.distributionId}`); }); test('can disable distribution', () => { @@ -1722,6 +1723,7 @@ added the ellipsis so a user would know there was more to r...`, expect(dist.distributionDomainName).toEqual('d111111abcdef8.cloudfront.net'); expect(dist.distributionId).toEqual('012345ABCDEF'); + expect(dist.distributionArn).toEqual(`arn:${cdk.Aws.PARTITION}:cloudfront::${cdk.Aws.ACCOUNT_ID}:distribution/012345ABCDEF`); }); }); diff --git a/packages/aws-cdk-lib/aws-codebuild/README.md b/packages/aws-cdk-lib/aws-codebuild/README.md index 28f22e0e8b461..238af9d4af946 100644 --- a/packages/aws-cdk-lib/aws-codebuild/README.md +++ b/packages/aws-cdk-lib/aws-codebuild/README.md @@ -482,7 +482,7 @@ new codebuild.Project(this, 'Project', { environment: { fleet: codebuild.Fleet.fromFleetArn( this, 'SharedFleet', - 'arn:aws:codebuild:us-east-1:123456789012:fleet/MyFleet:ed0d0823-e38a-4c10-90a1-1bf25f50fa76', + 'arn:aws:codebuild:us-east-1:123456789012:fleet/MyFleet:ed0d0823-e38a-4c10-90a1-1bf25f50fa76', ), buildImage: codebuild.LinuxBuildImage.STANDARD_7_0, }, @@ -981,4 +981,17 @@ Examples: new codebuild.Project(this, 'MyProject', { visibility: codebuild.ProjectVisibility.PUBLIC_READ, }); -``` \ No newline at end of file +``` + +## Auto retry limit +You can automatically retry your builds in AWS CodeBuild by setting `autoRetryLimit` property. + +With auto-retry enabled, CodeBuild will automatically call RetryBuild using the project's service role after a failed build up to a specified limit. + +For example, if the auto-retry limit is set to two, CodeBuild will call the RetryBuild API to automatically retry your build for up to two additional times. + +```ts +new codebuild.Project(this, 'MyProject', { + autoRetryLimit: 2, +}); +``` diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/project.ts b/packages/aws-cdk-lib/aws-codebuild/lib/project.ts index 42bdca951ba99..ff6246f98b192 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/project.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/project.ts @@ -734,6 +734,14 @@ export interface CommonProjectProps { * @default - no visibility is set */ readonly visibility?: ProjectVisibility; + + /** + * CodeBuild will automatically call retry build using the project's service role up to the auto-retry limit. + * `autoRetryLimit` must be between 0 and 10. + * + * @default - no retry + */ + readonly autoRetryLimit?: number; } export interface ProjectProps extends CommonProjectProps { @@ -1107,6 +1115,12 @@ export class Project extends ProjectBase { this.addFileSystemLocation(fileSystemLocation); } + if (!Token.isUnresolved(props.autoRetryLimit) && (props.autoRetryLimit !== undefined)) { + if (props.autoRetryLimit < 0 || props.autoRetryLimit > 10) { + throw new Error(`autoRetryLimit must be a value between 0 and 10, got ${props.autoRetryLimit}.`); + }; + }; + const resource = new CfnProject(this, 'Resource', { description: props.description, source: { @@ -1143,6 +1157,7 @@ export class Project extends ProjectBase { return config; }, }), + autoRetryLimit: props.autoRetryLimit, }); this.addVpcRequiredPermissions(props, resource); diff --git a/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts b/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts index 0379dfae7dbcd..f1438ac0c4cbb 100644 --- a/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts +++ b/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts @@ -2346,3 +2346,38 @@ describe('can be imported', () => { expect(project.env.region).toEqual('us-west-2'); }); }); + +test('can set autoRetryLimit', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new codebuild.Project(stack, 'Project', { + source: codebuild.Source.s3({ + bucket: new s3.Bucket(stack, 'Bucket'), + path: 'path', + }), + buildSpec: codebuild.BuildSpec.fromSourceFilename('hello.yml'), + autoRetryLimit: 2, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + AutoRetryLimit: 2, + }); +}); + +test.each([-1, 15])('throws when autoRetryLimit is invalid', (autoRetryLimit) => { + const stack = new cdk.Stack(); + + expect(() => { + new codebuild.Project(stack, 'Project', { + source: codebuild.Source.s3({ + bucket: new s3.Bucket(stack, 'Bucket'), + path: 'path', + }), + buildSpec: codebuild.BuildSpec.fromSourceFilename('hello.yml'), + autoRetryLimit, + }); + }).toThrow(`autoRetryLimit must be a value between 0 and 10, got ${autoRetryLimit}.`); +}); diff --git a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts index f86b2d11b5b9f..9416905ef17bc 100644 --- a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts +++ b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts @@ -49,6 +49,15 @@ export interface IRepository extends IResource { */ readonly repositoryUri: string; + /** + * The URI of this repository's registry: + * + * ACCOUNT.dkr.ecr.REGION.amazonaws.com + * + * @attribute + */ + readonly registryUri: string; + /** * Returns the URI of the repository for a certain tag. Can be used in `docker push/pull`. * @@ -191,6 +200,17 @@ export abstract class RepositoryBase extends Resource implements IRepository { return this.repositoryUriForTag(); } + /** + * The URI of this repository's registry: + * + * ACCOUNT.dkr.ecr.REGION.amazonaws.com + * + */ + public get registryUri(): string { + const parts = this.stack.splitArn(this.repositoryArn, ArnFormat.SLASH_RESOURCE_NAME); + return `${parts.account}.dkr.ecr.${parts.region}.${this.stack.urlSuffix}`; + } + /** * Returns the URL of the repository. Can be used in `docker push/pull`. * 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 0c703ad7763da..e5422fc17531a 100644 --- a/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts +++ b/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts @@ -308,6 +308,30 @@ describe('repository', () => { }); }); + test('calculate registry URI', () => { + // GIVEN + const stack = new cdk.Stack(); + const repo = new ecr.Repository(stack, 'Repo'); + + new cdk.CfnOutput(stack, 'RegistryUri', { + value: repo.registryUri, + }); + + // THEN + const arnSplit = { 'Fn::Split': [':', { 'Fn::GetAtt': ['Repo02AC86CF', 'Arn'] }] }; + Template.fromStack(stack).hasOutput('*', { + 'Value': { + 'Fn::Join': ['', [ + { 'Fn::Select': [4, arnSplit] }, + '.dkr.ecr.', + { 'Fn::Select': [3, arnSplit] }, + '.', + { Ref: 'AWS::URLSuffix' }, + ]], + }, + }); + }); + test('import with concrete arn', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/aws-cdk-lib/aws-eks/lib/cluster.ts b/packages/aws-cdk-lib/aws-eks/lib/cluster.ts index 2c624a477c9b8..8f66f4efa8b3a 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/cluster.ts @@ -1,7 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; import { Construct, Node } from 'constructs'; -import * as semver from 'semver'; import * as YAML from 'yaml'; import { IAccessPolicy, IAccessEntry, AccessEntry, AccessPolicy, AccessScopeType } from './access-entry'; import { IAddon, Addon } from './addon'; @@ -1579,9 +1578,8 @@ export class Cluster extends ClusterBase { this.prune = props.prune ?? true; this.vpc = props.vpc || new ec2.Vpc(this, 'DefaultVpc'); - const kubectlVersion = new semver.SemVer(`${props.version.version}.0`); - if (semver.gte(kubectlVersion, '1.22.0') && !props.kubectlLayer) { - Annotations.of(this).addWarningV2('@aws-cdk/aws-eks:clusterKubectlLayerNotSpecified', `You created a cluster with Kubernetes Version ${props.version.version} without specifying the kubectlLayer property. This may cause failures as the kubectl version provided with aws-cdk-lib is 1.20, which is only guaranteed to be compatible with Kubernetes versions 1.19-1.21. Please provide a kubectlLayer from @aws-cdk/lambda-layer-kubectl-v${kubectlVersion.minor}.`); + if (!props.kubectlLayer) { + Annotations.of(this).addWarningV2('@aws-cdk/aws-eks:clusterKubectlLayerNotSpecified', `You created a cluster with Kubernetes Version ${props.version.version} without specifying the kubectlLayer property. The property will become required instead of optional in 2025 Jan. Please update your CDK code to provide a kubectlLayer.`); }; this.version = props.version; diff --git a/packages/aws-cdk-lib/aws-eks/test/cluster.test.ts b/packages/aws-cdk-lib/aws-eks/test/cluster.test.ts index 254987e61065e..b539eb46d0a34 100644 --- a/packages/aws-cdk-lib/aws-eks/test/cluster.test.ts +++ b/packages/aws-cdk-lib/aws-eks/test/cluster.test.ts @@ -3140,26 +3140,12 @@ describe('cluster', () => { function message(version: string) { return [ `You created a cluster with Kubernetes Version 1.${version} without specifying the kubectlLayer property.`, - 'This may cause failures as the kubectl version provided with aws-cdk-lib is 1.20, which is only guaranteed to be compatible with Kubernetes versions 1.19-1.21.', - `Please provide a kubectlLayer from @aws-cdk/lambda-layer-kubectl-v${version}. [ack: @aws-cdk/aws-eks:clusterKubectlLayerNotSpecified]`, + 'The property will become required instead of optional in 2025 Jan. Please update your CDK code to provide a kubectlLayer.', + '[ack: @aws-cdk/aws-eks:clusterKubectlLayerNotSpecified]', ].join(' '); } - test('not added when version < 1.22 and no kubectl layer provided', () => { - // GIVEN - const { stack } = testFixture(); - - // WHEN - new eks.Cluster(stack, 'Cluster1', { - version: eks.KubernetesVersion.V1_21, - prune: false, - }); - - // THEN - Annotations.fromStack(stack).hasNoWarning('/Stack/Cluster1', message('21')); - }); - - test('added when version >= 1.22 and no kubectl layer provided', () => { + test('no kubectl layer provided', () => { // GIVEN const { stack } = testFixture(); diff --git a/packages/aws-cdk-lib/aws-s3tables/.jsiirc.json b/packages/aws-cdk-lib/aws-s3tables/.jsiirc.json new file mode 100644 index 0000000000000..fcb11f52cb871 --- /dev/null +++ b/packages/aws-cdk-lib/aws-s3tables/.jsiirc.json @@ -0,0 +1,13 @@ +{ + "targets": { + "java": { + "package": "software.amazon.awscdk.services.s3tables" + }, + "dotnet": { + "package": "Amazon.CDK.AWS.S3Tables" + }, + "python": { + "module": "aws_cdk.aws_s3tables" + } + } +} diff --git a/packages/aws-cdk-lib/aws-s3tables/README.md b/packages/aws-cdk-lib/aws-s3tables/README.md new file mode 100644 index 0000000000000..3a37f3f535bcd --- /dev/null +++ b/packages/aws-cdk-lib/aws-s3tables/README.md @@ -0,0 +1,39 @@ +# AWS::S3Tables Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts nofixture +import * as s3tables from 'aws-cdk-lib/aws-s3tables'; +``` + + + +There are no official hand-written ([L2](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib)) constructs for this service yet. Here are some suggestions on how to proceed: + +- Search [Construct Hub for S3Tables construct libraries](https://constructs.dev/search?q=s3tables) +- Use the automatically generated [L1](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_l1_using) constructs, in the same way you would use [the CloudFormation AWS::S3Tables resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_S3Tables.html) directly. + + + + +There are no hand-written ([L2](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib)) constructs for this service yet. +However, you can still use the automatically generated [L1](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_l1_using) constructs, and use this service exactly as you would using CloudFormation directly. + +For more information on the resources and properties available for this service, see the [CloudFormation documentation for AWS::S3Tables](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_S3Tables.html). + +(Read the [CDK Contributing Guide](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and submit an RFC if you are interested in contributing to this construct library.) + + diff --git a/packages/aws-cdk-lib/aws-s3tables/index.ts b/packages/aws-cdk-lib/aws-s3tables/index.ts new file mode 100644 index 0000000000000..f41a696fd204d --- /dev/null +++ b/packages/aws-cdk-lib/aws-s3tables/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/packages/aws-cdk-lib/aws-s3tables/lib/index.ts b/packages/aws-cdk-lib/aws-s3tables/lib/index.ts new file mode 100644 index 0000000000000..661052a64da24 --- /dev/null +++ b/packages/aws-cdk-lib/aws-s3tables/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::S3Tables Cloudformation Resources +export * from './s3tables.generated'; diff --git a/packages/aws-cdk-lib/aws-secretsmanager/README.md b/packages/aws-cdk-lib/aws-secretsmanager/README.md index d72b5b5953f80..2c14fc6b0bd97 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/README.md +++ b/packages/aws-cdk-lib/aws-secretsmanager/README.md @@ -93,6 +93,8 @@ const secret = new secretsmanager.Secret(this, 'Secret', { encryptionKey: key }) secret.grantRead(otherAccount); ``` +## Rotating a Secret + ### Using a Custom Lambda Function A rotation schedule can be added to a Secret using a custom Lambda function: diff --git a/packages/aws-cdk-lib/index.ts b/packages/aws-cdk-lib/index.ts index 3a1ed040cadae..2565510e4c99b 100644 --- a/packages/aws-cdk-lib/index.ts +++ b/packages/aws-cdk-lib/index.ts @@ -234,6 +234,7 @@ export * as aws_s3_notifications from './aws-s3-notifications'; export * as aws_s3express from './aws-s3express'; export * as aws_s3objectlambda from './aws-s3objectlambda'; export * as aws_s3outposts from './aws-s3outposts'; +export * as aws_s3tables from './aws-s3tables'; export * as aws_sagemaker from './aws-sagemaker'; export * as aws_sam from './aws-sam'; export * as aws_scheduler from './aws-scheduler'; diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 398a110c5989b..0b0243bb04896 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -136,7 +136,7 @@ "mime-types": "^2.1.35" }, "devDependencies": { - "@aws-cdk/aws-service-spec": "^0.1.37", + "@aws-cdk/aws-service-spec": "^0.1.38", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/custom-resource-handlers": "0.0.0", "@aws-cdk/pkglint": "0.0.0", @@ -449,6 +449,7 @@ "./aws-s3express": "./aws-s3express/index.js", "./aws-s3objectlambda": "./aws-s3objectlambda/index.js", "./aws-s3outposts": "./aws-s3outposts/index.js", + "./aws-s3tables": "./aws-s3tables/index.js", "./aws-sagemaker": "./aws-sagemaker/index.js", "./aws-sam": "./aws-sam/index.js", "./aws-scheduler": "./aws-scheduler/index.js", diff --git a/packages/aws-cdk-lib/scripts/scope-map.json b/packages/aws-cdk-lib/scripts/scope-map.json index 586b54aede737..4ac63e5e2bd8a 100644 --- a/packages/aws-cdk-lib/scripts/scope-map.json +++ b/packages/aws-cdk-lib/scripts/scope-map.json @@ -636,6 +636,9 @@ "aws-s3outposts": [ "AWS::S3Outposts" ], + "aws-s3tables": [ + "AWS::S3Tables" + ], "aws-sagemaker": [ "AWS::SageMaker" ], diff --git a/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts b/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts index c587e0fc20040..b2047bd3fbbfb 100644 --- a/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts +++ b/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts @@ -165,7 +165,7 @@ function isV3Provider(x: PluginProviderResult): x is SDKv3CompatibleCredentialPr } function isV2Credentials(x: PluginProviderResult): x is SDKv2CompatibleCredentials { - return !!(x && typeof x === 'object' && x.accessKeyId && (x as SDKv2CompatibleCredentials).getPromise); + return !!(x && typeof x === 'object' && (x as SDKv2CompatibleCredentials).getPromise); } function isV3Credentials(x: PluginProviderResult): x is SDKv3CompatibleCredentials { diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 15fab3fe5dfe3..9eb2b24c473ef 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -904,21 +904,6 @@ export class CdkToolkit { return undefined; } - // This is a slight hack; in integ mode we allow multiple stacks to be synthesized to stdout sequentially. - // This is to make it so that we can support multi-stack integ test expectations, without so drastically - // having to change the synthesis format that we have to rerun all integ tests. - // - // Because this feature is not useful to consumers (the output is missing - // the stack names), it's not exposed as a CLI flag. Instead, it's hidden - // behind an environment variable. - const isIntegMode = process.env.CDK_INTEG_MODE === '1'; - if (isIntegMode) { - printSerializedObject( - stacks.stackArtifacts.map((s) => obscureTemplate(s.template)), - json ?? false, - ); - } - // not outputting template to stdout, let's explain things to the user a little bit... success(`Successfully synthesized to ${chalk.blue(path.resolve(stacks.assembly.directory))}`); print( diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index b2fa6e4040e80..f40a71945485b 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -20,14 +20,13 @@ import { CdkToolkit, AssetBuildTime } from '../lib/cdk-toolkit'; import { realHandler as context } from '../lib/commands/context'; import { realHandler as docs } from '../lib/commands/docs'; import { realHandler as doctor } from '../lib/commands/doctor'; -import { MIGRATE_SUPPORTED_LANGUAGES, getMigrateScanType } from '../lib/commands/migrate'; -import { availableInitLanguages, cliInit, printAvailableTemplates } from '../lib/init'; +import { getMigrateScanType } from '../lib/commands/migrate'; +import { cliInit, printAvailableTemplates } from '../lib/init'; import { data, debug, error, print, setCI, setLogLevel, LogLevel } from '../lib/logging'; import { Notices } from '../lib/notices'; import { Command, Configuration, Settings } from '../lib/settings'; import * as version from '../lib/version'; import { SdkToCliLogger } from './api/aws-auth/sdk-logger'; -import { yargsNegativeAlias } from './util/yargs-helpers'; /* eslint-disable max-len */ /* eslint-disable @typescript-eslint/no-shadow */ // yargs @@ -38,17 +37,8 @@ if (!process.stdout.isTTY) { } export async function exec(args: string[], synthesizer?: Synthesizer): Promise { - function makeBrowserDefault(): string { - const defaultBrowserCommand: { [key in NodeJS.Platform]?: string } = { - darwin: 'open %u', - win32: 'start %u', - }; - - const cmd = defaultBrowserCommand[process.platform]; - return cmd ?? 'xdg-open %u'; - } - const argv = await parseCommandLineArguments(args, makeBrowserDefault(), await availableInitLanguages(), MIGRATE_SUPPORTED_LANGUAGES as string[], version.DISPLAY_VERSION, yargsNegativeAlias); + const argv = await parseCommandLineArguments(args); // if one -v, log at a DEBUG level // if 2 -v, log at a TRACE level diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index 62714e334f66f..f7aaf8d6bc013 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -1,14 +1,17 @@ -import type { CliConfig, DynamicResult } from '@aws-cdk/yargs-gen'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { CliHelpers, type CliConfig } from '@aws-cdk/yargs-gen'; import { StackActivityProgress } from './api/util/cloudformation/stack-activity-monitor'; +import { MIGRATE_SUPPORTED_LANGUAGES } from './commands/migrate'; import { RequireApproval } from './diff'; +import { availableInitLanguages } from './init'; -/* eslint-disable quote-props */ +export const YARGS_HELPERS = new CliHelpers('./util/yargs-helpers'); /** * Source of truth for all CDK CLI commands. `yargs-gen` translates this into the `yargs` definition * in `lib/parse-command-line-arguments.ts`. */ -export function makeConfig(): CliConfig { +export async function makeConfig(): Promise { return { globalOptions: { 'app': { type: 'string', alias: 'a', desc: 'REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. "node bin/my-app.js"). Can also be specified in cdk.json or ~/.cdk.json', requiresArg: true }, @@ -34,11 +37,11 @@ export function makeConfig(): CliConfig { 'output': { type: 'string', alias: 'o', desc: 'Emits the synthesized cloud assembly into a directory (default: cdk.out)', requiresArg: true }, 'notices': { type: 'boolean', desc: 'Show relevant notices' }, 'no-color': { type: 'boolean', desc: 'Removes colors and other style from console output', default: false }, - 'ci': { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: DynamicValue.fromInline(() => process.env.CI !== undefined) }, + 'ci': { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: YARGS_HELPERS.isCI() }, 'unstable': { type: 'array', desc: 'Opt in to unstable features. The flag indicates that the scope and API of a feature might still change. Otherwise the feature is generally production ready and fully supported. Can be specified multiple times.', default: [] }, }, commands: { - 'list': { + list: { arg: { name: 'STACKS', variadic: true, @@ -50,7 +53,7 @@ export function makeConfig(): CliConfig { 'show-dependencies': { type: 'boolean', default: false, alias: 'd', desc: 'Display stack dependency information for each stack' }, }, }, - 'synthesize': { + synthesize: { arg: { name: 'STACKS', variadic: true, @@ -58,9 +61,9 @@ export function makeConfig(): CliConfig { aliases: ['synth'], description: 'Synthesizes and prints the CloudFormation template for this stack', options: { - 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only synthesize requested stacks, don\'t include dependencies' }, - 'validation': { type: 'boolean', desc: 'After synthesis, validate stacks with the "validateOnSynth" attribute set (can also be controlled with CDK_VALIDATION)', default: true }, - 'quiet': { type: 'boolean', alias: 'q', desc: 'Do not output CloudFormation Template to stdout', default: false }, + exclusively: { type: 'boolean', alias: 'e', desc: 'Only synthesize requested stacks, don\'t include dependencies' }, + validation: { type: 'boolean', desc: 'After synthesis, validate stacks with the "validateOnSynth" attribute set (can also be controlled with CDK_VALIDATION)', default: true }, + quiet: { type: 'boolean', alias: 'q', desc: 'Do not output CloudFormation Template to stdout', default: false }, }, }, bootstrap: { @@ -242,18 +245,6 @@ export function makeConfig(): CliConfig { variadic: true, }, options: { - // I'm fairly certain none of these options, present for 'deploy', make sense for 'watch': - // .option('all', { type: 'boolean', default: false, desc: 'Deploy all available stacks' }) - // .option('ci', { type: 'boolean', desc: 'Force CI detection', default: process.env.CI !== undefined }) - // @deprecated(v2) -- tags are part of the Cloud Assembly and tags specified here will be overwritten on the next deployment - // .option('tags', { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)', nargs: 1, requiresArg: true }) - // .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }) - // These options, however, are more subtle - I could be convinced some of these should also be available for 'watch': - // .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' }) - // .option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} }) - // .option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }) - // .option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true }) - // .option('notification-arns', { type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true }) 'build-exclude': { type: 'array', alias: 'E', desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', default: [] }, 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' }, 'change-set-name': { type: 'string', desc: 'Name of the CloudFormation change set to create' }, @@ -295,9 +286,9 @@ export function makeConfig(): CliConfig { variadic: true, }, options: { - 'all': { type: 'boolean', default: false, desc: 'Destroy all available stacks' }, - 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' }, - 'force': { type: 'boolean', alias: 'f', desc: 'Do not ask for confirmation before destroying the stacks' }, + all: { type: 'boolean', default: false, desc: 'Destroy all available stacks' }, + exclusively: { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' }, + force: { type: 'boolean', alias: 'f', desc: 'Do not ask for confirmation before destroying the stacks' }, }, }, diff: { @@ -336,7 +327,7 @@ export function makeConfig(): CliConfig { notices: { description: 'Returns a list of relevant notices', options: { - 'unacknowledged': { type: 'boolean', alias: 'u', default: false, desc: 'Returns a list of unacknowledged notices' }, + unacknowledged: { type: 'boolean', alias: 'u', default: false, desc: 'Returns a list of unacknowledged notices' }, }, }, init: { @@ -346,16 +337,16 @@ export function makeConfig(): CliConfig { variadic: false, }, options: { - 'language': { type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)', choices: DynamicValue.fromParameter('availableInitLanguages') } as any, // TODO: preamble, this initTemplateLanguages variable needs to go as a statement there. + 'language': { type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)', choices: await availableInitLanguages() }, 'list': { type: 'boolean', desc: 'List the available templates' }, 'generate-only': { type: 'boolean', default: false, desc: 'If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project' }, }, }, - 'migrate': { + migrate: { description: false as any, options: { 'stack-name': { type: 'string', alias: 'n', desc: 'The name assigned to the stack created in the new project. The name of the app will be based off this name as well.', requiresArg: true }, - 'language': { type: 'string', default: 'typescript', alias: 'l', desc: 'The language to be used for the new project', choices: DynamicValue.fromParameter('migrateSupportedLanguages') as any }, + 'language': { type: 'string', default: 'typescript', alias: 'l', desc: 'The language to be used for the new project', choices: MIGRATE_SUPPORTED_LANGUAGES }, 'account': { type: 'string', desc: 'The account to retrieve the CloudFormation stack template from' }, 'region': { type: 'string', desc: 'The region to retrieve the CloudFormation stack template from' }, 'from-path': { type: 'string', desc: 'The path to the CloudFormation template to migrate. Use this for locally stored templates' }, @@ -379,54 +370,29 @@ export function makeConfig(): CliConfig { 'compress': { type: 'boolean', desc: 'Use this flag to zip the generated CDK app' }, }, }, - 'context': { + context: { description: 'Manage cached context values', options: { - 'reset': { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true }, - 'force': { alias: 'f', desc: 'Ignore missing key error', type: 'boolean', default: false }, - 'clear': { desc: 'Clear all context', type: 'boolean' }, + reset: { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true }, + force: { alias: 'f', desc: 'Ignore missing key error', type: 'boolean', default: false }, + clear: { desc: 'Clear all context', type: 'boolean' }, }, }, - 'docs': { + docs: { aliases: ['doc'], description: 'Opens the reference documentation in a browser', options: { - 'browser': { + browser: { alias: 'b', desc: 'the command to use to open the browser, using %u as a placeholder for the path of the file to open', type: 'string', - default: DynamicValue.fromParameter('browserDefault'), + default: YARGS_HELPERS.browserForPlatform(), }, }, }, - 'doctor': { + doctor: { description: 'Check your set-up for potential problems', }, }, }; } - -/** - * Informs the code library, `@aws-cdk/yargs-gen`, that - * this value references an entity not defined in this configuration file. - */ -export class DynamicValue { - /** - * Instructs `yargs-gen` to retrieve this value from the parameter with passed name. - */ - public static fromParameter(parameterName: string): DynamicResult { - return { - dynamicType: 'parameter', - dynamicValue: parameterName, - }; - } - - public static fromInline(f: () => any): DynamicResult { - const ARROW = '=>'; - const body = f.toString(); - return { - dynamicType: 'function', - dynamicValue: body.substring(body.indexOf(ARROW) + ARROW.length).trim(), - }; - } -} diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index 4f85e19394827..c7e4050d9f1e6 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -4,16 +4,10 @@ // ------------------------------------------------------------------------------------------- /* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props */ import { Argv } from 'yargs'; +import * as helpers from './util/yargs-helpers'; // @ts-ignore TS6133 -export function parseCommandLineArguments( - args: Array, - browserDefault: string, - availableInitLanguages: Array, - migrateSupportedLanguages: Array, - version: string, - yargsNegativeAlias: any -): any { +export function parseCommandLineArguments(args: Array): any { return yargs .env('CDK') .usage('Usage: cdk -a COMMAND') @@ -143,7 +137,7 @@ export function parseCommandLineArguments( .option('ci', { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', - default: process.env.CI !== undefined, + default: helpers.isCI(), }) .option('unstable', { type: 'array', @@ -424,8 +418,8 @@ export function parseCommandLineArguments( type: 'boolean', desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail", }) - .middleware(yargsNegativeAlias('R', 'rollback'), true) .option('R', { type: 'boolean', hidden: true }) + .middleware(helpers.yargsNegativeAlias('R', 'rollback'), true) .option('hotswap', { type: 'boolean', desc: "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.Do not use this in production environments", @@ -570,8 +564,8 @@ export function parseCommandLineArguments( type: 'boolean', desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail", }) - .middleware(yargsNegativeAlias('R', 'rollback'), true) .option('R', { type: 'boolean', hidden: true }) + .middleware(helpers.yargsNegativeAlias('R', 'rollback'), true) .option('hotswap', { type: 'boolean', desc: "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.'true' by default, use --no-hotswap to turn off", @@ -679,7 +673,7 @@ export function parseCommandLineArguments( type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)', - choices: availableInitLanguages, + choices: ['csharp', 'fsharp', 'go', 'java', 'javascript', 'python', 'typescript'], }) .option('list', { type: 'boolean', @@ -704,7 +698,7 @@ export function parseCommandLineArguments( default: 'typescript', alias: 'l', desc: 'The language to be used for the new project', - choices: migrateSupportedLanguages, + choices: ['typescript', 'go', 'java', 'python', 'csharp'], }) .option('account', { type: 'string', @@ -765,11 +759,11 @@ export function parseCommandLineArguments( alias: 'b', desc: 'the command to use to open the browser, using %u as a placeholder for the path of the file to open', type: 'string', - default: browserDefault, + default: helpers.browserForPlatform(), }) ) .command('doctor', 'Check your set-up for potential problems') - .version(version) + .version(helpers.cliVersion()) .demandCommand(1, '') .recommendCommands() .help() diff --git a/packages/aws-cdk/lib/util/yargs-helpers.ts b/packages/aws-cdk/lib/util/yargs-helpers.ts index 719b531b0d24c..92c982ab7bffd 100644 --- a/packages/aws-cdk/lib/util/yargs-helpers.ts +++ b/packages/aws-cdk/lib/util/yargs-helpers.ts @@ -1,10 +1,12 @@ +import * as version from '../../lib/version'; + /** * yargs middleware to negate an option if a negative alias is provided * E.g. `-R` will imply `--rollback=false` * * @param optionToNegate The name of the option to negate, e.g. `rollback` * @param negativeAlias The alias that should negate the option, e.g. `R` - * @returns + * @returns a middleware function that can be passed to yargs */ export function yargsNegativeAlias( negativeAlias: S, @@ -19,3 +21,35 @@ export function yargsNegativeAlias { diff --git a/packages/aws-cdk/test/api/plugin/credential-plugin.test.ts b/packages/aws-cdk/test/api/plugin/credential-plugin.test.ts index f665dc179db9c..77fc97731e58d 100644 --- a/packages/aws-cdk/test/api/plugin/credential-plugin.test.ts +++ b/packages/aws-cdk/test/api/plugin/credential-plugin.test.ts @@ -104,6 +104,26 @@ test('plugin can return V2 compatible credential-provider', async () => { expect(getPromise).toHaveBeenCalled(); }); +test('plugin can return V2 compatible credential-provider with initially empty keys', async () => { + // GIVEN + mockCredentialFunction(() => Promise.resolve({ + accessKeyId: '', + secretAccessKey: '', + expired: false, + getPromise() { + this.accessKeyId = 'keyid'; + return Promise.resolve({}); + }, + })); + + // WHEN + const creds = await fetchNow(); + + await expect(creds).toEqual(expect.objectContaining({ + accessKeyId: 'keyid', + })); +}); + test('plugin must not return something that is not a credential', async () => { // GIVEN mockCredentialFunction(() => Promise.resolve({ diff --git a/packages/aws-cdk/test/parse-command-line-arguments.test.ts b/packages/aws-cdk/test/parse-command-line-arguments.test.ts index 0571fb608b7c9..485536a7c28df 100644 --- a/packages/aws-cdk/test/parse-command-line-arguments.test.ts +++ b/packages/aws-cdk/test/parse-command-line-arguments.test.ts @@ -1,8 +1,40 @@ import { parseCommandLineArguments } from '../lib/parse-command-line-arguments'; -import { yargsNegativeAlias } from '../lib/util/yargs-helpers'; test('cdk deploy -R sets rollback to false', async () => { - const argv = await parseCommandLineArguments(['deploy', '-R'], 'open', ['typescript'], ['typescript'], 'test', yargsNegativeAlias); - + const argv = await parseCommandLineArguments(['deploy', '-R']); expect(argv.rollback).toBe(false); }); + +describe('cdk docs', () => { + const originalPlatform = process.platform; + // Helper to mock process.platform + const mockPlatform = (platform: string) => { + Object.defineProperty(process, 'platform', { + value: platform, + writable: false, + enumerable: true, + configurable: true, + }); + }; + + // Restore original platform after each test + afterEach(() => { + Object.defineProperty(process, 'platform', { + value: originalPlatform, + writable: false, + enumerable: true, + configurable: true, + }); + }); + + test.each([ + ['darwin', 'open %u'], + ['win32', 'start %u'], + ['linux', 'xdg-open %u'], + ['freebsd', 'xdg-open %u'], + ])('for %s should return "%s"', async (platform, browser) => { + mockPlatform(platform); + const argv = await parseCommandLineArguments(['docs']); + expect(argv.browser).toBe(browser); + }); +}); diff --git a/tools/@aws-cdk/spec2cdk/package.json b/tools/@aws-cdk/spec2cdk/package.json index b3ea047386690..710dd825b5f2f 100644 --- a/tools/@aws-cdk/spec2cdk/package.json +++ b/tools/@aws-cdk/spec2cdk/package.json @@ -32,9 +32,9 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-cdk/aws-service-spec": "^0.1.37", + "@aws-cdk/aws-service-spec": "^0.1.38", "@aws-cdk/service-spec-importers": "^0.0.58", - "@aws-cdk/service-spec-types": "^0.0.104", + "@aws-cdk/service-spec-types": "^0.0.105", "@cdklabs/tskb": "^0.0.3", "@cdklabs/typewriter": "^0.0.3", "camelcase": "^6", diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 0bfc5af85412e..341315ac4a8b9 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -1,4 +1,4 @@ -import { Expression, FreeFunction, Module, SelectiveModuleImport, Statement, Type, TypeScriptRenderer, code } from '@cdklabs/typewriter'; +import { $E, Expression, ExternalModule, FreeFunction, IScope, Module, SelectiveModuleImport, Statement, ThingSymbol, Type, TypeScriptRenderer, code, expr } from '@cdklabs/typewriter'; import { EsLintRules } from '@cdklabs/typewriter/lib/eslint-rules'; import * as prettier from 'prettier'; import { CliConfig, CliOption, YargsOption } from './yargs-types'; @@ -8,7 +8,18 @@ import { CliConfig, CliOption, YargsOption } from './yargs-types'; // eslint-disable-next-line @typescript-eslint/no-require-imports const cloneDeep = require('lodash.clonedeep'); -export async function renderYargs(config: CliConfig): Promise { +export class CliHelpers extends ExternalModule { + public readonly browserForPlatform = makeCallableExpr(this, 'browserForPlatform'); + public readonly cliVersion = makeCallableExpr(this, 'cliVersion'); + public readonly isCI = makeCallableExpr(this, 'isCI'); + public readonly yargsNegativeAlias = makeCallableExpr(this, 'yargsNegativeAlias'); +} + +function makeCallableExpr(scope: IScope, name: string) { + return $E(expr.sym(new ThingSymbol(name, scope))); +} + +export async function renderYargs(config: CliConfig, helpers: CliHelpers): Promise { const scope = new Module('aws-cdk'); scope.documentation.push( '-------------------------------------------------------------------------------------------'); @@ -17,6 +28,7 @@ export async function renderYargs(config: CliConfig): Promise { scope.documentation.push('-------------------------------------------------------------------------------------------'); scope.addImport(new SelectiveModuleImport(scope, 'yargs', ['Argv'])); + helpers.import(scope, 'helpers'); // 'https://github.com/yargs/yargs/issues/1929', // 'https://github.com/evanw/esbuild/issues/1492', @@ -29,14 +41,9 @@ export async function renderYargs(config: CliConfig): Promise { returnType: Type.ANY, parameters: [ { name: 'args', type: Type.arrayOf(Type.STRING) }, - { name: 'browserDefault', type: Type.STRING }, - { name: 'availableInitLanguages', type: Type.arrayOf(Type.STRING) }, - { name: 'migrateSupportedLanguages', type: Type.arrayOf(Type.STRING) }, - { name: 'version', type: Type.STRING }, - { name: 'yargsNegativeAlias', type: Type.ANY }, ], }); - parseCommandLineArguments.addBody(makeYargs(config)); + parseCommandLineArguments.addBody(makeYargs(config, helpers)); const ts = new TypeScriptRenderer({ disabledEsLintRules: [ @@ -67,14 +74,14 @@ export async function renderYargs(config: CliConfig): Promise { // By using the config above, every --arg will only consume one argument, so you can do the following: // // ./prog --arg one --arg two position => will parse to { arg: ['one', 'two'], _: ['positional'] }. -function makeYargs(config: CliConfig): Statement { +function makeYargs(config: CliConfig, helpers: CliHelpers): Statement { let yargsExpr: Expression = code.expr.ident('yargs'); yargsExpr = yargsExpr .callMethod('env', lit('CDK')) .callMethod('usage', lit('Usage: cdk -a COMMAND')); // we must compute global options first, as they are not part of an argument to a command call - yargsExpr = makeOptions(yargsExpr, config.globalOptions); + yargsExpr = makeOptions(yargsExpr, config.globalOptions, helpers); for (const command of Object.keys(config.commands)) { const commandFacts = config.commands[command]; @@ -87,7 +94,7 @@ function makeYargs(config: CliConfig): Statement { // must compute options before we compute the full command, because in yargs, the options are an argument to the command call. let optionsExpr: Expression = code.expr.directCode('(yargs: Argv) => yargs'); - optionsExpr = makeOptions(optionsExpr, commandFacts.options ?? {}); + optionsExpr = makeOptions(optionsExpr, commandFacts.options ?? {}, helpers); const commandCallArgs: Array = []; if (aliases) { @@ -104,10 +111,10 @@ function makeYargs(config: CliConfig): Statement { yargsExpr = yargsExpr.callMethod('command', ...commandCallArgs); } - return code.stmt.ret(makeEpilogue(yargsExpr)); + return code.stmt.ret(makeEpilogue(yargsExpr, helpers)); } -function makeOptions(prefix: Expression, options: { [optionName: string]: CliOption }) { +function makeOptions(prefix: Expression, options: { [optionName: string]: CliOption }, helpers: CliHelpers) { let optionsExpr = prefix; for (const option of Object.keys(options)) { const theOption: CliOption = options[option]; @@ -122,11 +129,8 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: CliOpt for (const optionProp of Object.keys(optionProps).filter(opt => !['negativeAlias'].includes(opt))) { const optionValue = (optionProps as any)[optionProp]; - if (optionValue && optionValue.dynamicType === 'parameter') { - optionArgs[optionProp] = code.expr.ident(optionValue.dynamicValue); - } else if (optionValue && optionValue.dynamicType === 'function') { - const inlineFunction: string = optionValue.dynamicValue; - optionArgs[optionProp] = code.expr.directCode(inlineFunction); + if (optionValue instanceof Expression) { + optionArgs[optionProp] = optionValue; } else { optionArgs[optionProp] = lit(optionValue); } @@ -139,20 +143,20 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: CliOpt // We need an additional option and a middleware: // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) if (theOption.negativeAlias) { - const middleware = code.expr.builtInFn('yargsNegativeAlias', lit(theOption.negativeAlias), lit(option)); - optionsExpr = optionsExpr.callMethod('middleware', middleware, lit(true)); + const middleware = helpers.yargsNegativeAlias.call(lit(theOption.negativeAlias), lit(option)); optionsExpr = optionsExpr.callMethod('option', lit(theOption.negativeAlias), code.expr.lit({ type: 'boolean', hidden: true, })); + optionsExpr = optionsExpr.callMethod('middleware', middleware, lit(true)); } } return optionsExpr; } -function makeEpilogue(prefix: Expression) { - let completeDefinition = prefix.callMethod('version', code.expr.ident('version')); +function makeEpilogue(prefix: Expression, helpers: CliHelpers) { + let completeDefinition = prefix.callMethod('version', helpers.cliVersion()); completeDefinition = completeDefinition.callMethod('demandCommand', lit(1), lit('')); // just print help completeDefinition = completeDefinition.callMethod('recommendCommands'); completeDefinition = completeDefinition.callMethod('help'); diff --git a/tools/@aws-cdk/yargs-gen/test/cli.test.ts b/tools/@aws-cdk/yargs-gen/test/cli.test.ts index 7c903e34fdfbf..52988720cf870 100644 --- a/tools/@aws-cdk/yargs-gen/test/cli.test.ts +++ b/tools/@aws-cdk/yargs-gen/test/cli.test.ts @@ -1,4 +1,7 @@ -import { CliConfig, renderYargs } from '../lib'; +import { $E, expr, ThingSymbol } from '@cdklabs/typewriter'; +import { CliConfig, CliHelpers, renderYargs } from '../lib'; + +const YARGS_HELPERS = new CliHelpers('./util/yargs-helpers'); describe('render', () => { test('can generate global options', async () => { @@ -20,23 +23,17 @@ describe('render', () => { commands: {}, }; - expect(await renderYargs(config)).toMatchInlineSnapshot(` + expect(await renderYargs(config, YARGS_HELPERS)).toMatchInlineSnapshot(` "// ------------------------------------------------------------------------------------------- // GENERATED FROM packages/aws-cdk/lib/config.ts. // Do not edit by hand; all changes will be overwritten at build time from the config file. // ------------------------------------------------------------------------------------------- /* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props */ import { Argv } from 'yargs'; + import * as helpers from './util/yargs-helpers'; // @ts-ignore TS6133 - export function parseCommandLineArguments( - args: Array, - browserDefault: string, - availableInitLanguages: Array, - migrateSupportedLanguages: Array, - version: string, - yargsNegativeAlias: any - ): any { + export function parseCommandLineArguments(args: Array): any { return yargs .env('CDK') .usage('Usage: cdk -a COMMAND') @@ -57,7 +54,7 @@ describe('render', () => { nargs: 1, requiresArg: true, }) - .version(version) + .version(helpers.cliVersion()) .demandCommand(1, '') .recommendCommands() .help() @@ -90,23 +87,17 @@ describe('render', () => { }, }; - expect(await renderYargs(config)).toMatchInlineSnapshot(` + expect(await renderYargs(config, YARGS_HELPERS)).toMatchInlineSnapshot(` "// ------------------------------------------------------------------------------------------- // GENERATED FROM packages/aws-cdk/lib/config.ts. // Do not edit by hand; all changes will be overwritten at build time from the config file. // ------------------------------------------------------------------------------------------- /* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props */ import { Argv } from 'yargs'; + import * as helpers from './util/yargs-helpers'; // @ts-ignore TS6133 - export function parseCommandLineArguments( - args: Array, - browserDefault: string, - availableInitLanguages: Array, - migrateSupportedLanguages: Array, - version: string, - yargsNegativeAlias: any - ): any { + export function parseCommandLineArguments(args: Array): any { return yargs .env('CDK') .usage('Usage: cdk -a COMMAND') @@ -117,10 +108,10 @@ describe('render', () => { alias: 'o', desc: 'text for one', }) - .middleware(yargsNegativeAlias('O', 'one'), true) .option('O', { type: 'boolean', hidden: true }) + .middleware(helpers.yargsNegativeAlias('O', 'one'), true) ) - .version(version) + .version(helpers.cliVersion()) .demandCommand(1, '') .recommendCommands() .help() @@ -134,4 +125,27 @@ describe('render', () => { " `); }); + + test('can pass-through expression unchanged', async () => { + const config: CliConfig = { + globalOptions: {}, + commands: { + test: { + description: 'the action under test', + options: { + one: { + type: 'boolean', + default: $E( + expr + .sym(new ThingSymbol('banana', YARGS_HELPERS)) + .call(expr.lit(1), expr.lit(2), expr.lit(3)), + ), + }, + }, + }, + }, + }; + + expect(await renderYargs(config, YARGS_HELPERS)).toContain('default: helpers.banana(1, 2, 3)'); + }); }); diff --git a/yarn.lock b/yarn.lock index 10b4bc41a6463..91882fd848a4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,12 +63,12 @@ resolved "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz#6d3c7860354d4856a7e75375f2f0ecab313b4989" integrity sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A== -"@aws-cdk/aws-service-spec@^0.1.37": - version "0.1.37" - resolved "https://registry.npmjs.org/@aws-cdk/aws-service-spec/-/aws-service-spec-0.1.37.tgz#39e78a07079fc276f2f2bfdb31c3c7226939a04a" - integrity sha512-WFGAvjslG8Jdj9XmzDtV4JbsWEmLj8K9pA882mc6iNK59l4ocGt2GqS4n3JuzRdzoEpzcVYqfgrqGUuV1ez7vg== +"@aws-cdk/aws-service-spec@^0.1.38": + version "0.1.38" + resolved "https://registry.npmjs.org/@aws-cdk/aws-service-spec/-/aws-service-spec-0.1.38.tgz#76eb52f578a0a0094d33d5b310bd13a9c2755d7e" + integrity sha512-Kk1/GEIScI492f9vRIzzZDHnG/pdxAX+AZxFyjBnHa0zVCH9pXOiG5hndd4kq6sDP/ePyrKcBK4iv3nO3WVw2w== dependencies: - "@aws-cdk/service-spec-types" "^0.0.104" + "@aws-cdk/service-spec-types" "^0.0.105" "@cdklabs/tskb" "^0.0.3" "@aws-cdk/cloud-assembly-schema@^38.0.0", "@aws-cdk/cloud-assembly-schema@^38.0.1": @@ -129,6 +129,13 @@ dependencies: "@cdklabs/tskb" "^0.0.3" +"@aws-cdk/service-spec-types@^0.0.105": + version "0.0.105" + resolved "https://registry.npmjs.org/@aws-cdk/service-spec-types/-/service-spec-types-0.0.105.tgz#b39898f6711068bbae016c88a5a6e7b8e1347d13" + integrity sha512-HUiKW7clPyaCRxbPmgURtK9ZXOCLarWFlsPbpMvHNbleiqjk+VB0Tgrf5LqJmBZMnKcq1Jx/cE42MiIxI/sRZA== + dependencies: + "@cdklabs/tskb" "^0.0.3" + "@aws-crypto/crc32@5.2.0": version "5.2.0" resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1"