Skip to content

Commit

Permalink
Merge pull request #31 from envato/support-albs
Browse files Browse the repository at this point in the history
Support ALBs
  • Loading branch information
Patrick Robinson authored May 21, 2019
2 parents 09a0073 + 025c2ea commit ff15a21
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 9 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ The Docker image to deploy.

Example: `"012345.dkr.ecr.us-east-1.amazonaws.com/my-service:123"`

### `target-group`

The Target Group ARN to map the service to.

Example: `"arn:aws:elasticloadbalancing:us-east-1:012345678910:targetgroup/alb/e987e1234cd12abc"`

### `target-container-name`

The Container Name to forward ALB requests to.

### `target-container-port`

The Container Port to forward requests to.

## AWS Roles

Requires the following AWS roles to be granted to the agent running this step:
Expand Down
41 changes: 38 additions & 3 deletions hooks/command
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,38 @@ image=${BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE?}
task_definition=${BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION?}
desired_count=${BUILDKITE_PLUGIN_ECS_DEPLOY_DESIRED_COUNT:-"1"}
task_role_arn=${BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_ROLE_ARN:-""}
target_group=${BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_GROUP:-""}
# Resolve any runtime environment variables it has
target_group=$(eval "echo $target_group")
target_container=${BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_CONTAINER_NAME:-""}
target_port=${BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_CONTAINER_PORT:-""}

function create_service() {
local cluster_name=$1
local task_definition=$2
local service_name=$3
local desired_count=$4
local target_group_arguments
target_group_arguments=$(generate_target_group_arguments "$5" "$6" "$7")

service_defined=$(aws ecs describe-services --cluster "$cluster_name" --service "$service_name" --query 'services[*].status' --output text |wc -l)
service_defined=$(aws ecs describe-services --cluster "$cluster_name" --service "$service_name" --query 'services[?status==`ACTIVE`].status' --output text |wc -l)
if [[ $service_defined -eq 0 ]]; then
echo "--- :ecs: Creating a Service $service_name in cluster $cluster_name"
aws ecs create-service --cluster "$cluster_name" --service-name "$service_name" --task-definition "$task_definition" --desired-count "$desired_count"
aws ecs create-service --cluster "$cluster_name" --service-name "$service_name" --task-definition "$task_definition" --desired-count "$desired_count" $target_group_arguments
fi
}

function generate_target_group_arguments() {
local target_group=$1
local target_container=$2
local target_port=$3
local target_group_arguments=""
if [[ -n $target_group ]] && [[ -n $target_container ]] && [[ -n $target_port ]]; then
target_group_arguments="--load-balancers targetGroupArn=${target_group},containerName=${target_container},containerPort=${target_port}"
fi
echo "$target_group_arguments"
}

