-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathecs-deploy.sh
258 lines (223 loc) · 7.27 KB
/
ecs-deploy.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/usr/bin/env bash
#######################################################################
# This script performs deployment of ECS Service using AWS CodeDeploy
#
# Heavily inspired by https://github.com/silinternational/ecs-deploy
# and https://gist.github.com/antonbabenko/632b54e8e488b9f48d016238792a9193
#
# Hacked on by: John Veldboom
#######################################################################
set -e
#set -x
####################################################################
# DO NOT TOUCH BELOW THIS LINE if you don't know what you are doing
####################################################################
APPSPEC_FILENAME="appspec_ecs.yaml"
APPSPEC_FILE=false
TASK_DEF_FILENAME="task_def.json"
TASK_DEFINITION_FILE=false
function print_usage {
echo
echo "Usage: ecs-deploy [OPTIONS]"
echo
echo "Required arguments:"
echo
echo -e " --cluster\t\t\tName of ECS Cluster"
echo -e " --service\t\t\tName of ECS Service to update"
echo -e " --image\t\t\tImage ID to deploy (eg, 123456789000.dkr.ecr.us-east-1.amazonaws.com/backend:v1.2.3)"
echo -e " --codedeploy-application\t\tName of CodeDeploy Application"
echo -e " --codedeploy-deployment-group\t\tName of CodeDeploy Deployment Group"
echo -e " --container-port\t\tContainer Port Number"
echo
echo "Optional arguments:"
echo
echo -e " --task-definition-file\tLocal task definition file to use"
echo -e " --appspec-file\t\tLocal AppSpec file to use"
echo
echo "Example with all arguments:"
echo
echo " ecs-deploy \\"
echo " --cluster production \\"
echo " --service backend \\"
echo " --container-port 3000 \\"
echo " --codedeploy-application backend_app \\"
echo " --codedeploy-deployment-group backend_app_dg \\"
echo " --image 123456789000.dkr.ecr.us-east-1.amazonaws.com/api:1.2.0 \\"
echo " --appspec-file appspec.yaml \\"
echo " --task-definition-file task-definition.json"
echo
}
function assert_not_empty {
local readonly arg_name="$1"
local readonly arg_value="$2"
if [[ -z "$arg_value" ]]; then
echo "ERROR: The value for '$arg_name' cannot be empty"
print_usage
exit 1
fi
}
function assert_is_installed {
local readonly name="$1"
if [[ ! $(command -v ${name}) ]]; then
echo "ERROR: The binary '$name' is required by this script but is not installed or in the system's PATH."
exit 1
fi
}
function get_current_task_definition() {
TASK_DEFINITION_ARN=$(aws ecs describe-services --services "$service" --cluster "$cluster" | jq -r ".services[0].taskDefinition")
TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$TASK_DEFINITION_ARN")
}
function create_new_task_def_json() {
DEF=$(echo "$TASK_DEFINITION" | jq -r ".taskDefinition.containerDefinitions[].image=\"$image\"" | jq -r ".taskDefinition")
# Default JQ filter for new task definition
NEW_DEF_JQ_FILTER="executionRoleArn: .executionRoleArn, family: .family, volumes: .volumes, containerDefinitions: .containerDefinitions, placementConstraints: .placementConstraints"
# Some options in task definition should only be included in new definition if present in
# current definition. If found in current definition, append to JQ filter.
CONDITIONAL_OPTIONS=(networkMode taskRoleArn placementConstraints)
for i in "${CONDITIONAL_OPTIONS[@]}"; do
re=".*${i}.*"
if [[ "$DEF" =~ $re ]]; then
NEW_DEF_JQ_FILTER="${NEW_DEF_JQ_FILTER}, ${i}: .${i}"
fi
done
# Updated jq filters for AWS Fargate
REQUIRES_COMPATIBILITIES=$(echo "${DEF}" | jq -r ". | select(.requiresCompatibilities != null) | .requiresCompatibilities[]")
if [[ "${REQUIRES_COMPATIBILITIES}" == 'FARGATE' ]]; then
FARGATE_JQ_FILTER='executionRoleArn: .executionRoleArn, requiresCompatibilities: .requiresCompatibilities, cpu: .cpu, memory: .memory'
NEW_DEF_JQ_FILTER="${NEW_DEF_JQ_FILTER}, ${FARGATE_JQ_FILTER}"
fi
# Build new DEF with jq filter
NEW_TASK_DEF=$(echo "$DEF" | jq "{${NEW_DEF_JQ_FILTER}}")
# If in test mode output $NEW_TASK_DEF
if [ "$BASH_SOURCE" != "$0" ]; then
echo "$NEW_TASK_DEF"
fi
}
function create_task_def_file() {
echo "$NEW_TASK_DEF" > $TASK_DEF_FILENAME
}
function create_app_spec_file() {
container_name=$(echo "$NEW_TASK_DEF" | jq -r ".containerDefinitions[].name")
echo "---
version: 1
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: PlaceholderForTaskDefinition
LoadBalancerInfo:
ContainerName: ${container_name}
ContainerPort: ${container_port}
PlatformVersion: "1.4.0"
" > $APPSPEC_FILENAME
}
function ecs_deploy_service() {
aws ecs deploy \
--cluster "$cluster" \
--service "$service" \
--task-definition "$TASK_DEF_FILENAME" \
--codedeploy-appspec "$APPSPEC_FILENAME" \
--codedeploy-application "$codedeploy_application" \
--codedeploy-deployment-group "$codedeploy_deployment_group"
}
function ecs_deploy {
assert_is_installed "jq"
local cluster=""
local service=""
local image=""
local iam_role=""
while [[ $# > 0 ]]; do
local key="$1"
case "$key" in
--iam-role)
iam_role="$2"
shift
;;
--cluster)
cluster="$2"
shift
;;
--service)
service="$2"
shift
;;
--codedeploy-application)
codedeploy_application="$2"
shift
;;
--codedeploy-deployment-group)
codedeploy_deployment_group="$2"
shift
;;
--image)
image="$2"
shift
;;
--container-port)
container_port="$2"
shift
;;
--task-definition-file)
TASK_DEFINITION_FILE="$2"
shift
;;
--appspec-file)
APPSPEC_FILE="$2"
shift
;;
--help)
print_usage
exit
;;
*)
echo "ERROR: Unrecognized argument: $key"
print_usage
exit 1
;;
esac
shift
done
assert_not_empty "--cluster" "$cluster"
assert_not_empty "--service" "$service"
assert_not_empty "--image" "$image"
assert_not_empty "--codedeploy-application" "$codedeploy_application"
assert_not_empty "--codedeploy-deployment-group" "$codedeploy_deployment_group"
assert_not_empty "--container-port" "$container_port"
echo "Cluster: $cluster"
echo "Service: $service"
echo "Image: $image"
echo
# Dynamically get task definition or read it from local file
if [ $TASK_DEFINITION_FILE == false ]; then
echo "Getting latest task definition for the service $service"
get_current_task_definition
echo "Task definition ARN: $TASK_DEFINITION_ARN"
echo
echo "Create new task definition"
create_new_task_def_json
echo "Created"
echo
echo "Create file $TASK_DEF_FILENAME"
create_task_def_file
echo "Created"
echo
else
echo "Using local task definition '$TASK_DEFINITION_FILE' for the service $service"
TASK_DEF_FILENAME=$TASK_DEFINITION_FILE
NEW_TASK_DEF=`cat $TASK_DEF_FILENAME`
fi
# Create AppSpec file or use local file
if [ $APPSPEC_FILE == false ]; then
echo "Create file $APPSPEC_FILENAME"
create_app_spec_file
echo "Created"
echo
else
echo "Using local appspec file '$APPSPEC_FILE'"
APPSPEC_FILENAME=$APPSPEC_FILE
fi
echo "Deploy ECS service"
ecs_deploy_service
echo "Done!"
}
ecs_deploy "$@"