diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/EBSTaskAttachDefaultTestDeployAssertF52EF4F9.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/EBSTaskAttachDefaultTestDeployAssertF52EF4F9.assets.json new file mode 100644 index 0000000000000..7478188235117 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/EBSTaskAttachDefaultTestDeployAssertF52EF4F9.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "EBSTaskAttachDefaultTestDeployAssertF52EF4F9.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-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/EBSTaskAttachDefaultTestDeployAssertF52EF4F9.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/EBSTaskAttachDefaultTestDeployAssertF52EF4F9.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/EBSTaskAttachDefaultTestDeployAssertF52EF4F9.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-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ-aws-ecs-ebs-task-attach.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ-aws-ecs-ebs-task-attach.assets.json new file mode 100644 index 0000000000000..95bfe458f82d1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ-aws-ecs-ebs-task-attach.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "114105e2d29626a5303e48c5ab4b3be1059592f803b1d317c99665664cd954a9": { + "source": { + "path": "integ-aws-ecs-ebs-task-attach.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "114105e2d29626a5303e48c5ab4b3be1059592f803b1d317c99665664cd954a9.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-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ-aws-ecs-ebs-task-attach.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ-aws-ecs-ebs-task-attach.template.json new file mode 100644 index 0000000000000..bac09e2c34fec --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ-aws-ecs-ebs-task-attach.template.json @@ -0,0 +1,451 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/17", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1RouteTableAssociation97140677" + ] + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/17", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "integ-aws-ecs-ebs-task-attach/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "FargateCluster7CCD5F93": { + "Type": "AWS::ECS::Cluster" + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "MountPoints": [ + { + "ContainerPath": "/var/lib", + "ReadOnly": false, + "SourceVolume": "ebs1" + } + ], + "Name": "web", + "PortMappings": [ + { + "ContainerPort": 80, + "Protocol": "tcp" + } + ] + } + ], + "Cpu": "256", + "Family": "integawsecsebstaskattachTaskDefB8F13A4F", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + }, + "Volumes": [ + { + "ConfiguredAtLaunch": true, + "Name": "ebs1" + } + ] + } + }, + "EBSVolumeEBSRoleC27DD941": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonECSInfrastructureRolePolicyForVolumes" + ] + ] + } + ] + } + }, + "FargateServiceAC2B3B85": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "FargateCluster7CCD5F93" + }, + "DeploymentConfiguration": { + "Alarms": { + "AlarmNames": [], + "Enable": false, + "Rollback": false + }, + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "DesiredCount": 1, + "EnableECSManagedTags": false, + "LaunchType": "FARGATE", + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "FargateServiceSecurityGroup0A0E79CB", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ] + } + }, + "TaskDefinition": { + "Ref": "TaskDef54694570" + }, + "VolumeConfigurations": [ + { + "ManagedEBSVolume": { + "Encrypted": true, + "FilesystemType": "ext4", + "Iops": 4000, + "RoleArn": { + "Fn::GetAtt": [ + "EBSVolumeEBSRoleC27DD941", + "Arn" + ] + }, + "SizeInGiB": 15, + "TagSpecifications": [ + { + "PropagateTags": "SERVICE", + "ResourceType": "volume", + "Tags": [ + { + "Key": "purpose", + "Value": "production" + } + ] + }, + { + "PropagateTags": "TASK_DEFINITION", + "ResourceType": "volume", + "Tags": [ + { + "Key": "purpose", + "Value": "development" + } + ] + } + ], + "Throughput": 500, + "VolumeType": "gp3" + }, + "Name": "ebs1" + } + ] + }, + "DependsOn": [ + "TaskDefTaskRole1EDB4A67" + ] + }, + "FargateServiceSecurityGroup0A0E79CB": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "integ-aws-ecs-ebs-task-attach/FargateService/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "DependsOn": [ + "TaskDefTaskRole1EDB4A67" + ] + } + }, + "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-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ.json new file mode 100644 index 0000000000000..7e4cd54d7f54c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "EBSTaskAttach/DefaultTest": { + "stacks": [ + "integ-aws-ecs-ebs-task-attach" + ], + "assertionStack": "EBSTaskAttach/DefaultTest/DeployAssert", + "assertionStackName": "EBSTaskAttachDefaultTestDeployAssertF52EF4F9" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/manifest.json new file mode 100644 index 0000000000000..5c7609dc81843 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/manifest.json @@ -0,0 +1,221 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-aws-ecs-ebs-task-attach.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-aws-ecs-ebs-task-attach.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-aws-ecs-ebs-task-attach": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-aws-ecs-ebs-task-attach.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}/114105e2d29626a5303e48c5ab4b3be1059592f803b1d317c99665664cd954a9.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-aws-ecs-ebs-task-attach.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-aws-ecs-ebs-task-attach.assets" + ], + "metadata": { + "/integ-aws-ecs-ebs-task-attach/Vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Vpc8378EB38" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1Subnet5C2D37C4" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTable6C95E38E" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTableAssociation97140677" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1DefaultRoute3DA9E72A" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1EIPD7E02669" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1NATGateway4D7517AA" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableB2C5B500" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableAssociation70C59FA6" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1DefaultRouteBE02A9ED" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcIGWD7BA715C" + } + ], + "/integ-aws-ecs-ebs-task-attach/Vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcVPCGWBF912B6E" + } + ], + "/integ-aws-ecs-ebs-task-attach/FargateCluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateCluster7CCD5F93" + } + ], + "/integ-aws-ecs-ebs-task-attach/TaskDef/TaskRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TaskDefTaskRole1EDB4A67" + } + ], + "/integ-aws-ecs-ebs-task-attach/TaskDef/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TaskDef54694570" + } + ], + "/integ-aws-ecs-ebs-task-attach/EBSVolume/EBSRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EBSVolumeEBSRoleC27DD941" + } + ], + "/integ-aws-ecs-ebs-task-attach/FargateService/Service": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateServiceAC2B3B85" + } + ], + "/integ-aws-ecs-ebs-task-attach/FargateService/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateServiceSecurityGroup0A0E79CB" + } + ], + "/integ-aws-ecs-ebs-task-attach/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-aws-ecs-ebs-task-attach/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-aws-ecs-ebs-task-attach" + }, + "EBSTaskAttachDefaultTestDeployAssertF52EF4F9.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "EBSTaskAttachDefaultTestDeployAssertF52EF4F9.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "EBSTaskAttachDefaultTestDeployAssertF52EF4F9": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "EBSTaskAttachDefaultTestDeployAssertF52EF4F9.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": [ + "EBSTaskAttachDefaultTestDeployAssertF52EF4F9.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": [ + "EBSTaskAttachDefaultTestDeployAssertF52EF4F9.assets" + ], + "metadata": { + "/EBSTaskAttach/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/EBSTaskAttach/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "EBSTaskAttach/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-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/tree.json new file mode 100644 index 0000000000000..896bcffef46a0 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.js.snapshot/tree.json @@ -0,0 +1,794 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-aws-ecs-ebs-task-attach": { + "id": "integ-aws-ecs-ebs-task-attach", + "path": "integ-aws-ecs-ebs-task-attach", + "children": { + "Vpc": { + "id": "Vpc", + "path": "integ-aws-ecs-ebs-task-attach/Vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/17", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "tags": [ + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/17", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "integ-aws-ecs-ebs-task-attach/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "integ-aws-ecs-ebs-task-attach/Vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + }, + "FargateCluster": { + "id": "FargateCluster", + "path": "integ-aws-ecs-ebs-task-attach/FargateCluster", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-ecs-ebs-task-attach/FargateCluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Cluster", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnCluster", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.Cluster", + "version": "0.0.0" + } + }, + "TaskDef": { + "id": "TaskDef", + "path": "integ-aws-ecs-ebs-task-attach/TaskDef", + "children": { + "TaskRole": { + "id": "TaskRole", + "path": "integ-aws-ecs-ebs-task-attach/TaskDef/TaskRole", + "children": { + "ImportTaskRole": { + "id": "ImportTaskRole", + "path": "integ-aws-ecs-ebs-task-attach/TaskDef/TaskRole/ImportTaskRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-aws-ecs-ebs-task-attach/TaskDef/TaskRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-aws-ecs-ebs-task-attach/TaskDef/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::TaskDefinition", + "aws:cdk:cloudformation:props": { + "containerDefinitions": [ + { + "essential": true, + "image": "amazon/amazon-ecs-sample", + "mountPoints": [ + { + "containerPath": "/var/lib", + "readOnly": false, + "sourceVolume": "ebs1" + } + ], + "name": "web", + "portMappings": [ + { + "containerPort": 80, + "protocol": "tcp" + } + ] + } + ], + "cpu": "256", + "family": "integawsecsebstaskattachTaskDefB8F13A4F", + "memory": "512", + "networkMode": "awsvpc", + "requiresCompatibilities": [ + "FARGATE" + ], + "taskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + }, + "volumes": [ + { + "name": "ebs1", + "configuredAtLaunch": true + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnTaskDefinition", + "version": "0.0.0" + } + }, + "web": { + "id": "web", + "path": "integ-aws-ecs-ebs-task-attach/TaskDef/web", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.ContainerDefinition", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateTaskDefinition", + "version": "0.0.0" + } + }, + "EBSVolume": { + "id": "EBSVolume", + "path": "integ-aws-ecs-ebs-task-attach/EBSVolume", + "children": { + "EBSRole": { + "id": "EBSRole", + "path": "integ-aws-ecs-ebs-task-attach/EBSVolume/EBSRole", + "children": { + "ImportEBSRole": { + "id": "ImportEBSRole", + "path": "integ-aws-ecs-ebs-task-attach/EBSVolume/EBSRole/ImportEBSRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-aws-ecs-ebs-task-attach/EBSVolume/EBSRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonECSInfrastructureRolePolicyForVolumes" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.ServiceManagedVolume", + "version": "0.0.0" + } + }, + "FargateService": { + "id": "FargateService", + "path": "integ-aws-ecs-ebs-task-attach/FargateService", + "children": { + "Service": { + "id": "Service", + "path": "integ-aws-ecs-ebs-task-attach/FargateService/Service", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Service", + "aws:cdk:cloudformation:props": { + "cluster": { + "Ref": "FargateCluster7CCD5F93" + }, + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 50, + "alarms": { + "alarmNames": [], + "enable": false, + "rollback": false + } + }, + "desiredCount": 1, + "enableEcsManagedTags": false, + "launchType": "FARGATE", + "networkConfiguration": { + "awsvpcConfiguration": { + "assignPublicIp": "DISABLED", + "subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "securityGroups": [ + { + "Fn::GetAtt": [ + "FargateServiceSecurityGroup0A0E79CB", + "GroupId" + ] + } + ] + } + }, + "taskDefinition": { + "Ref": "TaskDef54694570" + }, + "volumeConfigurations": [ + { + "name": "ebs1", + "managedEbsVolume": { + "roleArn": { + "Fn::GetAtt": [ + "EBSVolumeEBSRoleC27DD941", + "Arn" + ] + }, + "encrypted": true, + "filesystemType": "ext4", + "iops": 4000, + "throughput": 500, + "volumeType": "gp3", + "sizeInGiB": 15, + "tagSpecifications": [ + { + "resourceType": "volume", + "propagateTags": "SERVICE", + "tags": [ + { + "key": "purpose", + "value": "production" + } + ] + }, + { + "resourceType": "volume", + "propagateTags": "TASK_DEFINITION", + "tags": [ + { + "key": "purpose", + "value": "development" + } + ] + } + ] + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnService", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "integ-aws-ecs-ebs-task-attach/FargateService/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-ecs-ebs-task-attach/FargateService/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "integ-aws-ecs-ebs-task-attach/FargateService/SecurityGroup", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateService", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-aws-ecs-ebs-task-attach/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-aws-ecs-ebs-task-attach/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "EBSTaskAttach": { + "id": "EBSTaskAttach", + "path": "EBSTaskAttach", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "EBSTaskAttach/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "EBSTaskAttach/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "EBSTaskAttach/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "EBSTaskAttach/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "EBSTaskAttach/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.3.0" + } + } + }, + "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-ecs/test/fargate/integ.ebs-taskattach.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.ts new file mode 100644 index 0000000000000..d178806e48136 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.ebs-taskattach.ts @@ -0,0 +1,77 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib'; +import * as ecs from 'aws-cdk-lib/aws-ecs'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { Construct } from 'constructs'; + +class TestStack extends cdk.Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const vpc = new ec2.Vpc(this, 'Vpc', { + maxAzs: 1, + restrictDefaultSecurityGroup: false, + }); + + const cluster = new ecs.Cluster(this, 'FargateCluster', { + vpc, + }); + + const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef'); + + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + portMappings: [{ + containerPort: 80, + protocol: ecs.Protocol.TCP, + }], + }); + + const volume = new ecs.ServiceManagedVolume(this, 'EBSVolume', { + name: 'ebs1', + managedEBSVolume: { + encrypted: true, + volumeType: ec2.EbsDeviceVolumeType.GP3, + size: cdk.Size.gibibytes(15), + iops: 4000, + throughput: 500, + fileSystemType: ecs.FileSystemType.EXT4, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }, + { + tags: { + purpose: 'development', + }, + propagateTags: ecs.EbsPropagatedTagSource.TASK_DEFINITION, + }], + }, + }); + + volume.mountIn(container, { + containerPath: '/var/lib', + readOnly: false, + }); + + taskDefinition.addVolume(volume); + + const service = new ecs.FargateService(this, 'FargateService', { + cluster, + taskDefinition, + desiredCount: 1, + }); + + service.addVolume(volume); + } +} + +const app = new cdk.App(); +const stack = new TestStack(app, 'integ-aws-ecs-ebs-task-attach'); + +new integ.IntegTest(app, 'EBSTaskAttach', { + testCases: [stack], +}); +app.synth(); diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/integ.json new file mode 100644 index 0000000000000..fe3b37643d4db --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "test-efs-integ-test/DefaultTest": { + "stacks": [ + "test-efs-transition-integ" + ], + "assertionStack": "test-efs-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "testefsintegtestDefaultTestDeployAssert7E1529D5" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/manifest.json new file mode 100644 index 0000000000000..50775457be791 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/manifest.json @@ -0,0 +1,257 @@ +{ + "version": "36.0.0", + "artifacts": { + "test-efs-transition-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "test-efs-transition-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "test-efs-transition-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "test-efs-transition-integ.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}/a0ff3a279e15d0e57140ef487ec06c6930bcfe16dc844df0f8a06688bc719b54.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "test-efs-transition-integ.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": [ + "test-efs-transition-integ.assets" + ], + "metadata": { + "/test-efs-transition-integ/Vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Vpc8378EB38" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1Subnet5C2D37C4" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTable6C95E38E" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTableAssociation97140677" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1DefaultRoute3DA9E72A" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1EIPD7E02669" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1NATGateway4D7517AA" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTable94F7E489" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTableAssociationDD5762D8" + } + ], + "/test-efs-transition-integ/Vpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2DefaultRoute97F91067" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableB2C5B500" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableAssociation70C59FA6" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1DefaultRouteBE02A9ED" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2Subnet3788AAA1" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableA678073B" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableAssociationA89CAD56" + } + ], + "/test-efs-transition-integ/Vpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2DefaultRoute060D2087" + } + ], + "/test-efs-transition-integ/Vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcIGWD7BA715C" + } + ], + "/test-efs-transition-integ/Vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcVPCGWBF912B6E" + } + ], + "/test-efs-transition-integ/FileSystem/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FileSystem8A8E25C0" + } + ], + "/test-efs-transition-integ/FileSystem/EfsSecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FileSystemEfsSecurityGroup212D3ACB" + } + ], + "/test-efs-transition-integ/FileSystem/EfsMountTarget-PrivateSubnet1": [ + { + "type": "aws:cdk:logicalId", + "data": "FileSystemEfsMountTargetPrivateSubnet1BB305AF3" + } + ], + "/test-efs-transition-integ/FileSystem/EfsMountTarget-PrivateSubnet2": [ + { + "type": "aws:cdk:logicalId", + "data": "FileSystemEfsMountTargetPrivateSubnet265F3ED67" + } + ], + "/test-efs-transition-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/test-efs-transition-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "test-efs-transition-integ" + }, + "testefsintegtestDefaultTestDeployAssert7E1529D5.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "testefsintegtestDefaultTestDeployAssert7E1529D5": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "testefsintegtestDefaultTestDeployAssert7E1529D5.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": [ + "testefsintegtestDefaultTestDeployAssert7E1529D5.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": [ + "testefsintegtestDefaultTestDeployAssert7E1529D5.assets" + ], + "metadata": { + "/test-efs-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/test-efs-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "test-efs-integ-test/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-efs/test/integ.efs-transition.js.snapshot/test-efs-transition-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/test-efs-transition-integ.assets.json new file mode 100644 index 0000000000000..082e82cf0b792 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/test-efs-transition-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "a0ff3a279e15d0e57140ef487ec06c6930bcfe16dc844df0f8a06688bc719b54": { + "source": { + "path": "test-efs-transition-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "a0ff3a279e15d0e57140ef487ec06c6930bcfe16dc844df0f8a06688bc719b54.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-efs/test/integ.efs-transition.js.snapshot/test-efs-transition-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/test-efs-transition-integ.template.json new file mode 100644 index 0000000000000..17afd24beda12 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/test-efs-transition-integ.template.json @@ -0,0 +1,498 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1RouteTableAssociation97140677" + ] + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "FileSystem8A8E25C0": { + "Type": "AWS::EFS::FileSystem", + "Properties": { + "Encrypted": true, + "FileSystemPolicy": { + "Statement": [ + { + "Action": [ + "elasticfilesystem:ClientRootAccess", + "elasticfilesystem:ClientWrite" + ], + "Condition": { + "Bool": { + "elasticfilesystem:AccessedViaMountTarget": "true" + } + }, + "Effect": "Allow", + "Principal": { + "AWS": "*" + } + } + ], + "Version": "2012-10-17" + }, + "FileSystemTags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/FileSystem" + } + ], + "LifecyclePolicies": [ + { + "TransitionToIA": "AFTER_14_DAYS" + }, + { + "TransitionToPrimaryStorageClass": "AFTER_1_ACCESS" + }, + { + "TransitionToArchive": "AFTER_90_DAYS" + } + ] + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "FileSystemEfsSecurityGroup212D3ACB": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "test-efs-transition-integ/FileSystem/EfsSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "test-efs-transition-integ/FileSystem" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "FileSystemEfsMountTargetPrivateSubnet1BB305AF3": { + "Type": "AWS::EFS::MountTarget", + "Properties": { + "FileSystemId": { + "Ref": "FileSystem8A8E25C0" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "FileSystemEfsSecurityGroup212D3ACB", + "GroupId" + ] + } + ], + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "FileSystemEfsMountTargetPrivateSubnet265F3ED67": { + "Type": "AWS::EFS::MountTarget", + "Properties": { + "FileSystemId": { + "Ref": "FileSystem8A8E25C0" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "FileSystemEfsSecurityGroup212D3ACB", + "GroupId" + ] + } + ], + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + } + }, + "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-efs/test/integ.efs-transition.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json new file mode 100644 index 0000000000000..5808bd55fd3ee --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "testefsintegtestDefaultTestDeployAssert7E1529D5.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-efs/test/integ.efs-transition.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.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-efs/test/integ.efs-transition.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/tree.json new file mode 100644 index 0000000000000..d71614338e5a7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.js.snapshot/tree.json @@ -0,0 +1,848 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "test-efs-transition-integ": { + "id": "test-efs-transition-integ", + "path": "test-efs-transition-integ", + "children": { + "Vpc": { + "id": "Vpc", + "path": "test-efs-transition-integ/Vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "test-efs-transition-integ/Vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "2.118.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.118.0" + } + }, + "Acl": { + "id": "Acl", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.118.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.118.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.118.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.118.0" + } + }, + "EIP": { + "id": "EIP", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "2.118.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "test-efs-transition-integ/Vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "2.118.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "test-efs-transition-integ/Vpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "test-efs-transition-integ/Vpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.118.0" + } + }, + "Acl": { + "id": "Acl", + "path": "test-efs-transition-integ/Vpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.118.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "test-efs-transition-integ/Vpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.118.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "test-efs-transition-integ/Vpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.118.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "test-efs-transition-integ/Vpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "2.118.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.118.0" + } + }, + "Acl": { + "id": "Acl", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.118.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.118.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.118.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "2.118.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.118.0" + } + }, + "Acl": { + "id": "Acl", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.118.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.118.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.118.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "test-efs-transition-integ/Vpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "2.118.0" + } + }, + "IGW": { + "id": "IGW", + "path": "test-efs-transition-integ/Vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "2.118.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "test-efs-transition-integ/Vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "2.118.0" + } + }, + "FileSystem": { + "id": "FileSystem", + "path": "test-efs-transition-integ/FileSystem", + "children": { + "Resource": { + "id": "Resource", + "path": "test-efs-transition-integ/FileSystem/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EFS::FileSystem", + "aws:cdk:cloudformation:props": { + "encrypted": true, + "fileSystemPolicy": { + "Statement": [ + { + "Action": [ + "elasticfilesystem:ClientRootAccess", + "elasticfilesystem:ClientWrite" + ], + "Condition": { + "Bool": { + "elasticfilesystem:AccessedViaMountTarget": "true" + } + }, + "Effect": "Allow", + "Principal": { + "AWS": "*" + } + } + ], + "Version": "2012-10-17" + }, + "lifecyclePolicies": [ + { + "transitionToIa": "AFTER_14_DAYS" + }, + { + "transitionToPrimaryStorageClass": "AFTER_1_ACCESS" + } + ], + "fileSystemTags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/FileSystem" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_efs.CfnFileSystem", + "version": "2.118.0" + } + }, + "EfsSecurityGroup": { + "id": "EfsSecurityGroup", + "path": "test-efs-transition-integ/FileSystem/EfsSecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "test-efs-transition-integ/FileSystem/EfsSecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "test-efs-transition-integ/FileSystem/EfsSecurityGroup", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "tags": [ + { + "key": "Name", + "value": "test-efs-transition-integ/FileSystem" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "2.118.0" + } + }, + "EfsMountTarget-PrivateSubnet1": { + "id": "EfsMountTarget-PrivateSubnet1", + "path": "test-efs-transition-integ/FileSystem/EfsMountTarget-PrivateSubnet1", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EFS::MountTarget", + "aws:cdk:cloudformation:props": { + "fileSystemId": { + "Ref": "FileSystem8A8E25C0" + }, + "securityGroups": [ + { + "Fn::GetAtt": [ + "FileSystemEfsSecurityGroup212D3ACB", + "GroupId" + ] + } + ], + "subnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_efs.CfnMountTarget", + "version": "2.118.0" + } + }, + "EfsMountTarget-PrivateSubnet2": { + "id": "EfsMountTarget-PrivateSubnet2", + "path": "test-efs-transition-integ/FileSystem/EfsMountTarget-PrivateSubnet2", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EFS::MountTarget", + "aws:cdk:cloudformation:props": { + "fileSystemId": { + "Ref": "FileSystem8A8E25C0" + }, + "securityGroups": [ + { + "Fn::GetAtt": [ + "FileSystemEfsSecurityGroup212D3ACB", + "GroupId" + ] + } + ], + "subnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_efs.CfnMountTarget", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_efs.FileSystem", + "version": "2.118.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "test-efs-transition-integ/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.118.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "test-efs-transition-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "2.118.0" + } + }, + "test-efs-integ-test": { + "id": "test-efs-integ-test", + "path": "test-efs-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "test-efs-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "test-efs-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "test-efs-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "test-efs-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.118.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "test-efs-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "2.118.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "2.118.0-alpha.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "2.118.0-alpha.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "2.118.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.ts new file mode 100644 index 0000000000000..9d669f2c34db4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs-transition.ts @@ -0,0 +1,20 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib'; +import { FileSystem, LifecyclePolicy, OutOfInfrequentAccessPolicy } from 'aws-cdk-lib/aws-efs'; +import * as integ from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'test-efs-transition-integ'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 3, natGateways: 1, restrictDefaultSecurityGroup: false }); + +new FileSystem(stack, 'FileSystem', { + vpc, + lifecyclePolicy: LifecyclePolicy.AFTER_14_DAYS, + transitionToArchivePolicy: LifecyclePolicy.AFTER_90_DAYS, + outOfInfrequentAccessPolicy: OutOfInfrequentAccessPolicy.AFTER_1_ACCESS, +}); + +new integ.IntegTest(app, 'test-efs-integ-test', { + testCases: [stack], +}); \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json new file mode 100644 index 0000000000000..1b0f19292ce14 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.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-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.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-route53/test/integ.weighted-record.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/integ.json new file mode 100644 index 0000000000000..2b9f64b17cdbf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "Route53WeightedRecordInteg/DefaultTest": { + "stacks": [ + "weighted-record" + ], + "assertionStack": "Route53WeightedRecordInteg/DefaultTest/DeployAssert", + "assertionStackName": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/manifest.json new file mode 100644 index 0000000000000..19af9778152e4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "version": "36.0.0", + "artifacts": { + "weighted-record.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "weighted-record.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "weighted-record": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "weighted-record.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}/aaa3e08f1a3f807fd7d4163e5ad01d2400b1f162ead6be3e788281f64fb3f021.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "weighted-record.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": [ + "weighted-record.assets" + ], + "metadata": { + "/weighted-record/HostedZone/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedZoneDB99F866" + } + ], + "/weighted-record/WeightedRecord0/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "WeightedRecord0183C6356" + } + ], + "/weighted-record/WeightedRecord1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "WeightedRecord1901777B1" + } + ], + "/weighted-record/WeightedRecord2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "WeightedRecord2D4D415A2" + } + ], + "/weighted-record/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/weighted-record/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "weighted-record" + }, + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.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": [ + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.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": [ + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets" + ], + "metadata": { + "/Route53WeightedRecordInteg/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Route53WeightedRecordInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Route53WeightedRecordInteg/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-route53/test/integ.weighted-record.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/tree.json new file mode 100644 index 0000000000000..29e50fd5b0cd2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/tree.json @@ -0,0 +1,227 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "weighted-record": { + "id": "weighted-record", + "path": "weighted-record", + "children": { + "HostedZone": { + "id": "HostedZone", + "path": "weighted-record/HostedZone", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/HostedZone/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HostedZone", + "aws:cdk:cloudformation:props": { + "name": "cdk.dev." + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHostedZone", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.PublicHostedZone", + "version": "0.0.0" + } + }, + "WeightedRecord0": { + "id": "WeightedRecord0", + "path": "weighted-record/WeightedRecord0", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/WeightedRecord0/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "www.cdk.dev.", + "resourceRecords": [ + "1.2.3.4" + ], + "setIdentifier": "WEIGHT_20_ID_weightedrecordWeightedRecord036EC06FB", + "ttl": "10", + "type": "A", + "weight": 20 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "WeightedRecord1": { + "id": "WeightedRecord1", + "path": "weighted-record/WeightedRecord1", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/WeightedRecord1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "www.cdk.dev.", + "resourceRecords": [ + "2.3.4.5" + ], + "setIdentifier": "WEIGHT_30_ID_weightedrecordWeightedRecord1AB70F3F7", + "ttl": "10", + "type": "A", + "weight": 30 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "WeightedRecord2": { + "id": "WeightedRecord2", + "path": "weighted-record/WeightedRecord2", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/WeightedRecord2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "www.cdk.dev.", + "resourceRecords": [ + "3.4.5.6" + ], + "setIdentifier": "WEIGHT_50_ID_weightedrecordWeightedRecord2896F8B34", + "ttl": "10", + "type": "A", + "weight": 50 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "weighted-record/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "weighted-record/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "Route53WeightedRecordInteg": { + "id": "Route53WeightedRecordInteg", + "path": "Route53WeightedRecordInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "Route53WeightedRecordInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "Route53WeightedRecordInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "Route53WeightedRecordInteg/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Route53WeightedRecordInteg/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Route53WeightedRecordInteg/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.3.0" + } + } + }, + "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-route53/test/integ.weighted-record.js.snapshot/weighted-record.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.assets.json new file mode 100644 index 0000000000000..09cc2ad6e14b8 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "aaa3e08f1a3f807fd7d4163e5ad01d2400b1f162ead6be3e788281f64fb3f021": { + "source": { + "path": "weighted-record.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "aaa3e08f1a3f807fd7d4163e5ad01d2400b1f162ead6be3e788281f64fb3f021.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-route53/test/integ.weighted-record.js.snapshot/weighted-record.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.template.json new file mode 100644 index 0000000000000..245ed234bbc91 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.template.json @@ -0,0 +1,92 @@ +{ + "Resources": { + "HostedZoneDB99F866": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "cdk.dev." + } + }, + "WeightedRecord0183C6356": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "www.cdk.dev.", + "ResourceRecords": [ + "1.2.3.4" + ], + "SetIdentifier": "WEIGHT_20_ID_weightedrecordWeightedRecord036EC06FB", + "TTL": "10", + "Type": "A", + "Weight": 20 + } + }, + "WeightedRecord1901777B1": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "www.cdk.dev.", + "ResourceRecords": [ + "2.3.4.5" + ], + "SetIdentifier": "WEIGHT_30_ID_weightedrecordWeightedRecord1AB70F3F7", + "TTL": "10", + "Type": "A", + "Weight": 30 + } + }, + "WeightedRecord2D4D415A2": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "www.cdk.dev.", + "ResourceRecords": [ + "3.4.5.6" + ], + "SetIdentifier": "WEIGHT_50_ID_weightedrecordWeightedRecord2896F8B34", + "TTL": "10", + "Type": "A", + "Weight": 50 + } + } + }, + "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-route53/test/integ.weighted-record.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.ts new file mode 100644 index 0000000000000..7c6d9a3aa299b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.ts @@ -0,0 +1,36 @@ +import { App, Duration, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const hostedZone = new route53.PublicHostedZone(this, 'HostedZone', { + zoneName: 'cdk.dev', + }); + + [ + { target: '1.2.3.4', weight: 20 }, + { target: '2.3.4.5', weight: 30 }, + { target: '3.4.5.6', weight: 50 }, + ].forEach((data, index) => { + new route53.ARecord(this, `WeightedRecord${index}`, { + zone: hostedZone, + recordName: 'www', + weight: data.weight, + ttl: Duration.seconds(10), + target: route53.RecordTarget.fromIpAddresses(data.target), + }); + }); + } +} + +const app = new App(); +const stack = new TestStack(app, 'weighted-record'); + +new IntegTest(app, 'Route53WeightedRecordInteg', { + testCases: [stack], +}); +app.synth(); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/README.md b/packages/@aws-cdk/aws-appconfig-alpha/README.md index b29fa5707a26d..f7a658fdc5ab8 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/README.md +++ b/packages/@aws-cdk/aws-appconfig-alpha/README.md @@ -39,7 +39,7 @@ Create an application with a name and description: ```ts new appconfig.Application(this, 'MyApplication', { - name: 'App1', + applicationName: 'App1', description: 'This is my application created through CDK.', }); ``` @@ -70,6 +70,22 @@ new appconfig.DeploymentStrategy(this, 'MyDeploymentStrategy', { }); ``` +Importing a deployment strategy by ID: + +```ts +appconfig.DeploymentStrategy.fromDeploymentStrategyId(this, 'MyImportedDeploymentStrategy', appconfig.DeploymentStrategyId.fromString('abc123')); +``` + +Importing an AWS AppConfig predefined deployment strategy by ID: + +```ts +appconfig.DeploymentStrategy.fromDeploymentStrategyId( + this, + 'MyImportedPredefinedDeploymentStrategy', + appconfig.DeploymentStrategyId.CANARY_10_PERCENT_20_MINUTES, +); +``` + ## Configuration A configuration is a higher-level construct that can either be a `HostedConfiguration` (stored internally through AWS diff --git a/packages/@aws-cdk/aws-appconfig-alpha/awslint.json b/packages/@aws-cdk/aws-appconfig-alpha/awslint.json index ddb778bafd565..830aee3b4182b 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/awslint.json +++ b/packages/@aws-cdk/aws-appconfig-alpha/awslint.json @@ -25,11 +25,7 @@ "docs-public-apis:@aws-cdk/aws-appconfig-alpha.IConfiguration", "docs-public-apis:@aws-cdk/aws-appconfig-alpha.IApplication", - "no-unused-type:@aws-cdk/aws-appconfig-alpha.PredefinedDeploymentStrategyId", - "props-physical-name:@aws-cdk/aws-appconfig-alpha.ApplicationProps", - "props-physical-name:@aws-cdk/aws-appconfig-alpha.DeploymentStrategyProps", - "props-physical-name:@aws-cdk/aws-appconfig-alpha.EnvironmentProps", - "props-physical-name:@aws-cdk/aws-appconfig-alpha.ExtensionProps", + "from-signature:@aws-cdk/aws-appconfig-alpha.DeploymentStrategy.fromDeploymentStrategyId.params[2]", "events-method-signature:@aws-cdk/aws-appconfig-alpha.Application.on", "events-method-signature:@aws-cdk/aws-appconfig-alpha.Application.onDeploymentBaking*", "events-method-signature:@aws-cdk/aws-appconfig-alpha.Application.onDeploymentComplete*", diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts index 330bf68fad397..3d1d57858f03b 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts @@ -163,7 +163,7 @@ export interface ApplicationProps { * * @default - A name is generated. */ - readonly name?: string; + readonly applicationName?: string; /** * The description for the application. @@ -415,7 +415,7 @@ export class Application extends ApplicationBase { super(scope, id); this.description = props.description; - this.name = props.name || Names.uniqueResourceName(this, { + this.name = props.applicationName || Names.uniqueResourceName(this, { maxLength: 64, separator: '-', }); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts index a97c6ff02d038..83b3e9c2a88bd 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts @@ -18,7 +18,7 @@ export interface DeploymentStrategyProps { * * @default - A name is generated. */ - readonly name?: string; + readonly deploymentStrategyName?: string; /** * A description of the deployment strategy. @@ -66,16 +66,16 @@ export class DeploymentStrategy extends Resource implements IDeploymentStrategy * @param id The name of the deployment strategy construct * @param deploymentStrategyId The ID of the deployment strategy */ - public static fromDeploymentStrategyId(scope: Construct, id: string, deploymentStrategyId: string): IDeploymentStrategy { + public static fromDeploymentStrategyId(scope: Construct, id: string, deploymentStrategyId: DeploymentStrategyId): IDeploymentStrategy { const stack = Stack.of(scope); const deploymentStrategyArn = stack.formatArn({ service: 'appconfig', resource: 'deploymentstrategy', - resourceName: deploymentStrategyId, + resourceName: deploymentStrategyId.id, }); class Import extends Resource implements IDeploymentStrategy { - public readonly deploymentStrategyId = deploymentStrategyId; + public readonly deploymentStrategyId = deploymentStrategyId.id; public readonly deploymentStrategyArn = deploymentStrategyArn; } @@ -130,7 +130,7 @@ export class DeploymentStrategy extends Resource implements IDeploymentStrategy constructor(scope: Construct, id: string, props: DeploymentStrategyProps) { super(scope, id, { - physicalName: props.name, + physicalName: props.deploymentStrategyName, }); this.deploymentDurationInMinutes = props.rolloutStrategy.deploymentDuration.toMinutes(); @@ -138,7 +138,7 @@ export class DeploymentStrategy extends Resource implements IDeploymentStrategy this.description = props.description; this.finalBakeTimeInMinutes = props.rolloutStrategy.finalBakeTime?.toMinutes(); this.growthType = props.rolloutStrategy.growthType; - this.name = props.name || Names.uniqueResourceName(this, { + this.name = props.deploymentStrategyName || Names.uniqueResourceName(this, { maxLength: 64, separator: '-', }); @@ -182,36 +182,52 @@ export enum GrowthType { } /** - * Defines the deployment strategy ID's of AWS AppConfig predefined strategies. + * Defines the deployment strategy ID's of AWS AppConfig deployment strategies. * * @see https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-deployment-strategy.html */ -export enum PredefinedDeploymentStrategyId { +export abstract class DeploymentStrategyId { /** * **AWS Recommended**. This strategy processes the deployment exponentially using a 10% growth factor over 20 minutes. * AWS AppConfig recommends using this strategy for production deployments because it aligns with AWS best practices * for configuration deployments. */ - CANARY_10_PERCENT_20_MINUTES = 'AppConfig.Canary10Percent20Minutes', + public static readonly CANARY_10_PERCENT_20_MINUTES = DeploymentStrategyId.fromString('AppConfig.Canary10Percent20Minutes'); /** * **Testing/Demonstration**. This strategy deploys the configuration to half of all targets every 30 seconds for a * one-minute deployment. AWS AppConfig recommends using this strategy only for testing or demonstration purposes because * it has a short duration and bake time. */ - LINEAR_50_PERCENT_EVERY_30_SECONDS = 'AppConfig.Linear50PercentEvery30Seconds', + public static readonly LINEAR_50_PERCENT_EVERY_30_SECONDS = DeploymentStrategyId.fromString('AppConfig.Linear50PercentEvery30Seconds'); /** * **AWS Recommended**. This strategy deploys the configuration to 20% of all targets every six minutes for a 30 minute deployment. * AWS AppConfig recommends using this strategy for production deployments because it aligns with AWS best practices * for configuration deployments. */ - LINEAR_20_PERCENT_EVERY_6_MINUTES = 'AppConfig.Linear20PercentEvery6Minutes', + public static readonly LINEAR_20_PERCENT_EVERY_6_MINUTES = DeploymentStrategyId.fromString('AppConfig.Linear20PercentEvery6Minutes'); /** * **Quick**. This strategy deploys the configuration to all targets immediately. */ - ALL_AT_ONCE = 'AppConfig.AllAtOnce', + public static readonly ALL_AT_ONCE = DeploymentStrategyId.fromString('AppConfig.AllAtOnce'); + + /** + * Builds a deployment strategy ID from a string. + * + * @param deploymentStrategyId The deployment strategy ID. + */ + public static fromString(deploymentStrategyId: string): DeploymentStrategyId { + return { + id: deploymentStrategyId, + }; + } + + /** + * The deployment strategy ID. + */ + public abstract readonly id: string; } /** diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts index e4216a569ab87..767373f92da81 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts @@ -94,7 +94,7 @@ export interface EnvironmentOptions { * * @default - A name is generated. */ - readonly name?: string; + readonly environmentName?: string; /** * The description of the environment. @@ -236,10 +236,10 @@ export class Environment extends EnvironmentBase { constructor(scope: Construct, id: string, props: EnvironmentProps) { super(scope, id, { - physicalName: props.name, + physicalName: props.environmentName, }); - this.name = props.name || Names.uniqueResourceName(this, { + this.name = props.environmentName || Names.uniqueResourceName(this, { maxLength: 64, separator: '-', }); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts index 72f6d1bc9c739..5d9f1164d3ac2 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts @@ -338,7 +338,7 @@ export interface ExtensionOptions { * * @default - A name is generated. */ - readonly name?: string; + readonly extensionName?: string; /** * A description of the extension @@ -493,11 +493,11 @@ export class Extension extends Resource implements IExtension { constructor(scope: Construct, id: string, props: ExtensionProps) { super(scope, id, { - physicalName: props.name, + physicalName: props.extensionName, }); this.actions = props.actions; - this.name = props.name || Names.uniqueResourceName(this, { + this.name = props.extensionName || Names.uniqueResourceName(this, { maxLength: 64, separator: '-', }); @@ -662,7 +662,7 @@ export class ExtensibleBase implements IExtensible { } private getExtensionForActionPoint(eventDestination: IEventDestination, actionPoint: ActionPoint, options?: ExtensionOptions) { - const name = options?.name || this.getExtensionDefaultName(); + const name = options?.extensionName || this.getExtensionDefaultName(); const versionNumber = options?.latestVersionNumber ? options?.latestVersionNumber + 1 : 1; const extension = new Extension(this.scope, `Extension${this.getExtensionHash(name, versionNumber)}`, { actions: [ @@ -673,7 +673,7 @@ export class ExtensibleBase implements IExtensible { ], }), ], - name, + extensionName: name, ...(options?.description ? { description: options.description } : {}), ...(options?.latestVersionNumber ? { latestVersionNumber: options.latestVersionNumber } : {}), ...(options?.parameters ? { parameters: options.parameters } : {}), diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts index a5c85dad77d95..deda4253bcf4d 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts @@ -23,7 +23,7 @@ describe('appconfig', () => { test('appconfig with name', () => { const stack = new cdk.Stack(); new Application(stack, 'MyAppConfig', { - name: 'TestApp', + applicationName: 'TestApp', }); Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Application', { @@ -121,7 +121,7 @@ describe('appconfig', () => { }); appconfig.preCreateHostedConfigurationVersion(new LambdaDestination(func), { description: 'This is my description', - name: 'MyExtension', + extensionName: 'MyExtension', latestVersionNumber: 1, parameters: [ Parameter.required('myparam', 'val'), diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts index 489c95dfd818e..f6564708642f1 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts @@ -58,7 +58,7 @@ describe('configuration', () => { test('configuration with environments and no deployTo prop', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); app.addEnvironment('MyEnv1'); app.addEnvironment('MyEnv2'); @@ -96,7 +96,7 @@ describe('configuration', () => { test('configuration with environments and deployTo prop', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); app.addEnvironment('MyEnv1'); const env = app.addEnvironment('MyEnv2'); @@ -152,7 +152,7 @@ describe('configuration', () => { test('configuration using deploy method and no environment associated', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); app.addEnvironment('MyEnv1'); const env = app.addEnvironment('MyEnv2'); @@ -191,7 +191,7 @@ describe('configuration', () => { test('configuration using deploy method with environment associated', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); const env1 = app.addEnvironment('MyEnv1'); const env2 = app.addEnvironment('MyEnv2'); @@ -248,7 +248,7 @@ describe('configuration', () => { test('configuration with no environment associated and no deploy method used', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); new HostedConfiguration(stack, 'MyHostedConfig', { content: ConfigurationContent.fromInlineText('This is my content'), @@ -267,7 +267,7 @@ describe('configuration', () => { test('configuration with two configurations specified', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); const env1 = app.addEnvironment('MyEnv1'); const env2 = app.addEnvironment('MyEnv2'); @@ -382,7 +382,7 @@ describe('configuration', () => { test('configuration with two configurations and no deployment strategy specified', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig', { - name: 'MyApplication', + applicationName: 'MyApplication', }); const bucket = new Bucket(stack, 'MyBucket'); new HostedConfiguration(stack, 'MyHostedConfig', { diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts index 14e4e1875af25..2e0d51e1d966f 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts @@ -1,7 +1,7 @@ import * as cdk from 'aws-cdk-lib'; import { App } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; -import { DeploymentStrategy, PredefinedDeploymentStrategyId, RolloutStrategy } from '../lib'; +import { DeploymentStrategy, DeploymentStrategyId, RolloutStrategy } from '../lib'; describe('deployment strategy', () => { test('default deployment strategy', () => { @@ -25,7 +25,7 @@ describe('deployment strategy', () => { test('deployment strategy with name', () => { const stack = new cdk.Stack(); new DeploymentStrategy(stack, 'MyDeploymentStrategy', { - name: 'TestDeploymentStrategy', + deploymentStrategyName: 'TestDeploymentStrategy', rolloutStrategy: RolloutStrategy.linear({ growthFactor: 10, deploymentDuration: cdk.Duration.minutes(10), @@ -44,7 +44,7 @@ describe('deployment strategy', () => { test('deployment strategy duration in seconds', () => { const stack = new cdk.Stack(); new DeploymentStrategy(stack, 'MyDeploymentStrategy', { - name: 'TestDeploymentStrategy', + deploymentStrategyName: 'TestDeploymentStrategy', rolloutStrategy: RolloutStrategy.linear({ growthFactor: 10, deploymentDuration: cdk.Duration.seconds(120), @@ -63,7 +63,7 @@ describe('deployment strategy', () => { test('deployment strategy with description', () => { const stack = new cdk.Stack(); new DeploymentStrategy(stack, 'MyDeploymentStrategy', { - name: 'TestDeploymentStrategy', + deploymentStrategyName: 'TestDeploymentStrategy', rolloutStrategy: RolloutStrategy.linear({ growthFactor: 10, deploymentDuration: cdk.Duration.minutes(10), @@ -84,7 +84,7 @@ describe('deployment strategy', () => { test('deployment strategy with final bake time', () => { const stack = new cdk.Stack(); new DeploymentStrategy(stack, 'MyDeploymentStrategy', { - name: 'TestDeploymentStrategy', + deploymentStrategyName: 'TestDeploymentStrategy', rolloutStrategy: RolloutStrategy.linear({ growthFactor: 10, deploymentDuration: cdk.Duration.minutes(10), @@ -105,7 +105,7 @@ describe('deployment strategy', () => { test('deployment strategy with growth type', () => { const stack = new cdk.Stack(); new DeploymentStrategy(stack, 'MyDeploymentStrategy', { - name: 'TestDeploymentStrategy', + deploymentStrategyName: 'TestDeploymentStrategy', rolloutStrategy: RolloutStrategy.exponential({ growthFactor: 10, deploymentDuration: cdk.Duration.minutes(10), @@ -124,7 +124,7 @@ describe('deployment strategy', () => { test('deployment strategy with replicate to', () => { const stack = new cdk.Stack(); new DeploymentStrategy(stack, 'MyDeploymentStrategy', { - name: 'TestDeploymentStrategy', + deploymentStrategyName: 'TestDeploymentStrategy', rolloutStrategy: RolloutStrategy.linear({ growthFactor: 10, deploymentDuration: cdk.Duration.minutes(10), @@ -166,7 +166,7 @@ describe('deployment strategy', () => { account: '123456789012', }, }); - const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', 'abc123'); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', DeploymentStrategyId.fromString('abc123')); expect(deploymentStrategy.deploymentStrategyId).toEqual('abc123'); expect(deploymentStrategy.env.account).toEqual('123456789012'); @@ -181,7 +181,7 @@ describe('deployment strategy', () => { account: '123456789012', }, }); - const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', PredefinedDeploymentStrategyId.ALL_AT_ONCE); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', DeploymentStrategyId.ALL_AT_ONCE); expect(deploymentStrategy.deploymentStrategyId).toEqual('AppConfig.AllAtOnce'); expect(deploymentStrategy.env.account).toEqual('123456789012'); @@ -196,7 +196,7 @@ describe('deployment strategy', () => { account: '123456789012', }, }); - const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', PredefinedDeploymentStrategyId.CANARY_10_PERCENT_20_MINUTES); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', DeploymentStrategyId.CANARY_10_PERCENT_20_MINUTES); expect(deploymentStrategy.deploymentStrategyId).toEqual('AppConfig.Canary10Percent20Minutes'); expect(deploymentStrategy.env.account).toEqual('123456789012'); @@ -211,7 +211,7 @@ describe('deployment strategy', () => { account: '123456789012', }, }); - const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', PredefinedDeploymentStrategyId.LINEAR_50_PERCENT_EVERY_30_SECONDS); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', DeploymentStrategyId.LINEAR_50_PERCENT_EVERY_30_SECONDS); expect(deploymentStrategy.deploymentStrategyId).toEqual('AppConfig.Linear50PercentEvery30Seconds'); expect(deploymentStrategy.env.account).toEqual('123456789012'); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts index 9c6dd7c7f3e4b..261d113728dae 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts @@ -25,7 +25,7 @@ describe('environment', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig'); new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, }); @@ -41,7 +41,7 @@ describe('environment', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig'); new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, description: 'This is my description', }); @@ -72,7 +72,7 @@ describe('environment', () => { assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'), }); const env = new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [Monitor.fromCloudWatchAlarm(alarm, alarmRole)], }); @@ -118,7 +118,7 @@ describe('environment', () => { }); const app = new Application(stack, 'MyAppConfig'); const env = new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [Monitor.fromCloudWatchAlarm(alarm)], }); @@ -174,7 +174,7 @@ describe('environment', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig'); const env = new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [ Monitor.fromCfnMonitorsProperty({ @@ -203,7 +203,7 @@ describe('environment', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig'); const env = new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [ Monitor.fromCfnMonitorsProperty({ @@ -247,7 +247,7 @@ describe('environment', () => { alarmRule: alarm, }); const env = new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [ Monitor.fromCloudWatchAlarm(compositeAlarm), @@ -330,7 +330,7 @@ describe('environment', () => { alarmRule: alarm, }); const env = new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [ Monitor.fromCloudWatchAlarm(compositeAlarm1), @@ -433,7 +433,7 @@ describe('environment', () => { ), }); new Environment(stack, 'MyEnvironment', { - name: 'TestEnv', + environmentName: 'TestEnv', application: app, monitors: [ Monitor.fromCloudWatchAlarm(alarm1), diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts index 4bef613850bbf..9109975c31cad 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts @@ -185,7 +185,7 @@ describe('extension', () => { value: 'arn:lambda:us-east-1:123456789012:function:my-function', }); const appconfig = new Application(stack, 'MyApplication', { - name: 'MyApplication', + applicationName: 'MyApplication', }); const ext = new Extension(stack, 'MyExtension', { actions: [ @@ -196,7 +196,7 @@ describe('extension', () => { eventDestination: new LambdaDestination(func), }), ], - name: 'TestExtension', + extensionName: 'TestExtension', description: 'This is my extension', parameters: [ Parameter.required('testVariable', 'hello'), diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts index bf6e396bc8f51..d893c415ec5b0 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts @@ -38,7 +38,7 @@ const stack = new Stack(app, 'aws-appconfig-configuration'); // create application for config profile const appConfigApp = new Application(stack, 'MyAppConfig', { - name: 'AppForConfigTest', + applicationName: 'AppForConfigTest', }); const deploymentStrategy = new DeploymentStrategy(stack, 'MyDeployStrategy', { diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts index bce1591e70ba8..6681c66597bd4 100644 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts @@ -10,7 +10,7 @@ const stack = new Stack(app, 'aws-appconfig-environment'); // create resources needed for environment const appForEnv = new Application(stack, 'MyApplicationForEnv', { - name: 'AppForEnvTest', + applicationName: 'AppForEnvTest', }); const alarm = new Alarm(stack, 'MyAlarm', { metric: new Metric({ diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts index 94b3c38897037..0fdfd86d92e80 100755 --- a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts @@ -31,7 +31,7 @@ const lambda = new Function(stack, 'MyFunction', { code: Code.fromInline('def handler(event, context):\n\tprint(\'The function has been invoked.\')'), }); const app = new Application(stack, 'MyApplication', { - name: 'AppForExtensionTest', + applicationName: 'AppForExtensionTest', }); const lambdaExtension = new Extension(stack, 'MyLambdaExtension', { actions: [ diff --git a/packages/aws-cdk-lib/aws-ec2/lib/volume.ts b/packages/aws-cdk-lib/aws-ec2/lib/volume.ts index 2c29b6e6dbbd9..5b349c2ecd02e 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/volume.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/volume.ts @@ -344,7 +344,7 @@ export interface VolumeProps { /** * The size of the volume, in GiBs. You must specify either a snapshot ID or a volume size. - * See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html + * See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html * for details on the allowable size for each type of volume. * * @default If you're creating the volume from a snapshot and don't specify a volume size, the default is the snapshot size. @@ -427,7 +427,7 @@ export interface VolumeProps { /** * The number of I/O operations per second (IOPS) to provision for the volume. The maximum ratio is 50 IOPS/GiB for PROVISIONED_IOPS_SSD, * and 500 IOPS/GiB for both PROVISIONED_IOPS_SSD_IO2 and GENERAL_PURPOSE_SSD_GP3. - * See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html + * See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html * for more information. * * This parameter is valid only for PROVISIONED_IOPS_SSD, PROVISIONED_IOPS_SSD_IO2 and GENERAL_PURPOSE_SSD_GP3 volumes. @@ -446,7 +446,7 @@ export interface VolumeProps { /** * The throughput that the volume supports, in MiB/s * Takes a minimum of 125 and maximum of 1000. - * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html#cfn-ec2-ebs-volume-throughput + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html#cfn-ec2-volume-throughput * @default - 125 MiB/s. Only valid on gp3 volumes. */ readonly throughput?: number; @@ -691,11 +691,11 @@ export class Volume extends VolumeBase { ); } // Enforce minimum & maximum IOPS: - // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html const iopsRanges: { [key: string]: { Min: number, Max: number } } = {}; iopsRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3] = { Min: 3000, Max: 16000 }; iopsRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD] = { Min: 100, Max: 64000 }; - iopsRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2] = { Min: 100, Max: 64000 }; + iopsRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2] = { Min: 100, Max: 256000 }; const { Min, Max } = iopsRanges[volumeType]; if (props.iops < Min || props.iops > Max) { throw new Error(`\`${volumeType}\` volumes iops must be between ${Min} and ${Max}.`); @@ -739,7 +739,7 @@ export class Volume extends VolumeBase { if (props.size) { const size = props.size.toGibibytes({ rounding: SizeRoundingBehavior.FAIL }); // Enforce minimum & maximum volume size: - // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html const sizeRanges: { [key: string]: { Min: number, Max: number } } = {}; sizeRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD] = { Min: 1, Max: 16384 }; sizeRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3] = { Min: 1, Max: 16384 }; diff --git a/packages/aws-cdk-lib/aws-ec2/test/volume.test.ts b/packages/aws-cdk-lib/aws-ec2/test/volume.test.ts index ffe1ef38a9c17..cce55f2497bc8 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/volume.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/volume.test.ts @@ -1279,7 +1279,7 @@ describe('volume', () => { for (const testData of [ [EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, 3000, 16000], [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, 100, 64000], - [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, 100, 64000], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, 100, 256000], ]) { const volumeType = testData[0] as EbsDeviceVolumeType; const min = testData[1] as number; diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index b120df0bf0049..3b0db4577e94c 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -1631,6 +1631,89 @@ const customService = new ecs.FargateService(this, 'CustomizedService', { }); ``` +## ServiceManagedVolume + +Amazon ECS now supports the attachment of Amazon Elastic Block Store (EBS) volumes to ECS tasks, +allowing you to utilize persistent, high-performance block storage with your ECS services. +This feature supports various use cases, such as using EBS volumes as extended ephemeral storage or +loading data from EBS snapshots. +You can also specify `encrypted: true` so that ECS will manage the KMS key. If you want to use your own KMS key, you may do so by providing both `encrypted: true` and `kmsKeyId`. + +You can only attach a single volume for each task in the ECS Service. + +To add an empty EBS Volume to an ECS Service, call service.addVolume(). + +```ts +declare const cluster: ecs.Cluster; +const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef'); + +const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + portMappings: [{ + containerPort: 80, + protocol: ecs.Protocol.TCP, + }], +}); + +const volume = new ecs.ServiceManagedVolume(this, 'EBSVolume', { + name: 'ebs1', + managedEBSVolume: { + size: Size.gibibytes(15), + volumeType: ec2.EbsDeviceVolumeType.GP3, + fileSystemType: ecs.FileSystemType.XFS, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, +}); + +volume.mountIn(container, { + containerPath: '/var/lib', + readOnly: false, +}); + +taskDefinition.addVolume(volume); + +const service = new ecs.FargateService(this, 'FargateService', { + cluster, + taskDefinition, +}); + +service.addVolume(volume); +``` + +To create an EBS volume from an existing snapshot by specifying the `snapShotId` while adding a volume to the service. + +```ts +declare const container: ecs.ContainerDefinition; +declare const cluster: ecs.Cluster; +declare const taskDefinition: ecs.TaskDefinition; + +const volumeFromSnapshot = new ecs.ServiceManagedVolume(this, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + snapShotId: 'snap-066877671789bd71b', + volumeType: ec2.EbsDeviceVolumeType.GP3, + fileSystemType: ecs.FileSystemType.XFS, + }, +}); + +volumeFromSnapshot.mountIn(container, { + containerPath: '/var/lib', + readOnly: false, +}); +taskDefinition.addVolume(volumeFromSnapshot); +const service = new ecs.FargateService(this, 'FargateService', { + cluster, + taskDefinition, +}); + +service.addVolume(volumeFromSnapshot); +``` + ## Enable pseudo-terminal (TTY) allocation You can allocate a pseudo-terminal (TTY) for a container passing `pseudoTerminal` option while adding the container diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts index 9726ef21526ce..00c0941db1022 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts @@ -1,5 +1,6 @@ import { Construct } from 'constructs'; import { ScalableTaskCount } from './scalable-task-count'; +import { ServiceManagedVolume } from './service-managed-volume'; import * as appscaling from '../../../aws-applicationautoscaling'; import * as cloudwatch from '../../../aws-cloudwatch'; import * as ec2 from '../../../aws-ec2'; @@ -357,6 +358,14 @@ export interface BaseServiceOptions { * @default - Uses the revision of the passed task definition deployed by CloudFormation */ readonly taskDefinitionRevision?: TaskDefinitionRevision; + + /** + * Configuration details for a volume used by the service. This allows you to specify + * details about the EBS volume that can be attched to ECS tasks. + * + * @default - undefined + */ + readonly volumeConfigurations?: ServiceManagedVolume[]; } /** @@ -576,6 +585,11 @@ export abstract class BaseService extends Resource private readonly resource: CfnService; private scalableTaskCount?: ScalableTaskCount; + /** + * All volumes + */ + private readonly volumes: ServiceManagedVolume[] = []; + /** * Constructs a new instance of the BaseService class. */ @@ -626,6 +640,7 @@ export abstract class BaseService extends Resource networkConfiguration: Lazy.any({ produce: () => this.networkConfiguration }, { omitEmptyArray: true }), serviceRegistries: Lazy.any({ produce: () => this.serviceRegistries }, { omitEmptyArray: true }), serviceConnectConfiguration: Lazy.any({ produce: () => this._serviceConnectConfig }, { omitEmptyArray: true }), + volumeConfigurations: Lazy.any({ produce: () => this.renderVolumes() }, { omitEmptyArray: true }), ...additionalProps, }); @@ -686,6 +701,10 @@ export abstract class BaseService extends Resource this.enableServiceConnect(props.serviceConnectConfiguration); } + if (props.volumeConfigurations) { + props.volumeConfigurations.forEach(v => this.addVolume(v)); + } + if (props.enableExecuteCommand) { this.enableExecuteCommand(); @@ -721,6 +740,48 @@ export abstract class BaseService extends Resource this.node.defaultChild = this.resource; } + /** + * Adds a volume to the Service. + */ + public addVolume(volume: ServiceManagedVolume) { + this.volumes.push(volume); + } + + private renderVolumes(): CfnService.ServiceVolumeConfigurationProperty[] { + if (this.volumes.length > 1) { + throw new Error(`Only one EBS volume can be specified for 'volumeConfigurations', got: ${this.volumes.length}`); + } + return this.volumes.map(renderVolume); + function renderVolume(spec: ServiceManagedVolume): CfnService.ServiceVolumeConfigurationProperty { + const tagSpecifications = spec.config?.tagSpecifications?.map(ebsTagSpec => { + return { + resourceType: 'volume', + propagateTags: ebsTagSpec.propagateTags, + tags: ebsTagSpec.tags ? Object.entries(ebsTagSpec.tags).map(([key, value]) => ({ + key: key, + value: value, + })) : undefined, + } as CfnService.EBSTagSpecificationProperty; + }); + + return { + name: spec.name, + managedEbsVolume: spec.config && { + roleArn: spec.role.roleArn, + encrypted: spec.config.encrypted, + filesystemType: spec.config.fileSystemType, + iops: spec.config.iops, + kmsKeyId: spec.config.kmsKeyId?.keyId, + throughput: spec.config.throughput, + volumeType: spec.config.volumeType, + snapshotId: spec.config.snapShotId, + sizeInGiB: spec.config.size?.toGibibytes(), + tagSpecifications: tagSpecifications, + }, + }; + } + } + /** * Enable Deployment Alarms which take advantage of arbitrary alarms and configure them after service initialization. * If you have already enabled deployment alarms, this function can be used to tell ECS about additional alarms that diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/service-managed-volume.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/service-managed-volume.ts new file mode 100644 index 0000000000000..6cea5e124224a --- /dev/null +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/service-managed-volume.ts @@ -0,0 +1,310 @@ +import { Construct } from 'constructs'; +import * as ec2 from '../../../aws-ec2'; +import * as iam from '../../../aws-iam'; +import * as kms from '../../../aws-kms'; +import { Size, Token } from '../../../core'; +import { BaseMountPoint, ContainerDefinition } from '../container-definition'; + +/** +* Represents the Volume configuration for an ECS service. +*/ +export interface ServiceManagedVolumeProps { + /** + * The name of the volume. This corresponds to the name provided in the ECS TaskDefinition. + */ + readonly name: string; + + /** + * Configuration for an Amazon Elastic Block Store (EBS) volume managed by ECS. + * + * @default - undefined + */ + readonly managedEBSVolume?: ServiceManagedEBSVolumeConfiguration +} + +/** +* Represents the configuration for an ECS Service managed EBS volume. +*/ +export interface ServiceManagedEBSVolumeConfiguration { + /** + * An IAM role that allows ECS to make calls to EBS APIs on your behalf. + * This role is required to create and manage the Amazon EBS volume. + * + * @default - automatically generated role. + */ + readonly role?: iam.IRole; + + /** + * Indicates whether the volume should be encrypted. + * + * @default - Default Amazon EBS encryption. + */ + readonly encrypted?: boolean; + + /** + * AWS Key Management Service key to use for Amazon EBS encryption. + * + * @default - When `encryption` is turned on and no `kmsKey` is specified, + * the default AWS managed key for Amazon EBS volumes is used. + */ + readonly kmsKeyId?: kms.IKey; + + /** + * The volume type. + * + * @default - ec2.EbsDeviceVolumeType.GP2 + */ + readonly volumeType?: ec2.EbsDeviceVolumeType; + + /** + * The size of the volume in GiB. + * + * You must specify either `size` or `snapshotId`. + * You can optionally specify a volume size greater than or equal to the snapshot size. + * + * The following are the supported volume size values for each volume type. + * - gp2 and gp3: 1-16,384 + * - io1 and io2: 4-16,384 + * - st1 and sc1: 125-16,384 + * - standard: 1-1,024 + * + * @default - The snapshot size is used for the volume size if you specify `snapshotId`, + * otherwise this parameter is required. + */ + readonly size?: Size; + + /** + * The snapshot that Amazon ECS uses to create the volume. + * + * You must specify either `size` or `snapshotId`. + * + * @default - No snapshot. + */ + readonly snapShotId?: string; + + /** + * The number of I/O operations per second (IOPS). + * + * For gp3, io1, and io2 volumes, this represents the number of IOPS that are provisioned + * for the volume. For gp2 volumes, this represents the baseline performance of the volume + * and the rate at which the volume accumulates I/O credits for bursting. + * + * The following are the supported values for each volume type. + * - gp3: 3,000 - 16,000 IOPS + * - io1: 100 - 64,000 IOPS + * - io2: 100 - 256,000 IOPS + * + * This parameter is required for io1 and io2 volume types. The default for gp3 volumes is + * 3,000 IOPS. This parameter is not supported for st1, sc1, or standard volume types. + * + * @default - undefined + */ + readonly iops?: number; + + /** + * The throughput to provision for a volume, in MiB/s, with a maximum of 1,000 MiB/s. + * + * This parameter is only supported for the gp3 volume type. + * + * @default - No throughput. + */ + readonly throughput?: number; + + /** + * The Linux filesystem type for the volume. + * + * For volumes created from a snapshot, you must specify the same filesystem type that + * the volume was using when the snapshot was created. + * The available filesystem types are ext3, ext4, and xfs. + * + * @default - FileSystemType.XFS + */ + readonly fileSystemType?: FileSystemType; + + /** + * Specifies the tags to apply to the volume and whether to propagate those tags to the volume. + * + * @default - No tags are specified. + */ + readonly tagSpecifications?: EBSTagSpecification[]; +} + +/** + * Tag Specification for EBS volume. + */ +export interface EBSTagSpecification { + /** + * The tags to apply to the volume. + * + * @default - No tags + */ + readonly tags?: {[key: string]: string}; + + /** + * Specifies whether to propagate the tags from the task definition or the service to the task. + * Valid values are: PropagatedTagSource.SERVICE, PropagatedTagSource.TASK_DEFINITION + * + * @default - undefined + */ + readonly propagateTags?: EbsPropagatedTagSource; +} + +/** + * FileSystemType for Service Managed EBS Volume Configuration. + */ +export enum FileSystemType { + /** + * ext3 type + */ + EXT3 = 'ext3', + /** + * ext4 type + */ + EXT4 = 'ext4', + /** + * xfs type + */ + XFS = 'xfs', +} + +/** + * Propagate tags for EBS Volume Configuration from either service or task definition. + */ +export enum EbsPropagatedTagSource { + /** + * SERVICE + */ + SERVICE = 'SERVICE', + /** + * TASK_DEFINITION + */ + TASK_DEFINITION = 'TASK_DEFINITION', +} + +/** + * Defines the mount point details for attaching a volume to a container. + */ +export interface ContainerMountPoint extends BaseMountPoint { +} + +/** + * Represents a service-managed volume and always configured at launch. + */ +export class ServiceManagedVolume extends Construct { + /** + * Name of the volume, referenced by taskdefintion and mount point. + */ + public readonly name: string; + + /** + * Volume configuration + */ + public readonly config?: ServiceManagedEBSVolumeConfiguration; + + /** + * configuredAtLaunch indicates volume at launch time, referenced by taskdefinition volume. + */ + public readonly configuredAtLaunch: boolean = true; + + /** + * An IAM role that allows ECS to make calls to EBS APIs. + * If not provided, a new role with appropriate permissions will be created by default. + */ + public readonly role: iam.IRole; + + constructor(scope: Construct, id: string, props: ServiceManagedVolumeProps) { + super(scope, id); + this.validateEbsVolumeConfiguration(props.managedEBSVolume); + this.name = props.name; + this.role = props.managedEBSVolume?.role ?? new iam.Role(this, 'EBSRole', { + assumedBy: new iam.ServicePrincipal('ecs.amazonaws.com'), + managedPolicies: [ + iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonECSInfrastructureRolePolicyForVolumes'), + ], + }); + this.config = { + ...props.managedEBSVolume, + role: this.role, + }; + } + + /** + * Mounts the service managed volume to a specified container at a defined mount point. + * @param container The container to mount the volume on. + * @param mountPoint The mounting point details within the container. + */ + public mountIn(container: ContainerDefinition, mountPoint: ContainerMountPoint) { + container.addMountPoints({ + sourceVolume: this.name, + ...mountPoint, + }); + } + + private validateEbsVolumeConfiguration(volumeConfig?: ServiceManagedEBSVolumeConfiguration) { + if (!volumeConfig) return; + + const { volumeType = ec2.EbsDeviceVolumeType.GP2, iops, size, throughput, snapShotId } = volumeConfig; + + // Validate if both size and snapShotId are not specified. + if (size === undefined && snapShotId === undefined) { + throw new Error('\'size\' or \'snapShotId\' must be specified'); + } + + if (snapShotId && !Token.isUnresolved(snapShotId) && !/^snap-[0-9a-fA-F]+$/.test(snapShotId)) { + throw new Error(`'snapshotId' does match expected pattern. Expected 'snap-' (ex: 'snap-05abe246af') or Token, got: ${snapShotId}`); + } + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-servicemanagedebsvolumeconfiguration.html#cfn-ecs-service-servicemanagedebsvolumeconfiguration-sizeingib + const sizeInGiBRanges = { + [ec2.EbsDeviceVolumeType.GP2]: { minSize: 1, maxSize: 16384 }, + [ec2.EbsDeviceVolumeType.GP3]: { minSize: 1, maxSize: 16384 }, + [ec2.EbsDeviceVolumeType.IO1]: { minSize: 4, maxSize: 16384 }, + [ec2.EbsDeviceVolumeType.IO2]: { minSize: 4, maxSize: 16384 }, + [ec2.EbsDeviceVolumeType.SC1]: { minSize: 125, maxSize: 16384 }, + [ec2.EbsDeviceVolumeType.ST1]: { minSize: 125, maxSize: 16384 }, + [ec2.EbsDeviceVolumeType.STANDARD]: { minSize: 1, maxSize: 1024 }, + }; + + // Validate volume size ranges. + if (size !== undefined) { + const { minSize, maxSize } = sizeInGiBRanges[volumeType]; + if (size.toGibibytes() < minSize || size.toGibibytes() > maxSize) { + throw new Error(`'${volumeType}' volumes must have a size between ${minSize} and ${maxSize} GiB, got ${size.toGibibytes()} GiB`); + } + } + + // Validate throughput. + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-servicemanagedebsvolumeconfiguration.html#cfn-ecs-service-servicemanagedebsvolumeconfiguration-throughput + if (throughput !== undefined) { + if (volumeType !== ec2.EbsDeviceVolumeType.GP3) { + throw new Error(`'throughput' can only be configured with gp3 volume type, got ${volumeType}`); + } else if (!Token.isUnresolved(throughput) && throughput > 1000) { + throw new Error(`'throughput' must be less than or equal to 1000 MiB/s, got ${throughput} MiB/s`); + } + } + + // Check if IOPS is not supported for the volume type. + // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateVolume.html + if ([ec2.EbsDeviceVolumeType.SC1, ec2.EbsDeviceVolumeType.ST1, ec2.EbsDeviceVolumeType.STANDARD, + ec2.EbsDeviceVolumeType.GP2].includes(volumeType) && iops !== undefined) { + throw new Error(`'iops' cannot be specified with sc1, st1, gp2 and standard volume types, got ${volumeType}`); + } + + // Check if IOPS is required but not provided. + if ([ec2.EbsDeviceVolumeType.IO1, ec2.EbsDeviceVolumeType.IO2].includes(volumeType) && iops === undefined) { + throw new Error(`'iops' must be specified with io1 or io2 volume types, got ${volumeType}`); + } + + // Validate IOPS range if specified. + const iopsRanges: { [key: string]: { min: number, max: number } } = {}; + iopsRanges[ec2.EbsDeviceVolumeType.GP3]= { min: 3000, max: 16000 }; + iopsRanges[ec2.EbsDeviceVolumeType.IO1]= { min: 100, max: 64000 }; + iopsRanges[ec2.EbsDeviceVolumeType.IO2]= { min: 100, max: 256000 }; + if (iops !== undefined && !Token.isUnresolved(iops)) { + const { min, max } = iopsRanges[volumeType]; + if ((iops < min || iops > max)) { + throw new Error(`'${volumeType}' volumes must have 'iops' between ${min} and ${max}, got ${iops}`); + } + } + } +} diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index 34793208d28d5..cb2823ffb24ba 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -525,6 +525,7 @@ export class TaskDefinition extends TaskDefinitionBase { return { host: spec.host, name: spec.name, + configuredAtLaunch: spec.configuredAtLaunch, dockerVolumeConfiguration: spec.dockerVolumeConfiguration && { autoprovision: spec.dockerVolumeConfiguration.autoprovision, driver: spec.dockerVolumeConfiguration.driver, @@ -653,9 +654,21 @@ export class TaskDefinition extends TaskDefinitionBase { * Adds a volume to the task definition. */ public addVolume(volume: Volume) { + this.validateVolume(volume); this.volumes.push(volume); } + private validateVolume(volume: Volume):void { + if (volume.configuredAtLaunch !== true) { + return; + } + + // Other volume configurations must not be specified. + if (volume.host || volume.dockerVolumeConfiguration || volume.efsVolumeConfiguration) { + throw new Error(`Volume Configurations must not be specified for '${volume.name}' when 'configuredAtLaunch' is set to true`); + } + } + /** * Adds the specified placement constraint to the task definition. */ @@ -764,7 +777,25 @@ export class TaskDefinition extends TaskDefinitionBase { } } }); + // Validate if multiple volumes configured with configuredAtLaunch. + const runtimeVolumes = this.volumes.filter(vol => vol.configuredAtLaunch); + if (runtimeVolumes.length > 1) { + const volumeNames = runtimeVolumes.map(vol => vol.name).join(','); + ret.push(`More than one volume is configured at launch: [${volumeNames}]`); + } + + // Validate that volume with configuredAtLaunch set to true is mounted by at least one container. + for (const volume of this.volumes) { + if (volume.configuredAtLaunch) { + const isVolumeMounted = this.containers.some(container => { + return container.mountPoints.some(mp => mp.sourceVolume === volume.name); + }); + if (!isVolumeMounted) { + ret.push(`Volume '${volume.name}' should be mounted by at least one container when 'configuredAtLaunch' is true`); + } + } + } return ret; } @@ -978,6 +1009,13 @@ export interface Volume { */ readonly name: string; + /** + * Indicates if the volume should be configured at launch. + * + * @default false + */ + readonly configuredAtLaunch ?: boolean; + /** * This property is specified when you are using Docker volumes. * diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 0478a059df68d..1d4e3d995dc76 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -1364,9 +1364,9 @@ export interface ScratchSpace { } /** - * The details of data volume mount points for a container. + * The base details of where a volume will be mounted within a container */ -export interface MountPoint { +export interface BaseMountPoint { /** * The path on the container to mount the host volume at. */ @@ -1378,6 +1378,12 @@ export interface MountPoint { * If this value is false, then the container can write to the volume. */ readonly readOnly: boolean, +} + +/** + * The details of data volume mount points for a container. + */ +export interface MountPoint extends BaseMountPoint { /** * The name of the volume to mount. * diff --git a/packages/aws-cdk-lib/aws-ecs/lib/index.ts b/packages/aws-cdk-lib/aws-ecs/lib/index.ts index 498e8a0db5081..e3e34f236dd4d 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/index.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/index.ts @@ -1,6 +1,7 @@ export * from './base/base-service'; export * from './base/scalable-task-count'; export * from './base/task-definition'; +export * from './base/service-managed-volume'; export * from './container-definition'; export * from './container-image'; diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts index aec9f09bff889..791c913f261b4 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts @@ -4,6 +4,7 @@ import * as appscaling from '../../../aws-applicationautoscaling'; import * as cloudwatch from '../../../aws-cloudwatch'; import * as ec2 from '../../../aws-ec2'; import * as elbv2 from '../../../aws-elasticloadbalancingv2'; +import * as iam from '../../../aws-iam'; import * as kms from '../../../aws-kms'; import * as logs from '../../../aws-logs'; import * as s3 from '../../../aws-s3'; @@ -15,6 +16,7 @@ import * as cxapi from '../../../cx-api'; import { ECS_ARN_FORMAT_INCLUDES_CLUSTER_NAME } from '../../../cx-api'; import * as ecs from '../../lib'; import { DeploymentControllerType, LaunchType, PropagatedTagSource, ServiceConnectProps } from '../../lib/base/base-service'; +import { ServiceManagedVolume } from '../../lib/base/service-managed-volume'; import { addDefaultCapacityProvider } from '../util'; describe('fargate service', () => { @@ -1453,6 +1455,553 @@ describe('fargate service', () => { }); }); + describe('When setting up a service volume configurations', ()=>{ + let service: ecs.FargateService; + let stack: cdk.Stack; + let cluster: ecs.Cluster; + let taskDefinition: ecs.TaskDefinition; + let container: ecs.ContainerDefinition; + let role: iam.IRole; + let app: cdk.App; + + beforeEach(() => { + // GIVEN + app = new cdk.App(); + stack = new cdk.Stack(app); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('ecs.amazonaws.com'), + }); + container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }); + service = new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + }); + }); + test('success when adding a service volume', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + role: role, + size: cdk.Size.gibibytes(20), + fileSystemType: ecs.FileSystemType.XFS, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + VolumeConfigurations: [ + { + ManagedEBSVolume: { + RoleArn: { 'Fn::GetAtt': ['Role1ABCC5F0', 'Arn'] }, + SizeInGiB: 20, + FilesystemType: 'xfs', + TagSpecifications: [ + { + PropagateTags: 'SERVICE', + ResourceType: 'volume', + Tags: [ + { + Key: 'purpose', + Value: 'production', + }, + ], + }, + ], + }, + Name: 'nginx-vol', + }, + ], + }); + }); + + test('success when mounting via ServiceManagedVolume', () => { + // WHEN + const volume = new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + role: role, + size: cdk.Size.gibibytes(20), + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, + }); + taskDefinition.addVolume(volume); + service.addVolume(volume); + volume.mountIn(container, { + containerPath: '/var/lib', + readOnly: false, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + VolumeConfigurations: [ + { + ManagedEBSVolume: { + RoleArn: { 'Fn::GetAtt': ['Role1ABCC5F0', 'Arn'] }, + SizeInGiB: 20, + TagSpecifications: [ + { + PropagateTags: 'SERVICE', + ResourceType: 'volume', + Tags: [ + { + Key: 'purpose', + Value: 'production', + }, + ], + }, + ], + }, + Name: 'nginx-vol', + }, + ], + }); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + MountPoints: [ + { + ContainerPath: '/var/lib', + ReadOnly: false, + SourceVolume: 'nginx-vol', + }, + ], + }, + ], + Volumes: [ + { + Name: 'nginx-vol', + ConfiguredAtLaunch: true, + }, + ], + }); + }); + + test('throw an error when multiple volume configurations are added to ECS service', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + const vol1 = new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + size: cdk.Size.gibibytes(15), + }, + }); + const vol2 = new ServiceManagedVolume(stack, 'ebs1', { + name: 'ebs1', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + size: cdk.Size.gibibytes(15), + }, + }); + service.addVolume(vol1); + service.addVolume(vol2); + expect(() => { + app.synth(); + }).toThrow(/Only one EBS volume can be specified for 'volumeConfigurations', got: 2/); + }); + + test('create a default ebsrole when not provided', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + size: cdk.Size.gibibytes(20), + fileSystemType: ecs.FileSystemType.XFS, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + VolumeConfigurations: [ + { + ManagedEBSVolume: { + RoleArn: { 'Fn::GetAtt': ['EBSVolumeEBSRoleD38B9F31', 'Arn'] }, + SizeInGiB: 20, + FilesystemType: 'xfs', + TagSpecifications: [ + { + PropagateTags: 'SERVICE', + ResourceType: 'volume', + Tags: [ + { + Key: 'purpose', + Value: 'production', + }, + ], + }, + ], + }, + Name: 'nginx-vol', + }, + ], + }); + }); + + test('throw an error when both size and snapshotId are not provided', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + }, + })); + }).toThrow("'size' or 'snapShotId' must be specified"); + }); + + test('throw an error snapshot does not match pattern', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + snapShotId: 'snap-0d48decab5c493eee_', + }, + })); + }).toThrow("'snapshotId' does match expected pattern. Expected 'snap-' (ex: 'snap-05abe246af') or Token, got: snap-0d48decab5c493eee_"); + }); + + test('success when snapshotId matches the pattern', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + const vol = new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + snapShotId: 'snap-0d48decab5c493eee', + }, + }); + service.addVolume(vol); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + VolumeConfigurations: [ + { + ManagedEBSVolume: { + RoleArn: { 'Fn::GetAtt': ['EBSVolumeEBSRoleD38B9F31', 'Arn'] }, + SnapshotId: 'snap-0d48decab5c493eee', + FilesystemType: 'xfs', + }, + Name: 'nginx-vol', + }, + ], + }); + }); + + test('throw an error when size is greater than 16384 for gp2', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + size: cdk.Size.gibibytes(16390), + }, + })); + }).toThrow(/'gp2' volumes must have a size between 1 and 16384 GiB, got 16390 GiB/); + }); + + test('throw an error when size is less than 4 for volume type io1', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.IO1, + size: cdk.Size.gibibytes(0), + }, + })); + }).toThrow(/'io1' volumes must have a size between 4 and 16384 GiB, got 0 GiB/); + }); + test('throw an error when size is greater than 1024 for volume type standard', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.STANDARD, + size: cdk.Size.gibibytes(1500), + }, + })); + }).toThrow(/'standard' volumes must have a size between 1 and 1024 GiB, got 1500 GiB/); + }); + + test('throw an error if throughput is configured for volumetype gp2', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + size: cdk.Size.gibibytes(10), + throughput: 0, + }, + })); + }).toThrow(/'throughput' can only be configured with gp3 volume type, got gp2/); + }); + + test('throw an error if throughput is greater tahn 1000 for volume type gp3', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.GP3, + size: cdk.Size.gibibytes(10), + throughput: 10001, + }, + })); + }).toThrow("'throughput' must be less than or equal to 1000 MiB/s, got 10001 MiB/s"); + }); + + test('throw an error if throughput is greater tahn 1000 for volume type gp3', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.GP3, + size: cdk.Size.gibibytes(10), + throughput: 10001, + }, + })); + }).toThrow("'throughput' must be less than or equal to 1000 MiB/s, got 10001 MiB/s"); + }); + + test('throw an error if iops is not supported for volume type sc1', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.SC1, + size: cdk.Size.gibibytes(125), + iops: 0, + }, + })); + }).toThrow(/'iops' cannot be specified with sc1, st1, gp2 and standard volume types, got sc1/); + }); + + test('throw an error if iops is not supported for volume type sc1', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + size: cdk.Size.gibibytes(125), + iops: 0, + }, + })); + }).toThrow(/'iops' cannot be specified with sc1, st1, gp2 and standard volume types, got gp2/); + }); + + test('throw an error if if iops is required but not provided for volume type io2', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.IO2, + size: cdk.Size.gibibytes(125), + }, + })); + }).toThrow(/'iops' must be specified with io1 or io2 volume types, got io2/); + }); + + test('throw an error if if iops is less than 100 for volume type io2', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.IO2, + size: cdk.Size.gibibytes(125), + iops: 0, + }, + })); + }).toThrow("io2' volumes must have 'iops' between 100 and 256000, got 0"); + }); + + test('throw an error if if iops is greater than 256000 for volume type io2', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + expect(() => { + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.IO2, + size: cdk.Size.gibibytes(125), + iops: 256001, + }, + })); + }).toThrow("io2' volumes must have 'iops' between 100 and 256000, got 256001"); + }); + + test('success adding gp3 volume with throughput 0', ()=> { + // WHEN + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + + service.addVolume(new ServiceManagedVolume(stack, 'EBSVolume', { + name: 'nginx-vol', + managedEBSVolume: { + fileSystemType: ecs.FileSystemType.XFS, + volumeType: ec2.EbsDeviceVolumeType.GP3, + size: cdk.Size.gibibytes(15), + throughput: 0, + }, + })); + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + VolumeConfigurations: [ + { + ManagedEBSVolume: { + RoleArn: { 'Fn::GetAtt': ['EBSVolumeEBSRoleC27DD941', 'Arn'] }, + SizeInGiB: 15, + FilesystemType: 'xfs', + VolumeType: 'gp3', + Throughput: 0, + }, + Name: 'nginx-vol', + }, + ], + }); + }); + }); + describe('When setting up a health check', () => { test('grace period is respected', () => { // GIVEN diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts index eb1c280ec69c8..df7beb1b3fec8 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts @@ -162,6 +162,45 @@ describe('fargate task definition', () => { // THEN }); }); + describe('When configuredAtLaunch in the Volume', ()=> { + test('do not throw when configuredAtLaunch is false', () => { + // GIVEN + const stack = new cdk.Stack(); + + // THEN + expect(() => { + const taskDefinition =new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + taskDefinition.addVolume({ + name: 'nginx-vol', + efsVolumeConfiguration: { + fileSystemId: 'fs-1234', + }, + }); + taskDefinition.addVolume({ + name: 'nginx-vol1', + efsVolumeConfiguration: { + fileSystemId: 'fs-456', + }, + }); + }); + }); + test('throws when other volume configuration set with configuredAtLaunch', () => { + // GIVEN + const stack = new cdk.Stack(); + + // THEN + expect(() => { + const taskDefinition =new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + taskDefinition.addVolume({ + name: 'nginx-vol', + configuredAtLaunch: true, + efsVolumeConfiguration: { + fileSystemId: 'fs-1234', + }, + }); + }).toThrow(/Volume Configurations must not be specified for 'nginx-vol' when 'configuredAtLaunch' is set to true/); + }); + }); describe('When importing from an existing Fargate TaskDefinition', () => { test('can succeed using TaskDefinition Arn', () => { diff --git a/packages/aws-cdk-lib/aws-ecs/test/task-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/task-definition.test.ts index 2b4bad0b5d5a1..f098b3e89afcb 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/task-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/task-definition.test.ts @@ -1,8 +1,10 @@ import { Template } from '../../assertions'; +import { EbsDeviceVolumeType } from '../../aws-ec2'; import * as ecr from '../../aws-ecr'; import * as iam from '../../aws-iam'; import * as cdk from '../../core'; import * as ecs from '../lib'; +import { ServiceManagedVolume } from '../lib/base/service-managed-volume'; describe('task definition', () => { describe('When creating a new TaskDefinition', () => { @@ -230,6 +232,146 @@ describe('task definition', () => { }).toThrow("Port mapping name 'api' cannot appear in both 'Container2' and 'Container'"); }); + test('throws when multiple runtime volumes are set', () => { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition =new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }); + container.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol', + }); + const container1 = taskDefinition.addContainer('front', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }); + container1.addMountPoints({ + containerPath: '/var/lib', + readOnly: false, + sourceVolume: 'nginx-vol1', + }); + taskDefinition.addVolume({ + name: 'nginx-vol', + configuredAtLaunch: true, + }); + taskDefinition.addVolume({ + name: 'nginx-vol1', + configuredAtLaunch: true, + }); + + // THEN + expect(() => { + Template.fromStack(stack); + }).toThrow('More than one volume is configured at launch: [nginx-vol,nginx-vol1]'); + }); + + test('throws when none of the container mounts the volume', () => { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition =new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + taskDefinition.addVolume({ + name: 'nginx-vol', + configuredAtLaunch: true, + }); + + // THEN + expect(() => { + Template.fromStack(stack); + }).toThrow(/Volume 'nginx-vol' should be mounted by at least one container when 'configuredAtLaunch' is true/); + }); + + test('throws when none of the container mount the volume using ServiceManagedVolume', () => { + // GIVEN + const stack = new cdk.Stack(); + const ebsRole = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('ecs.amazonaws.com'), + }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + taskDefinition.addContainer('db', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }); + const serviceManagedVolume = new ServiceManagedVolume(stack, 'EBS Volume', { + name: 'nginx-vol', + managedEBSVolume: { + role: ebsRole, + size: cdk.Size.gibibytes(3), + volumeType: EbsDeviceVolumeType.GP3, + fileSystemType: ecs.FileSystemType.XFS, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, + }); + taskDefinition.addVolume(serviceManagedVolume); + + // THEN + expect(() => { + Template.fromStack(stack); + }).toThrow(/Volume 'nginx-vol' should be mounted by at least one container when 'configuredAtLaunch' is true/); + }); + + test('throws when multiple runtime volumes are set using ServiceManagedVolume', () => { + // GIVEN + const stack = new cdk.Stack(); + const ebsRole = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('ecs.amazonaws.com'), + }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + const containerDef = taskDefinition.addContainer('db', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }); + const volume1 = new ServiceManagedVolume(stack, 'EBS Volume1', { + name: 'nginx-vol', + managedEBSVolume: { + role: ebsRole, + size: cdk.Size.gibibytes(3), + volumeType: EbsDeviceVolumeType.GP3, + fileSystemType: ecs.FileSystemType.XFS, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, + }); + volume1.mountIn(containerDef, { + readOnly: false, + containerPath: 'var/lib', + }); + taskDefinition.addVolume(volume1); + const volume2 = new ServiceManagedVolume(stack, 'EBS Volume2', { + name: 'nginx-vol1', + managedEBSVolume: { + role: ebsRole, + size: cdk.Size.gibibytes(3), + volumeType: EbsDeviceVolumeType.GP3, + fileSystemType: ecs.FileSystemType.XFS, + tagSpecifications: [{ + tags: { + purpose: 'production', + }, + propagateTags: ecs.EbsPropagatedTagSource.SERVICE, + }], + }, + }); + volume2.mountIn(containerDef, { + readOnly: false, + containerPath: 'var/lib', + }); + taskDefinition.addVolume(volume2); + + // THEN + expect(() => { + Template.fromStack(stack); + }).toThrow('More than one volume is configured at launch: [nginx-vol,nginx-vol1]'); + }); + test('You can specify a container ulimits using the dedicated property in ContainerDefinitionOptions', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/aws-cdk-lib/aws-efs/README.md b/packages/aws-cdk-lib/aws-efs/README.md index 2ecc25ab01d17..98a27baad3b05 100644 --- a/packages/aws-cdk-lib/aws-efs/README.md +++ b/packages/aws-cdk-lib/aws-efs/README.md @@ -27,6 +27,7 @@ const fileSystem = new efs.FileSystem(this, 'MyEfsFileSystem', { lifecyclePolicy: efs.LifecyclePolicy.AFTER_14_DAYS, // files are not transitioned to infrequent access (IA) storage by default performanceMode: efs.PerformanceMode.GENERAL_PURPOSE, // default outOfInfrequentAccessPolicy: efs.OutOfInfrequentAccessPolicy.AFTER_1_ACCESS, // files are not transitioned back from (infrequent access) IA to primary storage by default + transitionToArchivePolicy: efs.LifecyclePolicy.AFTER_14_DAYS, // files are not transitioned to Archive by default }); ``` diff --git a/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts b/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts index 49cedf75870c8..430f3461185f3 100644 --- a/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts +++ b/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts @@ -8,7 +8,8 @@ import { ArnFormat, FeatureFlags, Lazy, RemovalPolicy, Resource, Size, Stack, Ta import * as cxapi from '../../cx-api'; /** - * EFS Lifecycle Policy, if a file is not accessed for given days, it will move to EFS Infrequent Access. + * EFS Lifecycle Policy, if a file is not accessed for given days, it will move to EFS Infrequent Access + * or Archive storage. * * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-efs-filesystem.html#cfn-elasticfilesystem-filesystem-lifecyclepolicies */ @@ -42,7 +43,22 @@ export enum LifecyclePolicy { /** * After 90 days of not being accessed. */ - AFTER_90_DAYS = 'AFTER_90_DAYS' + AFTER_90_DAYS = 'AFTER_90_DAYS', + + /** + * After 180 days of not being accessed. + */ + AFTER_180_DAYS = 'AFTER_180_DAYS', + + /** + * After 270 days of not being accessed. + */ + AFTER_270_DAYS = 'AFTER_270_DAYS', + + /** + * After 365 days of not being accessed. + */ + AFTER_365_DAYS = 'AFTER_365_DAYS', } /** @@ -211,6 +227,14 @@ export interface FileSystemProps { * @default - None. EFS will not transition files from IA storage to primary storage. */ readonly outOfInfrequentAccessPolicy?: OutOfInfrequentAccessPolicy; + + /** + * The number of days after files were last accessed in primary storage (the Standard storage class) at which to move them to Archive storage. + * Metadata operations such as listing the contents of a directory don't count as file access events. + * + * @default - None. EFS will not transition files to Archive storage class. + */ + readonly transitionToArchivePolicy?: LifecyclePolicy; /** * The performance mode that the file system will operate under. * An Amazon EFS file system's performance mode can't be changed after the file system has been created. @@ -507,8 +531,8 @@ export class FileSystem extends FileSystemBase { const encrypted = props.encrypted ?? (FeatureFlags.of(this).isEnabled( cxapi.EFS_DEFAULT_ENCRYPTION_AT_REST) ? true : undefined); - // LifecyclePolicies is an array of lists containing a single policy - let lifecyclePolicies = []; + // LifecyclePolicies must be an array of objects, each containing a single policy + const lifecyclePolicies: CfnFileSystem.LifecyclePolicyProperty[] = []; if (props.lifecyclePolicy) { lifecyclePolicies.push({ transitionToIa: props.lifecyclePolicy }); @@ -518,6 +542,10 @@ export class FileSystem extends FileSystemBase { lifecyclePolicies.push({ transitionToPrimaryStorageClass: props.outOfInfrequentAccessPolicy }); } + if (props.transitionToArchivePolicy) { + lifecyclePolicies.push({ transitionToArchive: props.transitionToArchivePolicy }); + } + const oneZoneAzName = props.vpc.availabilityZones[0]; this._resource = new CfnFileSystem(this, 'Resource', { diff --git a/packages/aws-cdk-lib/aws-efs/test/efs-file-system.test.ts b/packages/aws-cdk-lib/aws-efs/test/efs-file-system.test.ts index 5e0c8b984d420..32491d2a39687 100644 --- a/packages/aws-cdk-lib/aws-efs/test/efs-file-system.test.ts +++ b/packages/aws-cdk-lib/aws-efs/test/efs-file-system.test.ts @@ -108,12 +108,13 @@ test('file system is created correctly with a life cycle property', () => { }); }); -test('file system is created correctly with a life cycle property and out of infrequent access property', () => { +test('file system LifecyclePolicies is created correctly', () => { // WHEN new FileSystem(stack, 'EfsFileSystem', { vpc, lifecyclePolicy: LifecyclePolicy.AFTER_7_DAYS, outOfInfrequentAccessPolicy: OutOfInfrequentAccessPolicy.AFTER_1_ACCESS, + transitionToArchivePolicy: LifecyclePolicy.AFTER_14_DAYS, }); // THEN Template.fromStack(stack).hasResourceProperties('AWS::EFS::FileSystem', { @@ -124,6 +125,25 @@ test('file system is created correctly with a life cycle property and out of inf { TransitionToPrimaryStorageClass: 'AFTER_1_ACCESS', }, + { + TransitionToArchive: 'AFTER_14_DAYS', + }, + ], + }); +}); + +test('file system with transition to archive is created correctly', () => { + // WHEN + new FileSystem(stack, 'EfsFileSystem', { + vpc, + transitionToArchivePolicy: LifecyclePolicy.AFTER_1_DAY, + }); + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EFS::FileSystem', { + LifecyclePolicies: [ + { + TransitionToArchive: 'AFTER_1_DAY', + }, ], }); }); diff --git a/packages/aws-cdk-lib/aws-route53/README.md b/packages/aws-cdk-lib/aws-route53/README.md index ce92008765691..fb33c8f5e7fdf 100644 --- a/packages/aws-cdk-lib/aws-route53/README.md +++ b/packages/aws-cdk-lib/aws-route53/README.md @@ -151,6 +151,32 @@ new route53.ARecord(this, 'ARecordGeoLocationDefault', { }); ``` +To enable [weighted routing](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-weighted.html), use the `weight` parameter: + +```ts +declare const myZone: route53.HostedZone; + +new route53.ARecord(this, 'ARecordWeighted1', { + zone: myZone, + target: route53.RecordTarget.fromIpAddresses('1.2.3.4'), + weight: 10, +}); +``` + +To specify a unique identifier to differentiate among multiple resource record sets that have the same combination of name and type, use the `setIdentifier` parameter: + +```ts +declare const myZone: route53.HostedZone; + +new route53.ARecord(this, 'ARecordWeighted1', { + zone: myZone, + target: route53.RecordTarget.fromIpAddresses('1.2.3.4'), + weight: 10, + setIdentifier: 'weighted-record-id', +}); +``` +**Warning** It is not possible to specify `setIdentifier` in a simple routing without one of `weight` or `geoLocation` defined. + Constructs are available for A, AAAA, CAA, CNAME, MX, NS, SRV and TXT records. Use the `CaaAmazonRecord` construct to easily restrict certificate authorities diff --git a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts index 15b49c7e36cd5..02c1b2a8d3fac 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts @@ -5,7 +5,7 @@ import { IHostedZone } from './hosted-zone-ref'; import { CfnRecordSet } from './route53.generated'; import { determineFullyQualifiedDomainName } from './util'; import * as iam from '../../aws-iam'; -import { CustomResource, Duration, IResource, RemovalPolicy, Resource, Token } from '../../core'; +import { CustomResource, Duration, IResource, Names, RemovalPolicy, Resource, Token } from '../../core'; import { CrossAccountZoneDelegationProvider } from '../../custom-resource-handlers/dist/aws-route53/cross-account-zone-delegation-provider.generated'; import { DeleteExistingRecordSetProvider } from '../../custom-resource-handlers/dist/aws-route53/delete-existing-record-set-provider.generated'; @@ -184,6 +184,31 @@ export interface RecordSetOptions { * @default false */ readonly deleteExisting?: boolean; + + /** + * Among resource record sets that have the same combination of DNS name and type, + * a value that determines the proportion of DNS queries that Amazon Route 53 responds to using the current resource record set. + * + * Route 53 calculates the sum of the weights for the resource record sets that have the same combination of DNS name and type. + * Route 53 then responds to queries based on the ratio of a resource's weight to the total. + * + * This value can be a number between 0 and 255. + * + * @see https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-weighted.html + * + * @default - Do not set weighted routing + */ + readonly weight?: number; + + /** + * A string used to distinguish between different records with the same combination of DNS name and type. + * It can only be set when either weight or geoLocation is defined. + * + * This parameter must be between 1 and 128 characters in length. + * + * @default - Auto generated string + */ + readonly setIdentifier?: string; } /** @@ -241,10 +266,28 @@ export interface RecordSetProps extends RecordSetOptions { */ export class RecordSet extends Resource implements IRecordSet { public readonly domainName: string; + private readonly geoLocation?: GeoLocation; + private readonly weight?: number; constructor(scope: Construct, id: string, props: RecordSetProps) { super(scope, id); + if (props.weight && (props.weight < 0 || props.weight > 255)) { + throw new Error(`weight must be between 0 and 255 inclusive, got: ${props.weight}`); + } + if (props.setIdentifier && (props.setIdentifier.length < 1 || props.setIdentifier.length > 128)) { + throw new Error(`setIdentifier must be between 1 and 128 characters long, got: ${props.setIdentifier.length}`); + } + if (props.weight && props.geoLocation) { + throw new Error('Only one of weight or geoLocation can be specified, not both'); + } + if (props.setIdentifier && !props.weight && !props.geoLocation) { + throw new Error('setIdentifier can only be specified when either weight or geoLocation is defined'); + } + + this.geoLocation = props.geoLocation; + this.weight = props.weight; + const ttl = props.target.aliasTarget ? undefined : ((props.ttl && props.ttl.toSeconds()) ?? 1800).toString(); const recordName = determineFullyQualifiedDomainName(props.recordName || props.zone.zoneName, props.zone); @@ -262,7 +305,8 @@ export class RecordSet extends Resource implements IRecordSet { countryCode: props.geoLocation.countryCode, subdivisionCode: props.geoLocation.subdivisionCode, } : undefined, - setIdentifier: props.geoLocation ? this.configureSetIdentifer(props.geoLocation) : undefined, + setIdentifier: props.setIdentifier ?? this.configureSetIdentifier(), + weight: props.weight, }); this.domainName = recordSet.ref; @@ -308,18 +352,28 @@ export class RecordSet extends Resource implements IRecordSet { } } - private configureSetIdentifer(props: GeoLocation): string | undefined { - let identifier = 'GEO'; - if (props.continentCode) { - identifier = identifier.concat('_CONTINENT_', props.continentCode); - } - if (props.countryCode) { - identifier = identifier.concat('_COUNTRY_', props.countryCode); + private configureSetIdentifier(): string | undefined { + if (this.geoLocation) { + let identifier = 'GEO'; + if (this.geoLocation.continentCode) { + identifier = identifier.concat('_CONTINENT_', this.geoLocation.continentCode); + } + if (this.geoLocation.countryCode) { + identifier = identifier.concat('_COUNTRY_', this.geoLocation.countryCode); + } + if (this.geoLocation.subdivisionCode) { + identifier = identifier.concat('_SUBDIVISION_', this.geoLocation.subdivisionCode); + } + return identifier; } - if (props.subdivisionCode) { - identifier = identifier.concat('_SUBDIVISION_', props.subdivisionCode); + + if (this.weight) { + const idPrefix = `WEIGHT_${this.weight}_ID_`; + const identifier = `${idPrefix}${Names.uniqueResourceName(this, { maxLength: 64 - idPrefix.length })}`; + return identifier; } - return identifier; + + return undefined; } } diff --git a/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts b/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts index 1fa9bf4eae2e1..871768a41893a 100644 --- a/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts +++ b/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts @@ -1096,4 +1096,107 @@ describe('record set', () => { ], }); }); + + test('with weight', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { + zoneName: 'myzone', + }); + + // WHEN + new route53.RecordSet(stack, 'RecordSet', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight: 50, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: 'www.myzone.', + Type: 'CNAME', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + 'zzz', + ], + TTL: '1800', + Weight: 50, + SetIdentifier: 'WEIGHT_50_ID_RecordSet', + }); + }); + + test.each([ + [-1], + [256], + ])('throw error for invalid weight %s', (weight: number) => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight, + })).toThrow(`weight must be between 0 and 255 inclusive, got: ${weight}`); + }); + + test('throw error for invalid setIdentifier', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight: 20, + setIdentifier: 'a'.repeat(129), + })).toThrow('setIdentifier must be between 1 and 128 characters long, got: 129'); + }); + + test('throw error for the simultaneous definition of weight and geoLocation', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight: 50, + geoLocation: route53.GeoLocation.continent(route53.Continent.EUROPE), + setIdentifier: 'uniqueId', + })).toThrow('Only one of weight or geoLocation can be specified, not both'); + }); + + test('throw error for the definition of setIdentifier without weight or geoLocation', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + setIdentifier: 'uniqueId', + })).toThrow('setIdentifier can only be specified when either weight or geoLocation is defined'); + }); });