mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-30 19:43:47 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			210 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env bash
 | |
| set -eu
 | |
| set -o pipefail
 | |
| 
 | |
| usage() {
 | |
|     cat <<EOF
 | |
| USAGE: $0 [--roles=roles] [--branch=main] [--debug-key=username] server
 | |
| 
 | |
| Installs an empty Ubuntu server in AWS with a Zulip server role.
 | |
| 
 | |
|  * server is the local part of the hostname (e.g. postgres0)
 | |
| 
 | |
|  * roles is a comma-separated list of Puppet profile names; these
 | |
|    will get 'kandra::profile::' prepended to them, and passed
 | |
|    to scripts/lib/install -- e.g. 'postgresql'
 | |
|  * branch is used to override the default branch to install from.
 | |
|  * username is the name of the AWS SSH key pair to allow logins as
 | |
|    'ubuntu' with during initial boot; this is purely for debugging the
 | |
|    bootstrapping process.
 | |
| 
 | |
| Reads configuration from $HOME/.zulip-install-server.conf, which should look like:
 | |
| 
 | |
| [repo]
 | |
| repo_url=git@github.com:zulip/zulip.git
 | |
| [aws]
 | |
| zone_id=Z2U988IEXAMPLE
 | |
| security_groups=sg-01234567
 | |
| instance_type=m4.large
 | |
| EOF
 | |
| }
 | |
| 
 | |
| args="$(getopt -o '' --long help,branch:,roles:,debug-key: -n "$0" -- "$@")" || {
 | |
|     usage >&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)
 | |
| read -r -a SECURITY_GROUPS <<<"$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
 | |
| 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" <<EOF
 | |
| {
 | |
|     "Comment": "Add the $HOSTNAME CNAME record",
 | |
|     "Changes": [
 | |
|         {
 | |
|             "Action": "CREATE",
 | |
|             "ResourceRecordSet": {
 | |
|                 "Name": "$HOSTNAME",
 | |
|                 "Type": "CNAME",
 | |
|                 "TTL": 300,
 | |
|                 "ResourceRecords": [{"Value": "$PUBLIC_DNS_NAME"}]
 | |
|             }
 | |
|         }
 | |
|     ]
 | |
| }
 | |
| EOF
 | |
| $AWS route53 change-resource-record-sets --hosted-zone-id "$AWS_ZONE_ID" --change-batch "file://$ROUTE53_CHANGES"
 | |
| rm "$ROUTE53_CHANGES"
 | |
| 
 | |
| VOLUME_ID=$(echo "$INSTANCE_DATA" | jq -r .Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId)
 | |
| $AWS ec2 create-tags --resources "$VOLUME_ID" --tags "Key=Name,Value=$SERVER root"
 | |
| 
 | |
| set +x
 | |
| echo
 | |
| echo
 | |
| echo ">>> Install started successfully!  Provisioning takes 5-6min."
 | |
| echo "    sleep 360 && tsh ssh root@$HOSTNAME"
 |