Created
December 9, 2016 12:27
-
-
Save jonleighton/6044a080dbb7cef035299e07dc7eed9c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
export AWS_REGION="eu-west-1" | |
export COMMIT_ID=$1 | |
# Docker Hub account with read-only access to the repository | |
export DOCKER_HUB_USERNAME="[FILL ME IN]" | |
export DOCKER_HUB_PASSWORD="[FILL ME IN]" | |
function check_image_exists { | |
local baseurl="https://hub.docker.com/v2" | |
local token=$( | |
curl --header "Content-Type: application/json" \ | |
--request "POST" \ | |
--data "{\"username\": \"$DOCKER_HUB_USERNAME\", \"password\": \"$DOCKER_HUB_PASSWORD\"}" \ | |
--fail \ | |
--silent \ | |
--show-error \ | |
"$baseurl/users/login/" | | |
jq --raw-output ".token" | |
) | |
local tag_id=$( | |
curl --header "Authorization: JWT $token" \ | |
--silent \ | |
--fail \ | |
"$baseurl/repositories/loco2/loco2/tags/$COMMIT_ID/" | | |
jq --raw-output ".id" | |
) | |
if [[ "$tag_id" = "" ]]; then | |
echo "Docker image loco2/loco2:$COMMIT_ID is missing" > /dev/stderr | |
return 1 | |
else | |
return 0 | |
fi | |
} | |
# While the deploy is ongoing, there will be one "ACTIVE" deployment and one "PRIMARY" one | |
# When it finishes, there will just be one deployment. | |
function is_deploy_finished { | |
local cluster=$1 | |
local service=$2 | |
local deployments_count=$( | |
aws ecs describe-services --region $AWS_REGION \ | |
--cluster $cluster \ | |
--services $service | | |
jq '.services[0].deployments | length' | |
) | |
if [[ "$deployments_count" = "1" ]]; then | |
echo "true" | |
else | |
echo "false" | |
fi | |
} | |
# Update the task definition to freeze to the specific SHA that we are deploying | |
function update_task_definition { | |
local family=$1 | |
local file="$family.json" | |
aws ecs describe-task-definition --region $AWS_REGION \ | |
--task-definition $family | | |
jq '.taskDefinition | {family, volumes, containerDefinitions} | .containerDefinitions[].image="loco2/loco2:" + env.COMMIT_ID' > $file | |
aws ecs register-task-definition --region $AWS_REGION \ | |
--family $family \ | |
--cli-input-json file://$PWD/$file | | |
jq --raw-output '.taskDefinition.taskDefinitionArn' | |
rm $file | |
} | |
# Update the task definition for a service, and then updates the service to use the new task definition | |
# revision, triggering a rolling deploy. | |
function update_service { | |
local cluster=$1 | |
local service=$2 | |
echo "Updating service $service... " | |
aws ecs update-service --region $AWS_REGION \ | |
--cluster $cluster \ | |
--service $service \ | |
--task-definition $(update_task_definition $service) \ | |
> /dev/null | |
echo "Waiting for $service deployment to complete..." | |
while [ "$(is_deploy_finished $cluster $service)" = "false" ] | |
do | |
sleep 1 | |
done | |
echo "$service deployment complete" | |
} | |
function is_stopped_task { | |
local cluster=$1 | |
local task=$2 | |
local status=$( | |
aws ecs describe-tasks --region $AWS_REGION \ | |
--cluster $cluster \ | |
--tasks $task | | |
jq --raw-output '.tasks[0].lastStatus' | |
) | |
if [[ "$status" = "STOPPED" ]]; then | |
echo "true" | |
else | |
echo "false" | |
fi | |
} | |
# To prepare the deploy, we update the rake-deploy-prepare task definition to use our deploy SHA, | |
# and then run it on the worker cluster. This task runs migrations etc. | |
function prepare_deploy { | |
local cluster="worker" | |
local family="rake-deploy-prepare" | |
echo -n "Running \`rake deploy:prepare\` in new container image... " | |
local task=$( | |
aws ecs run-task --region $AWS_REGION \ | |
--cluster $cluster \ | |
--task-definition $(update_task_definition $family) \ | |
--started-by "$(echo $COMMIT_ID | cut -c 1-36)" | | |
jq --raw-output '.tasks[0].taskArn' | |
) | |
while [ "$(is_stopped_task $cluster $task)" = "false" ] | |
do | |
sleep 1 | |
done | |
local exit_code=$( | |
aws ecs describe-tasks --region $AWS_REGION \ | |
--cluster $cluster \ | |
--tasks $task | | |
jq --raw-output '.tasks[0].containers[0].exitCode' | |
) | |
if [[ "$exit_code" = "0" ]]; then | |
echo "success" | |
else | |
echo "failed" | |
fi | |
return $exit_code | |
} | |
pids=() | |
# Runs a child process in the background and adds its pid to the pids array | |
function run_concurrently { | |
$@ & | |
pids+=($!) | |
} | |
function wait_for_children { | |
# We must specify the pid we are waiting for. If we just do "wait" with no | |
# args, it will wait for all child processes, but will return a successful exit | |
# status regardless of the exit status of the children. | |
for pid in ${pids[*]}; do | |
wait $pid | |
done | |
} | |
# If these fail, we won't continue with the deploy | |
check_image_exists | |
prepare_deploy | |
run_concurrently update_service web web | |
run_concurrently update_service worker worker-core | |
run_concurrently update_service worker worker-maintenance-reports | |
run_concurrently update_service worker worker-maintenance-other | |
wait_for_children |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment