Skip to content

Commit ecab00c

Browse files
author
Patrick Robinson
authored
Merge pull request #33 from buildkite-plugins/support-multiple-images
Add support for multiple images in a single pipeline/definition
2 parents 827b090 + 49fa226 commit ecab00c

File tree

5 files changed

+114
-12
lines changed

5 files changed

+114
-12
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,16 @@ Example: `"my-task"`
5353

5454
### `image`
5555

56-
The Docker image to deploy.
56+
The Docker image to deploy. This can be an array to substitute multiple images in a single container definition.
5757

58-
Example: `"012345.dkr.ecr.us-east-1.amazonaws.com/my-service:123"`
58+
Examples:
59+
`"012345.dkr.ecr.us-east-1.amazonaws.com/my-service:123"`
60+
61+
```yaml
62+
image:
63+
- "012345.dkr.ecr.us-east-1.amazonaws.com/my-service:123"
64+
- "012345.dkr.ecr.us-east-1.amazonaws.com/nginx:123"
65+
```
5966

6067
### `target-group`
6168

examples/multiple-images.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[
2+
{
3+
"essential": true,
4+
"image": "amazon/amazon-ecs-sample",
5+
"memory": 100,
6+
"name": "sample",
7+
"portMappings": [
8+
{
9+
"containerPort": 80,
10+
"hostPort": 80
11+
}
12+
]
13+
},
14+
{
15+
"essential": true,
16+
"image": "amazon/amazon-ecs-sample",
17+
"memory": 100,
18+
"name": "sample",
19+
"portMappings": [
20+
{
21+
"containerPort": 80,
22+
"hostPort": 80
23+
}
24+
]
25+
}
26+
]

hooks/command

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,36 @@
11
#!/bin/bash
22
set -euo pipefail
33

4+
# Reads either a value or a list from plugin config
5+
function plugin_read_list() {
6+
prefix_read_list "BUILDKITE_PLUGIN_ECS_DEPLOY_$1"
7+
}
8+
9+
# Reads either a value or a list from the given env prefix
10+
function prefix_read_list() {
11+
local prefix="$1"
12+
local parameter="${prefix}_0"
13+
14+
if [[ -n "${!parameter:-}" ]]; then
15+
local i=0
16+
local parameter="${prefix}_${i}"
17+
while [[ -n "${!parameter:-}" ]]; do
18+
echo "${!parameter}"
19+
i=$((i+1))
20+
parameter="${prefix}_${i}"
21+
done
22+
elif [[ -n "${!prefix:-}" ]]; then
23+
echo "${!prefix}"
24+
fi
25+
}
26+
427
cluster=${BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER?}
528
task_family=${BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_FAMILY?}
629
service_name=${BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE?}
7-
image=${BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE?}
30+
images=()
31+
while read -r line ; do
32+
[[ -n "$line" ]] && images+=("$line")
33+
done <<< "$(plugin_read_list IMAGE)"
834
task_definition=${BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION?}
935
desired_count=${BUILDKITE_PLUGIN_ECS_DEPLOY_DESIRED_COUNT:-"1"}
1036
task_role_arn=${BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_ROLE_ARN:-""}
@@ -22,9 +48,11 @@ function create_service() {
2248
local target_group_arguments
2349
target_group_arguments=$(generate_target_group_arguments "$5" "$6" "$7")
2450

51+
# shellcheck disable=SC2016
2552
service_defined=$(aws ecs describe-services --cluster "$cluster_name" --service "$service_name" --query 'services[?status==`ACTIVE`].status' --output text |wc -l)
2653
if [[ $service_defined -eq 0 ]]; then
2754
echo "--- :ecs: Creating a Service $service_name in cluster $cluster_name"
55+
# shellcheck disable=SC2086
2856
aws ecs create-service --cluster "$cluster_name" --service-name "$service_name" --task-definition "$task_definition" --desired-count "$desired_count" $target_group_arguments
2957
fi
3058
}
@@ -41,10 +69,14 @@ function generate_target_group_arguments() {
4169
}
4270

4371
## This is the template definition of your containers
44-
container_definitions_json=$(jq --arg IMAGE "$image" \
45-
'.[0].image=$IMAGE' \
46-
"$task_definition"
47-
)
72+
image_idx=0
73+
container_definitions_json=$(cat "${task_definition}")
74+
for image in "${images[@]}"; do
75+
container_definitions_json=$(echo "$container_definitions_json" | jq --arg IMAGE "$image" \
76+
".[${image_idx}].image=\$IMAGE"
77+
)
78+
image_idx=$((image_idx+1))
79+
done
4880

