Create stack to run load tests
It can be useful having a simple way of scaling load tests
when checking the resiliency of the different recipes.
Feature: Issue 12919
Change-Id: Iff221093e17b5d50f56ded4e011e5ab7921b2f28
diff --git a/load-test-fleet/Makefile b/load-test-fleet/Makefile
new file mode 100644
index 0000000..6ebdd6c
--- /dev/null
+++ b/load-test-fleet/Makefile
@@ -0,0 +1,26 @@
+include ../Makefile.common
+
+LOAD_TEST_TEMPLATE:=cf-load-test-workers.yml
+AWS_REGION:=us-east-1
+AWS_FC_COMMAND=export AWS_PAGER=;aws cloudformation
+LOAD_TEST_STACK_NAME:=gerrit-load-test
+DESIRED_CAPACITY:=3
+ENTRYPOINT:="ls -lrt"
+
+.PHONY: load-test delete-load-test
+
+load-test:
+ $(AWS_FC_COMMAND) create-stack \
+ --stack-name $(LOAD_TEST_STACK_NAME) \
+ --capabilities CAPABILITY_IAM \
+ --template-body file://`pwd`/$(LOAD_TEST_TEMPLATE) \
+ --region $(AWS_REGION) \
+ --parameters \
+ ParameterKey=DesiredCapacity,ParameterValue=$(DESIRED_CAPACITY) \
+ ParameterKey=ECSKeyName,ParameterValue=$(CLUSTER_KEYS) \
+ ParameterKey=EntryPoint,ParameterValue="$(ENTRYPOINT)"
+
+delete-load-test:
+ $(AWS_FC_COMMAND) delete-stack \
+ --stack-name $(LOAD_TEST_STACK_NAME) \
+ --region $(AWS_REGION)
diff --git a/load-test-fleet/README.md b/load-test-fleet/README.md
new file mode 100644
index 0000000..4284059
--- /dev/null
+++ b/load-test-fleet/README.md
@@ -0,0 +1,21 @@
+# Load tests
+
+This is a set of Cloud Formation Templates and scripts to spin up a simple load
+test fleet of EC2 instances.
+
+It can be used to run load tests against stacks created with any recipe.
+
+## How to run it
+
+```
+make load-test ENTRYPOINT="command to run the tests" DESIRED_CAPACITY=5
+```
+
+This will create a CF stack with `DESIRED_CAPACITY` EC2 instances running the
+`ENTRYPOINT` command after startup.
+
+### Cleaning up
+
+```
+make delete-load-tets
+```
diff --git a/load-test-fleet/cf-load-test-workers.yml b/load-test-fleet/cf-load-test-workers.yml
new file mode 100644
index 0000000..c52ef3d
--- /dev/null
+++ b/load-test-fleet/cf-load-test-workers.yml
@@ -0,0 +1,231 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: A stack for deploying instance to run load tests
+Parameters:
+ DesiredCapacity:
+ Type: Number
+ Default: 3
+ Description: Number of EC2 instances to launch in your ECS cluster.
+ MaxSize:
+ Type: Number
+ Default: 10
+ Description: Maximum number of EC2 instances that can be launched in your ECS cluster.
+ ECSAMI:
+ Description: AMI ID
+ Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
+ Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id
+ InstanceType:
+ Description: EC2 instance type
+ Type: String
+ Default: m4.xlarge
+ AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, m3.medium, m3.large,
+ m3.xlarge, m3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.10xlarge,
+ c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c3.large, c3.xlarge,
+ c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge,
+ r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge]
+ ConstraintDescription: Please choose a valid instance type.
+ ECSKeyName:
+ Type: String
+ Default: gerrit-cluster-keys
+ Description: EC2 key pair name the cluter's instances
+ EntryPoint:
+ Type: String
+ Default: "docker info"
+ Description: Command to run when startinig the load tests
+Mappings:
+ # Hard values for the subnet masks. These masks define
+ # the range of internal IP addresses that can be assigned.
+ # The VPC can have all IP's from 10.0.0.0 to 10.0.255.255
+ # There is the subnet which cover the ranges:
+ #
+ # 10.0.0.0 - 10.0.0.255
+ SubnetConfig:
+ VPC:
+ CIDR: '10.0.0.0/16'
+ PublicOne:
+ CIDR: '10.0.0.0/24'
+Resources:
+ VPC:
+ Type: AWS::EC2::VPC
+ Properties:
+ EnableDnsSupport: true
+ EnableDnsHostnames: true
+ CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
+
+ PublicSubnetOne:
+ Type: AWS::EC2::Subnet
+ Properties:
+ AvailabilityZone:
+ Fn::Select:
+ - 0
+ - Fn::GetAZs: {Ref: 'AWS::Region'}
+ VpcId: !Ref 'VPC'
+ CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
+ MapPublicIpOnLaunch: true
+
+ InternetGateway:
+ Type: AWS::EC2::InternetGateway
+ GatewayAttachement:
+ Type: AWS::EC2::VPCGatewayAttachment
+ Properties:
+ VpcId: !Ref 'VPC'
+ InternetGatewayId: !Ref 'InternetGateway'
+ PublicRouteTable:
+ Type: AWS::EC2::RouteTable
+ Properties:
+ VpcId: !Ref 'VPC'
+ PublicRoute:
+ Type: AWS::EC2::Route
+ DependsOn: GatewayAttachement
+ Properties:
+ RouteTableId: !Ref 'PublicRouteTable'
+ DestinationCidrBlock: '0.0.0.0/0'
+ GatewayId: !Ref 'InternetGateway'
+ PublicSubnetOneRouteTableAssociation:
+ Type: AWS::EC2::SubnetRouteTableAssociation
+ Properties:
+ SubnetId: !Ref PublicSubnetOne
+ RouteTableId: !Ref PublicRouteTable
+
+ # ECS Resources
+ ECSCluster:
+ Type: AWS::ECS::Cluster
+
+ EcsHostSecurityGroup:
+ Type: AWS::EC2::SecurityGroup
+ Properties:
+ GroupDescription: Access to the ECS hosts that run containers
+ VpcId: !Ref 'VPC'
+ SecurityGroupIngress:
+ # Allow access to NLB from anywhere on the internet
+ - CidrIp: 0.0.0.0/0
+ IpProtocol: -1
+
+ CloudWatchLogsGroup:
+ Type: AWS::Logs::LogGroup
+ Properties:
+ LogGroupName: !Ref AWS::StackName
+ RetentionInDays: 1
+
+ # Autoscaling group. This launches the actual EC2 instances that will register
+ # themselves as members of the cluster, and run the docker containers.
+ ECSAutoScalingGroup:
+ Type: AWS::AutoScaling::AutoScalingGroup
+ Properties:
+ VPCZoneIdentifier:
+ - !Ref PublicSubnetOne
+ LaunchConfigurationName: !Ref 'ContainerInstances'
+ MinSize: '1'
+ MaxSize: !Ref 'MaxSize'
+ DesiredCapacity: !Ref 'DesiredCapacity'
+ CreationPolicy:
+ ResourceSignal:
+ Timeout: PT15M
+ UpdatePolicy:
+ AutoScalingReplacingUpdate:
+ WillReplace: 'true'
+ ContainerInstances:
+ Type: AWS::AutoScaling::LaunchConfiguration
+ Properties:
+ ImageId: !Ref 'ECSAMI'
+ SecurityGroups: [!Ref 'EcsHostSecurityGroup']
+ InstanceType: !Ref 'InstanceType'
+ IamInstanceProfile: !Ref 'EC2InstanceProfile'
+ KeyName: !Ref ECSKeyName
+ UserData:
+ Fn::Base64: !Sub |
+ #!/bin/bash -xe
+ echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
+ yum install -y aws-cfn-bootstrap
+ # Signal to CloudFormation aws-cfn-bootstrap has been correctly updated
+ /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
+ ${EntryPoint}
+ AutoscalingRole:
+ Type: AWS::IAM::Role
+ Properties:
+ AssumeRolePolicyDocument:
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: [application-autoscaling.amazonaws.com]
+ Action: ['sts:AssumeRole']
+ Path: /
+ Policies:
+ - PolicyName: service-autoscaling
+ PolicyDocument:
+ Statement:
+ - Effect: Allow
+ Action:
+ - 'application-autoscaling:*'
+ - 'cloudwatch:DescribeAlarms'
+ - 'cloudwatch:PutMetricAlarm'
+ - 'ecs:DescribeServices'
+ - 'ecs:UpdateService'
+ Resource: '*'
+ EC2InstanceProfile:
+ Type: AWS::IAM::InstanceProfile
+ Properties:
+ Path: /
+ Roles: [!Ref 'EC2Role']
+
+ # Role for the EC2 hosts. This allows the ECS agent on the EC2 hosts
+ # to communciate with the ECS control plane, as well as download the docker
+ # images from ECR to run on your host.
+ EC2Role:
+ Type: AWS::IAM::Role
+ Properties:
+ AssumeRolePolicyDocument:
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: [ec2.amazonaws.com]
+ Action: ['sts:AssumeRole']
+ Path: /
+ Policies:
+ - PolicyName: ecs-service
+ PolicyDocument:
+ Statement:
+ - Effect: Allow
+ Action:
+ - 'ecs:CreateCluster'
+ - 'ecs:DeregisterContainerInstance'
+ - 'ecs:DiscoverPollEndpoint'
+ - 'ecs:Poll'
+ - 'ecs:RegisterContainerInstance'
+ - 'ecs:StartTelemetrySession'
+ - 'ecs:Submit*'
+ - 'logs:CreateLogStream'
+ - 'logs:PutLogEvents'
+ - 'ecr:GetAuthorizationToken'
+ - 'ecr:BatchGetImage'
+ - 'ecr:GetDownloadUrlForLayer'
+ Resource: '*'
+ - PolicyName: s3-bucket
+ PolicyDocument:
+ Statement:
+ - Effect: Allow
+ Action:
+ - 's3:ListBucket'
+ Resource: '*'
+ - Effect: Allow
+ Action:
+ - 's3:PutObject'
+ - 's3:GetObject'
+ - 's3:DeleteObject'
+ Resource: '*'
+
+Outputs:
+ ClusterName:
+ Description: The name of the ECS cluster
+ Value: !Ref 'ECSCluster'
+ Export:
+ Name: !Join [ ':', [ !Ref 'AWS::StackName', 'ClusterName' ] ]
+ VPCId:
+ Description: The ID of the VPC that this stack is deployed in
+ Value: !Ref 'VPC'
+ Export:
+ Name: !Join [ ':', [ !Ref 'AWS::StackName', 'VPCId' ] ]
+ PublicSubnetOne:
+ Description: Public subnet one
+ Value: !Ref 'PublicSubnetOne'
+ Export:
+ Name: !Join [ ':', [ !Ref 'AWS::StackName', 'PublicSubnetOne' ] ]