mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-02-14 17:49:59 +00:00
483 lines
14 KiB
Bash
Executable File
483 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# Don't execute this script directly. Use ../trainer instead.
|
|
|
|
set -e # if we encounter an error, abort
|
|
|
|
export AWS_DEFAULT_OUTPUT=text
|
|
|
|
greet() {
|
|
hello=$(aws iam get-user --query 'User.UserName')
|
|
echo "Greetings, $hello/${USER}!"
|
|
}
|
|
|
|
deploy_hq(){
|
|
TAG=$1
|
|
need_tag $TAG
|
|
REMOTE_USER=ubuntu
|
|
REMOTE_HOST=$(aws_get_instance_ips_by_tag $TAG)
|
|
echo "Trying to reach $TAG instances..."
|
|
while ! tag_is_reachable $TAG; do
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
env | grep -i aws > envvars.sh
|
|
scp \
|
|
-o "UserKnownHostsFile /dev/null" \
|
|
-o "StrictHostKeyChecking=no" \
|
|
scripts/remote-execution.sh \
|
|
envvars.sh \
|
|
$REMOTE_USER@$REMOTE_HOST:/tmp/
|
|
|
|
ssh -A $REMOTE_USER@$REMOTE_HOST "bash /tmp/remote-execution.sh >>/tmp/pre.out 2>>/tmp/pre.err"
|
|
ssh -A $REMOTE_USER@$REMOTE_HOST
|
|
}
|
|
|
|
deploy_tag(){
|
|
TAG=$1
|
|
SETTINGS=$2
|
|
need_tag $TAG
|
|
link_tag $TAG
|
|
|
|
|
|
count=$(wc -l ips.txt)
|
|
|
|
# wait until all hosts are reachable before trying to deploy
|
|
echo "Trying to reach $TAG instances..."
|
|
while ! tag_is_reachable $TAG; do
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
|
|
echo "[[ Deploying tag $TAG ]]"
|
|
export SETTINGS
|
|
source scripts/postprep.rc
|
|
echo "Finished deploying $TAG."
|
|
echo "You may want to run one of the following commands:"
|
|
echo "./trainer pull-images $TAG"
|
|
echo "./trainer cards $TAG <settings/somefile.yaml>"
|
|
}
|
|
|
|
link_tag() {
|
|
TAG=$1
|
|
need_tag $TAG
|
|
IPS_FILE=tags/$TAG/ips.txt
|
|
need_ips_file $IPS_FILE
|
|
ln -sf $IPS_FILE ips.txt
|
|
}
|
|
|
|
pull_tag(){
|
|
TAG=$1
|
|
need_tag $TAG
|
|
link_tag $TAG
|
|
if [ ! -s $IPS_FILE ]; then
|
|
echo "Nonexistent or empty IPs file $IPS_FILE"
|
|
fi
|
|
|
|
# Pre-pull a bunch of images
|
|
pssh --timeout 900 'for I in \
|
|
debian:latest \
|
|
ubuntu:latest \
|
|
fedora:latest \
|
|
centos:latest \
|
|
postgres \
|
|
redis \
|
|
training/namer \
|
|
nathanleclaire/redisonrails; do
|
|
sudo -u docker docker pull $I
|
|
done'
|
|
|
|
echo "Finished pulling images for $TAG"
|
|
|
|
echo "You may now want to run:"
|
|
echo "./trainer cards $TAG <settings/somefile.yaml>"
|
|
}
|
|
|
|
wait_until_tag_is_running() {
|
|
max_retry=50
|
|
TAG=$1
|
|
COUNT=$2
|
|
i=0
|
|
done_count=0
|
|
while [[ $done_count -lt $COUNT ]]; do \
|
|
let "i += 1"
|
|
echo "Waiting: $done_count/$COUNT instances online"
|
|
done_count=$(aws ec2 describe-instances \
|
|
--filters "Name=instance-state-name,Values=running" \
|
|
"Name=tag:Name,Values=$TAG" \
|
|
--query "Reservations[*].Instances[*].State.Name" \
|
|
| tr "\t" "\n" \
|
|
| wc -l)
|
|
|
|
if [[ $i -gt $max_retry ]]; then
|
|
die "Timed out while waiting for instance creation (after $max_retry retries)"
|
|
fi
|
|
sleep 1
|
|
done
|
|
}
|
|
|
|
tag_is_reachable() {
|
|
TAG=$1
|
|
need_tag $TAG
|
|
link_tag $TAG
|
|
pssh -t 5 true 2>&1 >/dev/null
|
|
}
|
|
|
|
test_tag(){
|
|
ips_file=tags/$TAG/ips.txt
|
|
echo "Using random IP in $ips_file to run tests on $TAG"
|
|
ip=$(shuf -n 1 $ips_file)
|
|
test_vm $ip
|
|
echo "Tests complete. You may want to run one of the following commands:"
|
|
echo "./trainer cards $TAG <settings/somefile.yaml>"
|
|
}
|
|
|
|
test_vm() {
|
|
ip=$1
|
|
echo "[[ Testing instance with IP $(tput bold)$ip $(tput sgr0) ]]"
|
|
user=ubuntu
|
|
|
|
for cmd in "hostname" \
|
|
"whoami" \
|
|
"hostname -i" \
|
|
"cat /tmp/node" \
|
|
"cat /tmp/ipv4" \
|
|
"cat /etc/hosts" \
|
|
"hostnamectl status" \
|
|
"docker version | grep Version -B1" \
|
|
"docker-compose version" \
|
|
"docker-machine version" \
|
|
"docker images" \
|
|
"docker ps" \
|
|
"curl --silent localhost:55555" \
|
|
"sudo ls -la /mnt/ | grep docker" \
|
|
"env" \
|
|
"ls -la /home/docker/.ssh"; do
|
|
echo "=== $cmd ==="
|
|
echo "$cmd" |
|
|
ssh -A -q \
|
|
-o "UserKnownHostsFile /dev/null" \
|
|
-o "StrictHostKeyChecking=no" \
|
|
$user@$ip sudo -u docker -i
|
|
echo
|
|
done
|
|
}
|
|
|
|
make_key_name(){
|
|
SHORT_FINGERPRINT=$(ssh-add -l | grep RSA | head -n1 | cut -d " " -f 2 | tr -d : | cut -c 1-8)
|
|
echo "${SHORT_FINGERPRINT}-${USER}"
|
|
}
|
|
|
|
sync_keys() {
|
|
# make sure ssh-add -l contains "RSA"
|
|
ssh-add -l | grep -q RSA ||
|
|
die "The output of \`ssh-add -l\` doesn't contain 'RSA'. Start the agent, add your keys?"
|
|
|
|
AWS_KEY_NAME=$(make_key_name)
|
|
echo -n "Syncing keys... "
|
|
if ! aws ec2 describe-key-pairs --key-name "$AWS_KEY_NAME" &> /dev/null; then
|
|
aws ec2 import-key-pair --key-name $AWS_KEY_NAME \
|
|
--public-key-material "$(ssh-add -L \
|
|
| grep -i RSA \
|
|
| head -n1 \
|
|
| cut -d " " -f 1-2)" &> /dev/null
|
|
|
|
if ! aws ec2 describe-key-pairs --key-name "$AWS_KEY_NAME" &> /dev/null; then
|
|
die "Somehow, importing the key didn't work. Make sure that 'ssh-add -l | grep RSA | head -n1' returns an RSA key?"
|
|
else
|
|
echo "Imported new key $AWS_KEY_NAME."
|
|
fi
|
|
else
|
|
echo "Using existing key $AWS_KEY_NAME."
|
|
fi
|
|
}
|
|
|
|
suggest_amis() {
|
|
scripts/find-ubuntu-ami.sh -r $AWS_DEFAULT_REGION -a amd64 -v 16.04 -t hvm:ebs -N -q
|
|
}
|
|
|
|
get_token() {
|
|
if [ -z $USER ]; then
|
|
export USER=anonymous
|
|
fi
|
|
date +%Y-%m-%d-%H-%M-$USER
|
|
}
|
|
|
|
get_ami() {
|
|
suggest_amis | head -1
|
|
}
|
|
|
|
|
|
make_cards(){
|
|
# Generate cards for a given tag
|
|
TAG=$1
|
|
SETTINGS_FILE=$2
|
|
[[ -z "$SETTINGS_FILE" ]] && {
|
|
echo "Please specify the settings file you want to use."
|
|
echo "e.g.: settings/orchestration.yaml"
|
|
exit 1
|
|
}
|
|
aws_get_instance_ips_by_tag $TAG > tags/$TAG/ips.txt
|
|
|
|
# Remove symlinks to old cards
|
|
rm -f ips.html ips.pdf
|
|
|
|
# This will generate two files in the base dir: ips.pdf and ips.html
|
|
python scripts/ips-txt-to-html.py $SETTINGS_FILE
|
|
|
|
for f in ips.html ips.pdf; do
|
|
# Remove old versions of cards if they exist
|
|
rm -f tags/$TAG/$f
|
|
|
|
# Move the generated file and replace it with a symlink
|
|
mv -f $f tags/$TAG/$f && ln -s tags/$TAG/$f $f
|
|
done
|
|
|
|
echo "Cards created. You may want to run:"
|
|
echo "chromium ips.html"
|
|
echo "chromium ips.pdf"
|
|
}
|
|
|
|
describe_tag() {
|
|
# Display instance details and reachability/status information
|
|
TAG=$1
|
|
need_tag $TAG
|
|
echo "============= Tag: $TAG ============="
|
|
aws_display_instances_by_tag $TAG
|
|
aws_display_instance_statuses_by_tag $TAG
|
|
}
|
|
|
|
run_cli() {
|
|
case "$1" in
|
|
ami)
|
|
# A wrapper for scripts/find-ubuntu-ami.sh
|
|
shift
|
|
scripts/find-ubuntu-ami.sh -r $AWS_DEFAULT_REGION $*
|
|
echo
|
|
echo "Protip:"
|
|
echo "./trainer ami -a amd64 -v 16.04 -t hvm:ebs -N | grep -v ^REGION | cut -d\" \" -f15"
|
|
echo
|
|
echo "Suggestions:"
|
|
suggest_amis
|
|
;;
|
|
cards)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
make_cards $TAG $3
|
|
;;
|
|
deploy)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
if [[ $TAG == *"-hq"* ]]; then
|
|
echo "Deploying HQ"
|
|
deploy_hq $TAG
|
|
else
|
|
SETTINGS=$3
|
|
if [[ -z "$SETTINGS" ]]; then
|
|
echo "Please specify a settings file."
|
|
exit 1
|
|
fi
|
|
if ! [[ -f "$SETTINGS" ]]; then
|
|
echo "Settings file $SETTINGS not found."
|
|
exit 1
|
|
fi
|
|
echo "Deploying with settings $SETTINGS."
|
|
deploy_tag $TAG $SETTINGS
|
|
fi
|
|
;;
|
|
ids)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
IDS=$(aws_get_instance_ids_by_tag $TAG)
|
|
echo "$IDS"
|
|
|
|
# Just in case we managed to create instances but weren't able to tag them
|
|
echo "Lookup by client token $TAG:"
|
|
IDS=$(aws_get_instance_ids_by_client_token $TAG)
|
|
echo "$IDS"
|
|
;;
|
|
ips)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
mkdir -p tags/$TAG
|
|
aws_get_instance_ips_by_tag $TAG | tee tags/$TAG/ips.txt
|
|
link_tag $TAG
|
|
;;
|
|
list)
|
|
# list existing instances in a given batch
|
|
# to list batches, see "tags" command
|
|
echo "Using region $AWS_DEFAULT_REGION."
|
|
TAG=$2
|
|
need_tag $TAG
|
|
describe_tag $TAG
|
|
tag_is_reachable $TAG
|
|
echo "You may be interested in running one of the following commands:"
|
|
echo "./trainer ips $TAG"
|
|
echo "./trainer deploy $TAG <settings/somefile.yaml>"
|
|
;;
|
|
opensg)
|
|
aws ec2 authorize-security-group-ingress \
|
|
--group-name default \
|
|
--protocol icmp \
|
|
--port -1 \
|
|
--cidr 0.0.0.0/0
|
|
|
|
aws ec2 authorize-security-group-ingress \
|
|
--group-name default \
|
|
--protocol udp \
|
|
--port 0-65535 \
|
|
--cidr 0.0.0.0/0
|
|
|
|
aws ec2 authorize-security-group-ingress \
|
|
--group-name default \
|
|
--protocol tcp \
|
|
--port 0-65535 \
|
|
--cidr 0.0.0.0/0
|
|
;;
|
|
pull-images)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
pull_tag $TAG
|
|
;;
|
|
retag)
|
|
if [[ -z "$2" ]] || [[ -z "$3" ]]; then
|
|
die "Please specify old tag/token, and new tag."
|
|
fi
|
|
aws_tag_instances $2 $3
|
|
;;
|
|
shell)
|
|
# Get a shell in the container
|
|
export PS1="trainer@$AWS_DEFAULT_REGION# "
|
|
exec $SHELL
|
|
;;
|
|
start)
|
|
# Create $2 instances
|
|
COUNT=$2
|
|
|
|
if [ -z "$COUNT" ]; then
|
|
die "Indicate number of instances to start."
|
|
fi
|
|
|
|
greet # Print our AWS username, to ease the pain of credential-juggling
|
|
key_name=$(sync_keys) # Upload our SSH keys to AWS if needed, to be added to each VM's authorized_keys
|
|
AMI=$(get_ami) # Retrieve the AWS image ID
|
|
TOKEN=$(get_token) # generate a timestamp token for this batch of VMs
|
|
if [ ! -z $3 ]; then
|
|
# If an extra arg is present, append it to the tag
|
|
TOKEN=$TOKEN-$3
|
|
fi
|
|
|
|
echo "-----------------------------------"
|
|
echo "Starting $COUNT instances:"
|
|
echo " Region: $AWS_DEFAULT_REGION"
|
|
echo " Token/tag: $TOKEN"
|
|
echo " AMI: $AMI"
|
|
|
|
AWS_KEY_NAME=$(make_key_name)
|
|
result=$(aws ec2 run-instances \
|
|
--key-name $AWS_KEY_NAME \
|
|
--count $2 \
|
|
--instance-type t2.medium \
|
|
--client-token $TOKEN \
|
|
--image-id $AMI)
|
|
reservation_id=$(echo "$result" | head -1 | awk '{print $2}' )
|
|
echo " Key name: $AWS_KEY_NAME"
|
|
echo "Reservation ID: $reservation_id"
|
|
echo "-----------------------------------"
|
|
|
|
# if instance creation succeeded, we should have some IDs
|
|
IDS=$(aws_get_instance_ids_by_client_token $TOKEN)
|
|
if [ -z "$IDS" ]; then
|
|
die "Instance creation failed."
|
|
fi
|
|
|
|
# Tag these new instances with a tag that is the same as the token
|
|
TAG=$TOKEN
|
|
aws_tag_instances $TOKEN $TAG
|
|
|
|
wait_until_tag_is_running $TAG $COUNT
|
|
|
|
echo "[-------------------------------------------------------------------------------------]"
|
|
echo " Successfully created $2 instances with tag: $TAG"
|
|
echo "[-------------------------------------------------------------------------------------]"
|
|
|
|
mkdir -p tags/$TAG
|
|
IPS=$(aws_get_instance_ips_by_tag $TAG)
|
|
echo "$IPS" > tags/$TAG/ips.txt
|
|
link_tag $TAG
|
|
echo "To deploy or kill these instances, run one of the following:"
|
|
echo "./trainer deploy $TAG <settings/somefile.yaml>"
|
|
echo "./trainer list $TAG"
|
|
;;
|
|
status)
|
|
greet && echo
|
|
|
|
max_instances=$(aws ec2 describe-account-attributes \
|
|
--attribute-names max-instances \
|
|
--query 'AccountAttributes[*][AttributeValues]')
|
|
echo "Max instances: $max_instances" && echo
|
|
|
|
# Print list of AWS EC2 regions, highlighting ours ($AWS_DEFAULT_REGION) in the list
|
|
# If our $AWS_DEFAULT_REGION is not valid, the error message will be pretty descriptive:
|
|
# Could not connect to the endpoint URL: "https://ec2.foo.amazonaws.com/"
|
|
echo "Region:" # $AWS_DEFAULT_REGION."
|
|
aws ec2 describe-regions | awk '{print $3}' | grep --color=auto $AWS_DEFAULT_REGION -C50
|
|
|
|
;;
|
|
stop)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
aws_kill_instances_by_tag $TAG
|
|
;;
|
|
tag)
|
|
# add a tag to a batch of VMs
|
|
TAG=$2
|
|
NEW_TAG_KEY=$3
|
|
NEW_TAG_VALUE=$4
|
|
need_tag $TAG
|
|
need_tag $NEW_TAG_KEY
|
|
need_tag $NEW_TAG_VALUE
|
|
;;
|
|
test)
|
|
TAG=$2
|
|
need_tag $TAG
|
|
test_tag $TAG
|
|
;;
|
|
*)
|
|
echo "
|
|
./trainer <command> [n-instances|tag] [settings/file.yaml]
|
|
|
|
Core commands:
|
|
start n Start n instances
|
|
list [TAG] If a tag is provided, list its VMs. Otherwise, list tags.
|
|
deploy TAG Deploy all instances with a given tag
|
|
pull-images TAG Pre-pull docker images. Run only after deploying.
|
|
stop TAG Stop and delete instances tagged TAG
|
|
|
|
Extras:
|
|
ips TAG List all IPs of instances with a given tag (updates ips.txt)
|
|
ids TAG/TOKEN List all instance IDs with a given tag
|
|
shell Get a shell in the trainer container
|
|
status TAG Print information about this tag and its VMs
|
|
tags List all tags (per-region)
|
|
retag TAG/TOKEN TAG Retag instances with a new tag
|
|
|
|
Beta:
|
|
ami Look up Amazon Machine Images
|
|
cards FILE Generate cards
|
|
opensg Modify AWS security groups
|
|
"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
(
|
|
cd $SCRIPT_DIR
|
|
source scripts/cli.sh
|
|
source scripts/aws.sh
|
|
source scripts/rc
|
|
source scripts/colors.sh
|
|
mkdir -p tags
|
|
# TODO: unset empty envvars
|
|
run_cli "$@"
|
|
)
|