4981
echo "jq --arg IMAGE $image \
5082
'.taskDefinition.containerDefinitions[0].image=\$IMAGE' \
@@ -73,6 +105,7 @@ echo "Registered ${task_family}:${task_revision}"
73105
# Create service if it doesn't already exist
74106
create_service "$cluster" "${task_family}:${task_revision}" "$service_name" "$desired_count" "$target_group" "$target_container" "$target_port"
75107

108+
# shellcheck disable=SC2016
76109
lb_config=$(aws ecs describe-services --cluster "$cluster" --services "$service_name" --query 'services[?status==`ACTIVE`]' |jq -r '.[0].loadBalancers[0]')
77110
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"
78111

plugin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ configuration:
1515
task-family:
1616
type: string
1717
image:
18-
type: string
18+
type: [ string, array ]
1919
desired-count:
2020
type: string
2121
task-role-arn:

tests/command.bats

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ load '/usr/local/lib/bats/load.bash'
1515
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION=examples/hello-world.json
1616

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

@@ -38,7 +38,43 @@ load '/usr/local/lib/bats/load.bash'
3838
unset BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE
3939
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
4040
unset BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE
41+
}
42+
43+
@test "Run a deploy with multiple images" {
44+
export BUILDKITE_BUILD_NUMBER=1
45+
export BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER=my-cluster
46+
export BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE=my-service
47+
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_FAMILY=hello-world
48+
export BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE_0=hello-world:llamas
49+
export BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE_1=hello-world:alpacas
50+
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION=examples/multiple-images.json
51+
52+
stub jq \
53+
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' : echo '[{\"image\":\"hello-world:llamas\"},{\"image\":\"replaceme\"}]'" \
54+
"--arg IMAGE hello-world:alpacas '.[1].image=\$IMAGE' : echo '[{\"image\":\"hello-world:llamas\"},{\"image\":\"hello-world:alpacas\"}]'" \
55+
"'.taskDefinition.revision' : echo 1" \
56+
"-r '.[0].loadBalancers[0]' : echo null"
57+
58+
stub aws \
59+
"ecs register-task-definition --family hello-world --container-definitions '[{\"image\":\"hello-world:llamas\"},{\"image\":\"hello-world:alpacas\"}]' : echo '{\"taskDefinition\":{\"revision\":1}}'" \
60+
"ecs describe-services --cluster my-cluster --service my-service --query 'services[?status==\`ACTIVE\`].status' --output text : echo '1'" \
61+
"ecs describe-services --cluster my-cluster --services my-service --query 'services[?status==\`ACTIVE\`]' : echo 'null'" \
62+
"ecs update-service --cluster my-cluster --service my-service --task-definition hello-world:1 : echo ok" \
63+
"ecs wait services-stable --cluster my-cluster --services my-service : echo ok" \
64+
"ecs describe-services --cluster my-cluster --service my-service : echo ok"
65+
66+
run "$PWD/hooks/command"
67+
68+
assert_success
69+
assert_output --partial "Service is up 🚀"
70+
71+
unstub aws
72+
unstub jq
73+
unset BUILDKITE_PLUGIN_ECS_DEPLOY_CLUSTER
74+
unset BUILDKITE_PLUGIN_ECS_DEPLOY_SERVICE
4175
unset BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION
76+
unset BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE_0
77+
unset BUILDKITE_PLUGIN_ECS_DEPLOY_IMAGE_1
4278
}
4379

4480
@test "Run a deploy when service does not exist" {
@@ -50,7 +86,7 @@ load '/usr/local/lib/bats/load.bash'
5086
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_DEFINITION=examples/hello-world.json
5187

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

@@ -87,7 +123,7 @@ load '/usr/local/lib/bats/load.bash'
87123
export BUILDKITE_PLUGIN_ECS_DEPLOY_TASK_ROLE_ARN=arn:aws:iam::012345678910:role/world
88124

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

@@ -125,7 +161,7 @@ load '/usr/local/lib/bats/load.bash'
125161
export BUILDKITE_PLUGIN_ECS_DEPLOY_TARGET_CONTAINER_PORT=80
126162

127163
stub jq \
128-
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' examples/hello-world.json : echo '{\"json\":true}'" \
164+
"--arg IMAGE hello-world:llamas '.[0].image=\$IMAGE' : echo '{\"json\":true}'" \
129165
"'.taskDefinition.revision' : echo 1" \
130166
"-r '.[0].loadBalancers[0]' : echo alb" \
131167
"-r .containerName : echo nginx" \

0 commit comments

Comments
 (0)