## This is the template definition of your containers
container_definitions_json=$(jq --arg IMAGE "$image" \
'.[0].image=$IMAGE' \
Expand Down Expand Up @@ -53,7 +71,24 @@ task_revision=$(jq '.taskDefinition.revision' <<< "$json_output")
echo "Registered ${task_family}:${task_revision}"

# Create service if it doesn't already exist
create_service "$cluster" "${task_family}:${task_revision}" "$service_name" "$desired_count"
create_service "$cluster" "${task_family}:${task_revision}" "$service_name" "$desired_count" "$target_group" "$target_container" "$target_port"

lb_config=$(aws ecs describe-services --cluster "$cluster" --services "$service_name" --query 'services[?status==`ACTIVE`]' |jq -r '.[0].loadBalancers[0]')
error="+++ Cannot update a service to add a load balancer. First delete the service and then run again, or rename the service to force a new one to be created"

# No easy way to tell if the target group has changed, since describe-services only returns the load balancer name
if [[ "$lb_config" == "null" ]] && [[ -n $target_group ]]; then
echo "$error"
exit 1
fi

if [[ "$lb_config" == "null" ]]; then
# noop
true
elif [[ $(echo "$lb_config" |jq -r '.containerName') != "$target_container" ]] || [[ $(echo "$lb_config" |jq -r '.containerPort') -ne $target_port ]]; then
echo "$error"
exit 1
fi

echo "--- :ecs: Updating service for ${service_name}"
aws ecs update-service \
Expand Down
6 changes: 6 additions & 0 deletions plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ configuration:
type: string
task-role-arn:
type: string
target-group:
type: string
target-container-name:
type: string
target-container-port:
type: integer
required:
- cluster
- service
Expand Down
63 changes: 57 additions & 6 deletions tests/command.bats
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ load '/usr/local/lib/bats/load.bash'

stub jq \
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' examples/hello-world.json : echo '{\"json\":true}'" \
"'.taskDefinition.revision' : echo 1"
"'.taskDefinition.revision' : echo 1" \
"-r '.[0].loadBalancers[0]' : echo null"

stub aws \
"ecs register-task-definition --family hello-world --container-definitions '{\"json\":true}' : echo '{\"taskDefinition\":{\"revision\":1}}'" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[*].status' --output text : echo '1'" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[?status==\`ACTIVE\`].status' --output text : echo '1'" \
"ecs describe-services --cluster my-cluster --services my-service --query 'services[?status==\`ACTIVE\`]' : echo 'null'" \
"ecs update-service --cluster my-cluster --service my-service --task-definition hello-world:1 : echo ok" \
"ecs wait services-stable --cluster my-cluster --services my-service : echo ok" \
"ecs describe-services --cluster my-cluster --service my-service : echo ok"
Expand All @@ -35,6 +37,8 @@ load '/usr/local/lib/bats/load.bash'
unset BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER
unset BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
unset BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
}

@test "Run a deploy when service does not exist" {
Expand All @@ -47,12 +51,14 @@ load '/usr/local/lib/bats/load.bash'

stub jq \
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' examples/hello-world.json : echo '{\"json\":true}'" \
"'.taskDefinition.revision' : echo 1"
"'.taskDefinition.revision' : echo 1" \
"-r '.[0].loadBalancers[0]' : echo null"

stub aws \
"ecs register-task-definition --family hello-world --container-definitions '{\"json\":true}' : echo '{\"taskDefinition\":{\"revision\":1}}'" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[*].status' --output text : echo -n ''" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[?status==\`ACTIVE\`].status' --output text : echo -n ''" \
"ecs create-service --cluster my-cluster --service-name my-service --task-definition hello-world:1 --desired-count 1 : echo -n ''" \
"ecs describe-services --cluster my-cluster --services my-service --query 'services[?status==\`ACTIVE\`]' : echo 'null'" \
"ecs update-service --cluster my-cluster --service my-service --task-definition hello-world:1 : echo ok" \
"ecs wait services-stable --cluster my-cluster --services my-service : echo ok" \
"ecs describe-services --cluster my-cluster --service my-service : echo ok"
Expand All @@ -67,6 +73,8 @@ load '/usr/local/lib/bats/load.bash'
unset BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER
unset BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
unset BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
}

@test "Run a deploy with task role" {
Expand All @@ -80,11 +88,54 @@ load '/usr/local/lib/bats/load.bash'

stub jq \
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' examples/hello-world.json : echo '{\"json\":true}'" \
"'.taskDefinition.revision' : echo 1"
"'.taskDefinition.revision' : echo 1" \
"-r '.[0].loadBalancers[0]' : echo null"

stub aws \
"ecs register-task-definition --family hello-world --container-definitions '{\"json\":true}' --task-role-arn arn:aws:iam::012345678910:role/world : echo '{\"taskDefinition\":{\"revision\":1}}'" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[*].status' --output text : echo '1'" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[?status==\`ACTIVE\`].status' --output text : echo '1'" \
"ecs describe-services --cluster my-cluster --services my-service --query 'services[?status==\`ACTIVE\`]' : echo 'null'" \
"ecs update-service --cluster my-cluster --service my-service --task-definition hello-world:1 : echo ok" \
"ecs wait services-stable --cluster my-cluster --services my-service : echo ok" \
"ecs describe-services --cluster my-cluster --service my-service : echo ok"

run "$PWD/hooks/command"

assert_success
assert_output --partial "Service is up 🚀"

unstub aws
unstub jq
unset BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER
unset BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
unset BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_ROLE_ARN
}

@test "Run a deploy with target group" {
export BUILDKITE_BUILD_NUMBER=1
export BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER=my-cluster
export BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE=my-service
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_FAMILY=hello-world
export BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE=hello-world:llamas
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION=examples/hello-world.json
export BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_GROUP=arn:aws:elasticloadbalancing:us-east-1:012345678910:targetgroup/alb/e987e1234cd12abc
export BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_CONTAINER_NAME=nginx
export BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_CONTAINER_PORT=80

stub jq \
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' examples/hello-world.json : echo '{\"json\":true}'" \
"'.taskDefinition.revision' : echo 1" \
"-r '.[0].loadBalancers[0]' : echo alb" \
"-r .containerName : echo nginx" \
"-r .containerPort : echo 80"

stub aws \
"ecs register-task-definition --family hello-world --container-definitions '{\"json\":true}' : echo '{\"taskDefinition\":{\"revision\":1}}'" \
"ecs describe-services --cluster my-cluster --service my-service --query 'services[?status==\`ACTIVE\`].status' --output text : echo -n ''" \
"ecs create-service --cluster my-cluster --service-name my-service --task-definition hello-world:1 --desired-count 1 --load-balancers targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:012345678910:targetgroup/alb/e987e1234cd12abc,containerName=nginx,containerPort=80 : echo -n ''" \
"ecs describe-services --cluster my-cluster --services my-service --query 'services[?status==\`ACTIVE\`]' : echo '[{\"loadBalancerName\": \"alb\",\"containerName\": \"nginx\",\"containerPort\": 80}]'" \
"ecs update-service --cluster my-cluster --service my-service --task-definition hello-world:1 : echo ok" \
"ecs wait services-stable --cluster my-cluster --services my-service : echo ok" \
"ecs describe-services --cluster my-cluster --service my-service : echo ok"
Expand Down

0 comments on commit ff15a21

Please sign in to comment.