-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathecs-run
executable file
·287 lines (256 loc) · 8.45 KB
/
ecs-run
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#!/usr/bin/env bash
set -e
function usage() {
set -e
cat <<EOM
##### ecs-run #####
Simple script for running tasks on Amazon Elastic Container Service
One of the following is required:
Required arguments:
-d | --task-definition Name of task definition to deploy
-c | --cluster Name of ECS cluster
-n | --container-name Name of Docker container
Optional arguments:
-m | --command
--aws-instance-profile Use the IAM role associated with this instance
-D | --desired-count The number of instantiations of the task to place.
-t | --retries Default is 12 for two hours. Script monitors ECS Service for new task definition to be running.
-e | --tag-env-var Get image tag name from environment variable. If provided this will override value specified in image name argument.
-v | --verbose Verbose output
-r | --region AWS Region
-p | --profile AWS Profile to use
Requirements:
aws: AWS Command Line Interface
jq: Command-line JSON processor
Examples:
Simple deployment of a service (Using env vars for AWS settings):
ecs-run -c production1 -d foo-taskdef -n foo-container -m "sleep,15"
All options:
EOM
exit 2
}
if [ $# == 0 ]; then usage; fi
function _echo {
echo "${PREFIX}$@"
}
function command_to_array {
local plain_array=""
local IFS=' '
for item in $@; do
plain_array=$plain_array\"$item\"
done
echo "[$(echo $plain_array | sed 's/,/","/g')]"
}
# Check requirements
function require {
command -v $1 > /dev/null 2>&1 || {
_echo "Some of the required software is not installed:"
_echo " please install $1" >&2;
exit 1;
}
}
function logs_link {
local service_name=$1
local task_id=$(echo $2 | cut -d / -f 2)
local log_config=$($AWS_ECS describe-task-definition --task-definition ${TASK_DEFINITION} | jq ".taskDefinition.containerDefinitions | map(select(.name == \"${CONTAINER_NAME}\"))[0].logConfiguration")
local driver=$(echo $log_config | jq '.logDriver' | cut -d'"' -f2)
if [ $driver != "awslogs" ]; then
echo "Not configured to be logged in CloudWatch"
return
fi
local region=$(echo $log_config | jq '.options["awslogs-region"]')
if [ -z $region ]; then
region=$($AWS_CLI configure get region || echo "us-east-1")
fi
region=$(echo $region | cut -d'"' -f2)
local log_group=$(echo $log_config | jq '.options["awslogs-group"]' | cut -d'"' -f2)
local prefix=$(echo $log_config | jq '.options["awslogs-stream-prefix"]' | cut -d'"' -f2)
echo "https://$region.console.aws.amazon.com/cloudwatch/home?region=$region#logEventViewer:group=${log_group};stream=${prefix}/${service_name}/${task_id}"
}
# Check for AWS, AWS Command Line Interface
require aws
# Check for jq, Command-line JSON processor
require jq
# Setup default values for variables
CLUSTER=false
TASK_DEFINITION=false
CONTAINER_NAME=false
COMMAND=false
VERBOSE=false
TAGVAR=false
AWS_CLI="$(which aws) --output json"
DESIRED=1
# Retry the command if the reason of failure is REOURCE:CPU or RESOURCE:MEMORY
MAX_RETRIES=5
RETRY_SLEEP_TIME=60
RETRIES_ACCEPTED_FAILURES=(
RESOURCE:CPU
RESOURCE:MEMORY
)
# Two hours waiting
MAX_WAITER_RETRIES=12
# Loop through arguments, two at a time for key and value
while [[ $# > 0 ]]
do
key="$1"
case $key in
-p|--profile)
AWS_PROFILE="$2"
shift # past argument
;;
--aws-instance-profile)
AWS_IAM_ROLE=true
;;
-c|--cluster)
CLUSTER="$2"
shift # past argument
;;
-n|--container-name)
CONTAINER_NAME="$2"
shift # past argument
;;
-d|--task-definition)
TASK_DEFINITION="$2"
shift
;;
-m|--command)
COMMAND="$(command_to_array $2)"
shift
;;
-r|--region)
AWS_DEFAULT_REGION="$2"
shift # past argument
;;
-D|--desired-count)
DESIRED="$2"
shift
;;
-e|--tag-env-var)
TAGVAR="$2"
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-t|--retries)
MAX_WAITER_RETRIES="$2"
shift
;;
*)
_echo "ERROR: $1 is not a valid option"
usage
exit 2
;;
esac
shift # past argument or value
done
if [ $VERBOSE == true ]; then
set -x
fi
if [ $TASK_DEFINITION == false ]; then
_echo "TASK DEFINITON is required. You can pass the value using -d / --task-definiton for a task"
exit 1
fi
if [ $CLUSTER == false ]; then
_echo "CLUSTER is required. You can pass the value using -c or --cluster"
exit 1
fi
if [ $CONTAINER_NAME == false ]; then
_echo "CONTAINER_NAME is required. You can pass the value using -n or --container-name"
exit 1
fi
if [ "$COMMAND" == false ]; then
OVERRIDES="{\"containerOverrides\": [{\"name\": \"$CONTAINER_NAME\"}]}"
else
OVERRIDES='{"containerOverrides": [{"name": "'"$CONTAINER_NAME"'", "command": '"$COMMAND"'}]}'
fi
if [ -z ${AWS_DEFAULT_REGION+x} ]; then
unset AWS_DEFAULT_REGION
else
AWS_CLI="$AWS_CLI --region $AWS_DEFAULT_REGION"
fi
if [ -z ${AWS_PROFILE+x} ]; then
unset AWS_PROFILE
else
AWS_CLI="$AWS_CLI --profile $AWS_PROFILE"
fi
if [ -n "$DRY_RUN" ]; then
AWS_ECS="echo $AWS_CLI ecs"
else
AWS_ECS="$AWS_CLI ecs"
fi
run_task () {
local run_result
run_result=$($AWS_ECS run-task \
--cluster $CLUSTER \
--task-definition $TASK_DEFINITION \
--count $DESIRED \
--overrides "$OVERRIDES")
local returned_value=$?
echo $run_result
return $returned_value
}
DO_RETRY=1
RETRIES=0
set +e
while [ "$RETRIES" -lt $MAX_RETRIES ] && [ $DO_RETRY -eq 1 ]; do
DO_RETRY=0
REASON_FAILURE=''
RUN_TASK=$(run_task)
RUN_TASK_EXIT_CODE=$?
_echo $RUN_TASK
FAILURES=$(echo $RUN_TASK | jq '.failures|length')
if [ $FAILURES -eq 0 ]; then
TASK_ARN=$(echo $RUN_TASK | jq '.tasks[0].taskArn' | sed -e 's/^"//' -e 's/"$//')
WAITER_RETRY=1
WAITER_RETRIES=0
while [ $WAITER_RETRIES -lt $MAX_WAITER_RETRIES ] && [ $WAITER_RETRY -eq 1 ]; do
WAITER_RETRY=0
$AWS_ECS wait tasks-stopped --tasks "$TASK_ARN" --cluster $CLUSTER 2>/dev/null
WAITER_EXIT_CODE=$?
if [ $WAITER_EXIT_CODE -eq 0 ]; then
DESCRIBE_TASKS=$($AWS_ECS describe-tasks --tasks "$TASK_ARN" --cluster $CLUSTER)
EXIT_CODE=$(echo $DESCRIBE_TASKS | jq ".tasks[0].containers | map(select(.name == \"${CONTAINER_NAME}\"))[0].exitCode")
if [ $EXIT_CODE -eq 0 ]; then
_echo "ECS task exited successfully"
_echo $(logs_link $CONTAINER_NAME $TASK_ARN)
exit 0
else
_echo "ECS task failed: $DESCRIBE_TASKS"
_echo $(logs_link $CONTAINER_NAME $TASK_ARN)
exit $EXIT_CODE
fi
elif [ $WAITER_EXIT_CODE -eq 255 ]; then
((WAITER_RETRIES++))
WAITER_RETRY=1
if [ $WAITER_RETRIES -eq $MAX_WAITER_RETRIES ]; then
_echo "ECS Waiter max retries reached, $WAITER_RETRIES, exit"
exit 255
fi
_echo "ECS Waiter because timeout, waiter retry $WAITER_RETRIES (don't launch the task other time)"
else
_echo "ECS Waiter failed, status: $WAITER_EXIT_CODE"
_echo $(logs_link $CONTAINER_NAME $TASK_ARN)
exit $WAITER_EXIT_CODE
fi
done
else
REASON_FAILURE=$(echo $RUN_TASK | jq -r '.failures[0].reason')
if [ -n "$REASON_FAILURE" ] && [[ "${RETRIES_ACCEPTED_FAILURES[@]}" =~ $REASON_FAILURE ]]; then
DO_RETRY=1
((RETRIES++))
if [ -n "$REASON_FAILURE" ] && [ $RETRIES -eq $MAX_RETRIES ]; then
_echo "Max RETRIES reached REASON: $REASON_FAILURE RETRIES: $RETRIES"
_echo $(logs_link $CONTAINER_NAME $TASK_ARN)
exit 253
fi
_echo "Retrying in ${RETRY_SLEEP_TIME}s, try number: $RETRIES because: $REASON_FAILURE"
sleep $RETRY_SLEEP_TIME
else
_echo "ECS task failed: $REASON_FAILURE"
_echo $(logs_link $CONTAINER_NAME $TASK_ARN)
exit 1
fi
fi
done