#!/usr/bin/env bash set -eu set -o pipefail usage() { cat <&2 exit 1 } eval "set -- $args" BRANCH="production" ROLES="base" DEBUG_KEY="" while true; do case "$1" in --help) usage exit 0 ;; --roles) shift ROLES="$1" shift ;; --branch) shift BRANCH="$1" shift ;; --debug-key) shift DEBUG_KEY="$1" shift ;; --) shift break ;; esac done if [ $# -ne 1 ]; then usage >&2 exit 1 fi SERVER="$1" set -x cd "$(dirname "$0")/../.." ./puppet/kandra/files/install-aws-cli AWS=/srv/zulip-aws-tools/bin/aws zulip_install_config_file="$HOME/.zulip-install-server.conf" if [ ! -f "$zulip_install_config_file" ]; then echo "No configuration file found in $zulip_install_config_file" exit 1 fi REPO_URL=$(crudini --get "$zulip_install_config_file" repo repo_url) for role in ${ROLES//,/ }; do if ! [ -f "./puppet/kandra/manifests/profile/$role.pp" ]; then echo "No such role kandra::profile::$role !" exit 1 fi done FULL_ROLES=$(echo "$ROLES" | perl -pe '$_=join(",",map{"kandra::profile::$_"} split ",")') function lookup() { KEY="$1" crudini --get "$zulip_install_config_file" "aws-$ROLES" "$KEY" 2>/dev/null \ || crudini --get "$zulip_install_config_file" aws "$KEY" } AWS_ZONE_ID=$(lookup zone_id) SECURITY_GROUPS=$(lookup security_groups) INSTANCE_TYPE=$(lookup instance_type) IAM_PROFILE=$(lookup iam_profile) AZ=$(lookup availability_zone) DISK_SIZE=$(lookup disk_size) # Determine the architecture ARCH=$($AWS ec2 describe-instance-types --instance-types "$INSTANCE_TYPE" --query 'InstanceTypes[*].ProcessorInfo.SupportedArchitectures[0]' --output text) # Lookup the latest 24.04 image AMI_ID=$($AWS ec2 describe-images --owners 099720109477 --filters 'Name=name,Values=ubuntu/images/hvm-ssd-gp3/ubuntu-*-24.04*' "Name=architecture,Values=$ARCH" --query 'sort_by(Images, &CreationDate)[-1].ImageId' --output text) # Verify it doesn't exist already ZONE_NAME=$($AWS route53 get-hosted-zone --id "$AWS_ZONE_ID" | jq -r '.HostedZone.Name') HOSTNAME="$SERVER.${ZONE_NAME%?}" # Remove trailing . EXISTING_RECORDS=$($AWS route53 list-resource-record-sets \ --hosted-zone-id "$AWS_ZONE_ID" \ --query "ResourceRecordSets[?Name == '$HOSTNAME.']" \ | jq '. | length') if [ "$EXISTING_RECORDS" != "0" ]; then echo "$HOSTNAME already exists!" exit 1 fi # https://docs.aws.amazon.com/cli/latest/reference/ec2/run-instances.html # shellcheck disable=SC2206 # We intentionally split $SECURITY_GROUPS EXTRA_ARGS+=( --iam-instance-profile "Name=\"$IAM_PROFILE\"" --image-id "$AMI_ID" --instance-type "$INSTANCE_TYPE" --security-group-ids $SECURITY_GROUPS --monitoring Enabled=true --placement "AvailabilityZone=$AZ" --block-device-mappings "DeviceName=/dev/sda1,Ebs={VolumeSize=$DISK_SIZE,VolumeType=gp3,Throughput=125,Iops=3000,Encrypted=true}" --metadata-options "InstanceMetadataTags=enabled" ) if [ -n "$DEBUG_KEY" ]; then EXTRA_ARGS+=( --key-name "$DEBUG_KEY" ) fi # Build up the provisioning script BOOTDATA=$(mktemp) { echo "#!/bin/bash" echo "SERVER=$SERVER" echo "HOSTNAME=$HOSTNAME" echo "FULL_ROLES=$FULL_ROLES" echo "REPO_URL=$REPO_URL" echo "BRANCH=$BRANCH" # Replace anything which looks like FOO="inline!bar/baz" with the # output of pack-local-script, which will make "$FOO" inside the # $BOOTDATA be the path to that script (smuggled inline and # unpacked before use). perl -ple 's|^(\w+)="inline!([^"]+)"|qx(./tools/setup/pack-local-script $1 $2)|e' ./tools/setup/bootstrap-aws-installer } >>"$BOOTDATA" TAG_ROLE_NAMES="$ROLES" TAGS="[{Key=Name,Value=$SERVER},{Key=role,Value=\"$TAG_ROLE_NAMES\"}]" INSTANCE_DATA=$($AWS ec2 run-instances \ --tag-specifications "ResourceType=instance,Tags=$TAGS" \ "${EXTRA_ARGS[@]}" \ --user-data "file://$BOOTDATA") INSTANCEID=$(echo "$INSTANCE_DATA" | jq -r .Instances[0].InstanceId) # Wait for public IP assignment PUBLIC_DNS_NAME="" while [ -z "$PUBLIC_DNS_NAME" ]; do sleep 1 INSTANCE_DATA=$($AWS ec2 describe-instances --instance-ids "$INSTANCEID") PUBLIC_DNS_NAME=$(echo "$INSTANCE_DATA" | jq -r .Reservations[0].Instances[0].PublicDnsName) done # Add the hostname to the zone ROUTE53_CHANGES=$(mktemp) cat >"$ROUTE53_CHANGES" <>> Install started successfully! Provisioning takes 5-6min." echo " sleep 360 && tsh ssh root@$HOSTNAME"