package com.aws; import software.amazon.awscdk.App; import software.amazon.awscdk.assertions.Template; import java.io.IOException; import org.junit.jupiter.api.Test; import java.util.HashMap; import software.amazon.awscdk.Environment; import java.util.List; import java.util.Map; /* Unit testing for ServerEcsStack, tests for key properties * in the cloudformation output of the stack. * Some empty maps and lists can be found to match * any value for fields in the Cloudformation output with * unimportant information. * More extensive tests are performed through deployments * of various scenarios and verifying outputs. */ public class ServerEcsStackTest { private String cidr; private String region; private String instanceType; private String ecrUri; private String scenarioFile; private Template serverTemplate; private Template vpcTemplate; private EcsStack serverEcsStack; private VpcStack vpcStack; private String arm; public ServerEcsStackTest() { App app = new App(); cidr = "11.0.0.0/16"; region = "us-west-2"; instanceType = "t4g.xlarge"; ecrUri = "public.ecr.aws/d2r9y8c2/s2n-quic-collector-server-scenario"; scenarioFile = "/usr/bin/request_response.json"; arm = "true"; vpcStack = new VpcStack(app, "VpcStack", VpcStackProps.builder() .env(makeEnv(System.getenv("CDK_DEFAULT_ACCOUNT"), region)) .cidr(cidr) .build()); serverEcsStack = new EcsStack(app, "ServerEcsStack", EcsStackProps.builder() .env(makeEnv(System.getenv("CDK_DEFAULT_ACCOUNT"), region)) .bucket(vpcStack.getBucket()) .stackType("server") .vpc(vpcStack.getVpc()) .instanceType(instanceType) .ecrUri(ecrUri) .scenario(scenarioFile) .serverRegion(region) .arm(arm) .build()); serverTemplate = Template.fromStack(serverEcsStack); vpcTemplate = Template.fromStack(vpcStack); } @Test public void testServerStack() throws IOException { //Security Group serverTemplate.hasResourceProperties("AWS::EC2::SecurityGroup", new HashMap<String, Object>() {{ put("SecurityGroupEgress", List.of(Map.of("CidrIp", "0.0.0.0/0", "Description", "Allow all outbound traffic by default","IpProtocol", "-1"))); put("SecurityGroupIngress", List.of(Map.of("CidrIp", "0.0.0.0/0", "Description", "from 0.0.0.0/0:ALL TRAFFIC","IpProtocol", "-1"))); }}); //Cluster serverTemplate.hasResource("AWS::ECS::Cluster", new HashMap<String, Object>() {{ put("DeletionPolicy", "Delete"); }}); //Launch config serverTemplate.hasResourceProperties("AWS::AutoScaling::LaunchConfiguration", new HashMap<String, Object>() {{ put("InstanceType", instanceType); }}); //Autoscaling group serverTemplate.hasResourceProperties("AWS::AutoScaling::AutoScalingGroup", new HashMap<String, Object>() {{ put("DesiredCapacity", "1"); put("MinSize", "0"); }}); //Asg Provider serverTemplate.hasResourceProperties("AWS::ECS::CapacityProvider", new HashMap<String, Object>() {{ put("AutoScalingGroupProvider", Map.of("ManagedTerminationProtection","DISABLED")); }}); //Task Definition serverTemplate.hasResourceProperties("AWS::ECS::TaskDefinition", new HashMap<String, Object>() {{ put("ContainerDefinitions", List.of(Map.of( "Environment", List.of(Map.of("Name", "PORT", "Value", "3000"), Map.of("Name", "SCENARIO", "Value", scenarioFile)), "PortMappings", List.of(Map.of("ContainerPort", 3000, "HostPort", 3000, "Protocol", "udp")), "Image", ecrUri, "LogConfiguration", Map.of("LogDriver", "awslogs", "Options", Map.of( "awslogs-stream-prefix", "server-ecs-task"))) )); put("NetworkMode", "awsvpc"); }}); //Server task policy to access metrics s3 bucket serverTemplate.hasResourceProperties("AWS::IAM::Policy", new HashMap<String, Object>() {{ put("PolicyDocument", Map.of("Statement", List.of(Map.of("Action", List.of( "s3:DeleteObject*", "s3:PutObject", "s3:PutObjectLegalHold", "s3:PutObjectRetention", "s3:PutObjectTagging", "s3:PutObjectVersionTagging", "s3:Abort*"), "Effect", "Allow", "Resource", List.of(Map.of(), Map.of()))), "Version", "2012-10-17")); }}); //Server task policy can send logs to Cloudwatch serverTemplate.hasResourceProperties("AWS::IAM::Policy", new HashMap<String, Object>() {{ put("PolicyDocument", Map.of("Statement", List.of(Map.of("Action", List.of( "logs:CreateLogStream", "logs:PutLogEvents"), "Effect", "Allow", "Resource", Map.of())))); }}); //Namespace serverTemplate.hasResourceProperties("AWS::ServiceDiscovery::PrivateDnsNamespace", new HashMap<String, Object>() {{ put("Name", "serverecs.com"); }}); //LogGroup serverTemplate.hasResource("AWS::Logs::LogGroup", new HashMap<String, Object>() {{ put("Properties", Map.of("RetentionInDays", 1)); }}); //Export lambda function serverTemplate.hasResourceProperties("AWS::Lambda::Function", new HashMap<String, Object>() {{ put("Handler", "exportS3.handler"); put("Runtime", "nodejs14.x"); }}); //Export lambda policy permissions serverTemplate.hasResourceProperties("AWS::IAM::Policy", new HashMap<String, Object>() {{ put("PolicyDocument", Map.of("Statement", List.of( Map.of("Action", "logs:CreateExportTask", "Effect", "Allow"), Map.of("Action", List.of("s3:GetObject*", "s3:GetBucket*", "s3:List*", "s3:DeleteObject*", "s3:PutObject", "s3:PutObjectLegalHold", "s3:PutObjectRetention", "s3:PutObjectTagging", "s3:PutObjectVersionTagging", "s3:Abort*"), "Effect", "Allow") ))); }}); //Log Retention serverTemplate.hasResourceProperties("Custom::LogRetention", new HashMap<String, Object>() {{ put("RetentionInDays", 1); }}); //ECS Service serverTemplate.hasResourceProperties("AWS::ECS::Service", new HashMap<String, Object>() {{ put("CapacityProviderStrategy", List.of(Map.of("CapacityProvider", Map.of()))); put("DesiredCount", 1); put("NetworkConfiguration", Map.of("AwsvpcConfiguration", Map.of())); }}); //Service Discovery serverTemplate.hasResourceProperties("AWS::ServiceDiscovery::Service", new HashMap<String, Object>() {{ put("DnsConfig", Map.of("DnsRecords", List.of(Map.of("TTL", 60, "Type", "A")))); put ("Name", "ec2serviceserverCloudmapSrv-UEyneXTpp1nx"); }}); //Check bucket policy changed by export lambda vpcTemplate.hasResourceProperties("AWS::S3::BucketPolicy", new HashMap<String, Object>() {{ put("PolicyDocument", Map.of( "Statement", List.of( Map.of("Action", List.of( "s3:PutObject","s3:PutObjectLegalHold", "s3:PutObjectRetention","s3:PutObjectTagging", "s3:PutObjectVersionTagging","s3:Abort*"), "Effect", "Allow", "Principal", Map.of("Service", "logs.us-west-2.amazonaws.com")), Map.of("Action", "s3:GetBucketAcl", "Effect", "Allow", "Principal", Map.of("Service", "logs.us-west-2.amazonaws.com")) ))); }}); } static Environment makeEnv(String account, String region) { return Environment.builder() .account(account) .region(region) .build(); } }