| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993 |
- #!/usr/bin/env bash
- set -e
- here=$(dirname "$0")
- # shellcheck source=net/common.sh
- source "$here"/common.sh
- cloudProvider=$(basename "$0" .sh)
- bootDiskType=""
- case $cloudProvider in
- gce)
- # shellcheck source=net/scripts/gce-provider.sh
- source "$here"/scripts/gce-provider.sh
- # use n1 instead of n2 so we don't need to spin up >= 4 local SSD's
- cpuBootstrapLeaderMachineType="--custom-cpu 24 --min-cpu-platform Intel%20Skylake --custom-vm-type n1"
- clientMachineType="--custom-cpu 16 --custom-memory 20GB"
- blockstreamerMachineType="--machine-type n1-standard-8"
- selfDestructHours=8
- ;;
- ec2)
- # shellcheck source=net/scripts/ec2-provider.sh
- source "$here"/scripts/ec2-provider.sh
- cpuBootstrapLeaderMachineType=m5.4xlarge
- clientMachineType=c5.2xlarge
- blockstreamerMachineType=m5.4xlarge
- selfDestructHours=0
- ;;
- azure)
- # shellcheck source=net/scripts/azure-provider.sh
- source "$here"/scripts/azure-provider.sh
- cpuBootstrapLeaderMachineType=Standard_D16s_v3
- clientMachineType=Standard_D16s_v3
- blockstreamerMachineType=Standard_D16s_v3
- selfDestructHours=0
- ;;
- colo)
- # shellcheck source=net/scripts/colo-provider.sh
- source "$here"/scripts/colo-provider.sh
- cpuBootstrapLeaderMachineType=0
- clientMachineType=0
- blockstreamerMachineType=0
- selfDestructHours=0
- ;;
- *)
- echo "Error: Unknown cloud provider: $cloudProvider"
- ;;
- esac
- prefix=testnet-dev-${USER//[^A-Za-z0-9]/}
- additionalValidatorCount=2
- clientNodeCount=0
- blockstreamer=false
- validatorBootDiskSizeInGb=100
- clientBootDiskSizeInGb=75
- validatorAdditionalDiskSizeInGb=
- externalNodes=false
- failOnValidatorBootupFailure=true
- preemptible=true
- evalInfo=false
- tmpfsAccounts=false
- defaultCustomMemoryGB="$(cloud_DefaultCustomMemoryGB)"
- customMemoryGB="$defaultCustomMemoryGB"
- publicNetwork=false
- letsEncryptDomainName=
- customMachineType=
- customAddress=
- zones=()
- containsZone() {
- local e match="$1"
- shift
- for e; do [[ "$e" == "$match" ]] && return 0; done
- return 1
- }
- usage() {
- exitcode=0
- if [[ -n "$1" ]]; then
- exitcode=1
- echo "Error: $*"
- fi
- cat <<EOF
- usage: $0 [create|config|delete] [common options] [command-specific options]
- Manage testnet instances
- create - create a new testnet (implies 'config')
- config - configure the testnet and write a config file describing it
- delete - delete the testnet
- info - display information about the currently configured testnet
- status - display status information of all resources
- common options:
- -p [prefix] - Optional common prefix for instance names to avoid
- collisions (default: $prefix)
- -z [zone] - Zone(s) for the nodes (default: $(cloud_DefaultZone))
- If specified multiple times, the validators will be evenly
- distributed over all specified zones and
- client/blockstreamer nodes will be created in the first
- zone
- -x - append to the existing configuration instead of creating a
- new configuration
- --allow-boot-failures
- - Discard from config validator nodes that didn't bootup
- successfully
- create-specific options:
- -n [number] - Number of additional validators (default: $additionalValidatorCount)
- -c [number] - Number of client nodes (default: $clientNodeCount)
- -u - Include a Blockstreamer (default: $blockstreamer)
- -P - Use public network IP addresses (default: $publicNetwork)
- -a [address] - Address to be be assigned to the Blockstreamer if present,
- otherwise the bootstrap validator.
- * For GCE, [address] is the "name" of the desired External
- IP Address.
- * For EC2, [address] is the "allocation ID" of the desired
- Elastic IP.
- -d [disk-type] - Specify a boot disk type (default None) Use pd-ssd to get ssd on GCE.
- --letsencrypt [dns name]
- - Attempt to generate a TLS certificate using this
- DNS name (useful only when the -a and -P options
- are also provided)
- --custom-machine-type [type]
- - Set a custom machine type.
- $(
- if [[ -n "$defaultCustomMemoryGB" ]]; then
- echo " --custom-memory-gb"
- echo " - Set memory size for custom machine type in GB (default: $defaultCustomMemoryGB)"
- fi
- )
- --validator-additional-disk-size-gb [number]
- - Add an additional [number] GB SSD to all validators to store the config directory.
- If not set, config will be written to the boot disk by default.
- Only supported on GCE.
- --dedicated - Use dedicated instances for additional validators
- (by default preemptible instances are used to reduce
- cost). Note that the bootstrap validator,
- blockstreamer and client nodes are always dedicated.
- Set this flag on colo to prevent your testnet from being pre-empted by nightly test automation.
- --self-destruct-hours [number]
- - Specify lifetime of the allocated instances in hours. 0 to
- disable. Only supported on GCE. (default: $selfDestructHours)
- --validator-boot-disk-size-gb [number]
- - Specify validator boot disk size in gb.
- --client-machine-type [type]
- - custom client machine type
- --tmpfs-accounts - Put accounts directory on a swap-backed tmpfs volume
- config-specific options:
- -P - Use public network IP addresses (default: $publicNetwork)
- delete-specific options:
- --reclaim-preemptible-reservations
- - If set, reclaims all reservations on colo nodes that were not created with --dedicated.
- This behavior does not filter by testnet name or owner. Only implemented on colo.
- --reclaim-all-reservations
- - If set, reclaims all reservations on all colo nodes, regardless of owner, pre-emptibility, or creator.
- info-specific options:
- --eval - Output in a form that can be eval-ed by a shell: eval \$(gce.sh info --eval)
- none
- EOF
- exit $exitcode
- }
- command=$1
- [[ -n $command ]] || usage
- shift
- [[ $command = create || $command = config || $command = info || $command = delete || $command = status ]] ||
- usage "Invalid command: $command"
- shortArgs=()
- while [[ -n $1 ]]; do
- if [[ ${1:0:2} = -- ]]; then
- if [[ $1 = --letsencrypt ]]; then
- letsEncryptDomainName="$2"
- shift 2
- elif [[ $1 = --validator-additional-disk-size-gb ]]; then
- validatorAdditionalDiskSizeInGb="$2"
- shift 2
- elif [[ $1 == --machine-type* || $1 == --custom-cpu* ]]; then # Bypass quoted long args
- shortArgs+=("$1")
- shift
- elif [[ $1 == --allow-boot-failures ]]; then
- failOnValidatorBootupFailure=false
- shift
- elif [[ $1 == --dedicated ]]; then
- preemptible=false
- shift
- elif [[ $1 == --eval ]]; then
- evalInfo=true
- shift
- elif [[ $1 == --enable-gpu ]]; then
- echo "GPU support has been dropped, --enable-gpu is a noop"
- shift
- elif [[ $1 = --custom-machine-type ]]; then
- customMachineType="$2"
- shift 2
- elif [[ $1 = --client-machine-type ]]; then
- clientMachineType="$2"
- shift 2
- elif [[ $1 = --validator-boot-disk-size-gb ]]; then
- validatorBootDiskSizeInGb="$2"
- shift 2
- elif [[ $1 == --self-destruct-hours ]]; then
- maybeTimeout=$2
- if [[ $maybeTimeout =~ ^[0-9]+$ ]]; then
- selfDestructHours=$maybeTimeout
- else
- echo " Invalid parameter ($maybeTimeout) to $1"
- usage 1
- fi
- shift 2
- elif [[ $1 == --reclaim-preemptible-reservations ]]; then
- reclaimOnlyPreemptibleReservations=true
- shift
- elif [[ $1 == --reclaim-all-reservations ]]; then
- reclaimAllReservations=true
- shift
- elif [[ $1 == --tmpfs-accounts ]]; then
- tmpfsAccounts=true
- shift
- elif [[ $1 == --custom-memory-gb ]]; then
- customMemoryGB=$2
- shift 2
- else
- usage "Unknown long option: $1"
- fi
- else
- shortArgs+=("$1")
- shift
- fi
- done
- while getopts "h?p:Pn:c:r:z:gG:a:d:uxf" opt "${shortArgs[@]}"; do
- case $opt in
- h | \?)
- usage
- ;;
- p)
- [[ ${OPTARG//[^A-Za-z0-9-]/} == "$OPTARG" ]] || usage "Invalid prefix: \"$OPTARG\", alphanumeric only"
- prefix=$OPTARG
- ;;
- P)
- publicNetwork=true
- ;;
- n)
- additionalValidatorCount=$OPTARG
- ;;
- c)
- clientNodeCount=$OPTARG
- ;;
- z)
- containsZone "$OPTARG" "${zones[@]}" || zones+=("$OPTARG")
- ;;
- g)
- echo "GPU support has been dropped, -g argument is a noop"
- ;;
- G)
- echo "GPU support has been dropped, -G argument is a noop"
- ;;
- a)
- customAddress=$OPTARG
- ;;
- d)
- bootDiskType=$OPTARG
- ;;
- u)
- blockstreamer=true
- ;;
- x)
- externalNodes=true
- ;;
- *)
- usage "unhandled option: $opt"
- ;;
- esac
- done
- [[ ${#zones[@]} -gt 0 ]] || zones+=("$(cloud_DefaultZone)")
- [[ -z $1 ]] || usage "Unexpected argument: $1"
- if [[ $cloudProvider = ec2 ]]; then
- # EC2 keys can't be retrieved from running instances like GCE keys can so save
- # EC2 keys in the user's home directory so |./ec2.sh config| can at least be
- # used on the same host that ran |./ec2.sh create| .
- sshPrivateKey="$HOME/.ssh/solana-net-id_$prefix"
- else
- sshPrivateKey="$netConfigDir/id_$prefix"
- fi
- case $cloudProvider in
- gce)
- if [[ "$tmpfsAccounts" = "true" ]]; then
- cpuBootstrapLeaderMachineType+=" --local-ssd interface=nvme"
- if [[ $customMemoryGB -lt 100 ]]; then
- # shellcheck disable=SC2016 # We don't want expression expansion on these backticks
- echo -e '\nWarning: At least 100GB of system RAM is recommending with `--tmpfs-accounts` (see `--custom-memory-gb`)\n'
- fi
- fi
- cpuBootstrapLeaderMachineType+=" --custom-memory ${customMemoryGB}GB"
- ;;
- ec2|azure|colo)
- if [[ -n $validatorAdditionalDiskSizeInGb ]] ; then
- usage "--validator-additional-disk-size-gb currently only supported with cloud provider: gce"
- fi
- if [[ "$tmpfsAccounts" = "true" ]]; then
- usage "--tmpfs-accounts only supported on cloud provider: gce"
- fi
- if [[ "$customMemoryGB" != "$defaultCustomMemoryGB" ]]; then
- usage "--custom-memory-gb only supported on cloud provider: gce"
- fi
- ;;
- *)
- echo "Error: Unknown cloud provider: $cloudProvider"
- ;;
- esac
- case $cloudProvider in
- gce | ec2 | azure)
- maybePreemptible="never preemptible"
- ;;
- colo)
- maybePreemptible=$preemptible
- ;;
- *)
- echo "Error: Unknown cloud provider: $cloudProvider"
- ;;
- esac
- if [[ $reclaimOnlyPreemptibleReservations == "true" && $reclaimAllReservations == "true" ]]; then
- usage "Cannot set both --reclaim-preemptible-reservations and --reclaim-all-reservations. Set one or none"
- fi
- if [[ -n $reclaimAllReservations || -n $reclaimOnlyPreemptibleReservations ]]; then
- forceDelete="true"
- fi
- if [[ -n "$customMachineType" ]] ; then
- bootstrapLeaderMachineType="$customMachineType"
- else
- bootstrapLeaderMachineType="$cpuBootstrapLeaderMachineType"
- fi
- validatorMachineType=$bootstrapLeaderMachineType
- blockstreamerMachineType=$bootstrapLeaderMachineType
- # cloud_ForEachInstance [cmd] [extra args to cmd]
- #
- # Execute a command for each element in the `instances` array
- #
- # cmd - The command to execute on each instance
- # The command will receive arguments followed by any
- # additional arguments supplied to cloud_ForEachInstance:
- # name - name of the instance
- # publicIp - The public IP address of this instance
- # privateIp - The private IP address of this instance
- # zone - Zone of this instance
- # count - Monotonically increasing count for each
- # invocation of cmd, starting at 1
- # ... - Extra args to cmd..
- #
- #
- cloud_ForEachInstance() {
- declare cmd="$1"
- shift
- [[ -n $cmd ]] || { echo cloud_ForEachInstance: cmd not specified; exit 1; }
- declare count=1
- for info in "${instances[@]}"; do
- declare name publicIp privateIp
- IFS=: read -r name publicIp privateIp zone < <(echo "$info")
- # shellcheck disable=SC2294
- eval "$cmd" "$name" "$publicIp" "$privateIp" "$zone" "$count" "$@"
- count=$((count + 1))
- done
- }
- # Given a cloud provider zone, return an approximate lat,long location for the
- # data center. Normal geoip lookups for cloud provider IP addresses are
- # sometimes widely inaccurate.
- zoneLocation() {
- declare zone="$1"
- case "$zone" in
- us-west1*)
- echo "[45.5946, -121.1787]"
- ;;
- us-central1*)
- echo "[41.2619, -95.8608]"
- ;;
- us-east1*)
- echo "[33.1960, -80.0131]"
- ;;
- asia-east2*)
- echo "[22.3193, 114.1694]"
- ;;
- asia-northeast1*)
- echo "[35.6762, 139.6503]"
- ;;
- asia-northeast2*)
- echo "[34.6937, 135.5023]"
- ;;
- asia-south1*)
- echo "[19.0760, 72.8777]"
- ;;
- asia-southeast1*)
- echo "[1.3404, 103.7090]"
- ;;
- australia-southeast1*)
- echo "[-33.8688, 151.2093]"
- ;;
- europe-north1*)
- echo "[60.5693, 27.1878]"
- ;;
- europe-west2*)
- echo "[51.5074, -0.1278]"
- ;;
- europe-west3*)
- echo "[50.1109, 8.6821]"
- ;;
- europe-west4*)
- echo "[53.4386, 6.8355]"
- ;;
- europe-west6*)
- echo "[47.3769, 8.5417]"
- ;;
- northamerica-northeast1*)
- echo "[45.5017, -73.5673]"
- ;;
- southamerica-east1*)
- echo "[-23.5505, -46.6333]"
- ;;
- *)
- ;;
- esac
- }
- prepareInstancesAndWriteConfigFile() {
- $metricsWriteDatapoint "testnet-deploy net-config-begin=1"
- if $externalNodes; then
- echo "Appending to existing config file"
- echo "externalNodeSshKey=$sshPrivateKey" >> "$configFile"
- else
- rm -f "$geoipConfigFile"
- cat >> "$configFile" <<EOF
- # autogenerated at $(date)
- netBasename=$prefix
- publicNetwork=$publicNetwork
- sshPrivateKey=$sshPrivateKey
- letsEncryptDomainName=$letsEncryptDomainName
- export TMPFS_ACCOUNTS=$tmpfsAccounts
- EOF
- fi
- touch "$geoipConfigFile"
- buildSshOptions
- cloud_RestartPreemptedInstances "$prefix"
- fetchPrivateKey() {
- declare nodeName
- declare nodeIp
- declare nodeZone
- IFS=: read -r nodeName nodeIp _ nodeZone < <(echo "${instances[0]}")
- # Make sure the machine is alive or pingable
- timeout_sec=90
- cloud_WaitForInstanceReady "$nodeName" "$nodeIp" "$nodeZone" "$timeout_sec"
- if [[ ! -r $sshPrivateKey ]]; then
- echo "Fetching $sshPrivateKey from $nodeName"
- # Try to scp in a couple times, sshd may not yet be up even though the
- # machine can be pinged...
- (
- set -o pipefail
- for i in $(seq 1 60); do
- set -x
- cloud_FetchFile "$nodeName" "$nodeIp" /solana-scratch/id_ecdsa "$sshPrivateKey" "$nodeZone" &&
- cloud_FetchFile "$nodeName" "$nodeIp" /solana-scratch/id_ecdsa.pub "$sshPrivateKey.pub" "$nodeZone" &&
- break
- set +x
- sleep 1
- echo "Retry $i..."
- done
- )
- chmod 400 "$sshPrivateKey"
- ls -l "$sshPrivateKey"
- fi
- }
- recordInstanceIp() {
- declare name="$1"
- declare publicIp="$2"
- declare privateIp="$3"
- declare zone="$4"
- #declare index="$5"
- declare failOnFailure="$6"
- declare arrayName="$7"
- if [ "$publicIp" = "TERMINATED" ] || [ "$privateIp" = "TERMINATED" ]; then
- if $failOnFailure; then
- exit 1
- else
- return 0
- fi
- fi
- ok=true
- echo "Waiting for $name to finish booting..."
- (
- set +e
- fetchPrivateKey || exit 1
- for i in $(seq 1 60); do
- (
- set -x
- timeout --preserve-status --foreground 20s ssh "${sshOptions[@]}" "$publicIp" "ls -l /solana-scratch/.instance-startup-complete"
- )
- ret=$?
- if [[ $ret -eq 0 ]]; then
- echo "$name has booted."
- exit 0
- fi
- sleep 5
- echo "Retry $i..."
- done
- echo "$name failed to boot."
- exit 1
- ) || ok=false
- if ! $ok; then
- if $failOnFailure; then
- exit 1
- fi
- else
- {
- echo "$arrayName+=($publicIp) # $name"
- echo "${arrayName}Private+=($privateIp) # $name"
- echo "${arrayName}Zone+=($zone) # $name"
- } >> "$configFile"
- declare latlng=
- latlng=$(zoneLocation "$zone")
- if [[ -n $latlng ]]; then
- echo "$publicIp: $latlng" >> "$geoipConfigFile"
- fi
- fi
- }
- if $externalNodes; then
- echo "Bootstrap validator is already configured"
- else
- echo "Looking for bootstrap validator instance..."
- cloud_FindInstance "$prefix-bootstrap-validator"
- [[ ${#instances[@]} -eq 1 ]] || {
- echo "Unable to find bootstrap validator"
- exit 1
- }
- echo "validatorIpList=()" >> "$configFile"
- echo "validatorIpListPrivate=()" >> "$configFile"
- cloud_ForEachInstance recordInstanceIp true validatorIpList
- fi
- if [[ $additionalValidatorCount -gt 0 ]]; then
- numZones=${#zones[@]}
- if [[ $additionalValidatorCount -gt $numZones ]]; then
- numNodesPerZone=$((additionalValidatorCount / numZones))
- numLeftOverNodes=$((additionalValidatorCount % numZones))
- else
- numNodesPerZone=1
- numLeftOverNodes=0
- fi
- for ((i=((numZones - 1)); i >= 0; i--)); do
- zone=${zones[i]}
- if [[ $i -eq 0 ]]; then
- numNodesPerZone=$((numNodesPerZone + numLeftOverNodes))
- fi
- echo "Looking for additional validator instances in $zone ..."
- cloud_FindInstances "$prefix-$zone-validator"
- declare numInstances=${#instances[@]}
- if [[ $numInstances -ge $numNodesPerZone || ( ! $failOnValidatorBootupFailure && $numInstances -gt 0 ) ]]; then
- cloud_ForEachInstance recordInstanceIp "$failOnValidatorBootupFailure" validatorIpList
- else
- echo "Unable to find additional validators"
- if $failOnValidatorBootupFailure; then
- exit 1
- fi
- fi
- done
- fi
- if ! $externalNodes; then
- echo "clientIpList=()" >> "$configFile"
- echo "clientIpListPrivate=()" >> "$configFile"
- fi
- echo "Looking for client bencher instances..."
- cloud_FindInstances "$prefix-client"
- [[ ${#instances[@]} -eq 0 ]] || {
- cloud_ForEachInstance recordInstanceIp true clientIpList
- }
- if ! $externalNodes; then
- echo "blockstreamerIpList=()" >> "$configFile"
- echo "blockstreamerIpListPrivate=()" >> "$configFile"
- fi
- echo "Looking for blockstreamer instances..."
- cloud_FindInstances "$prefix-blockstreamer"
- [[ ${#instances[@]} -eq 0 ]] || {
- cloud_ForEachInstance recordInstanceIp true blockstreamerIpList
- }
- echo "Wrote $configFile"
- $metricsWriteDatapoint "testnet-deploy net-config-complete=1"
- }
- delete() {
- $metricsWriteDatapoint "testnet-deploy net-delete-begin=1"
- case $cloudProvider in
- gce | ec2 | azure)
- # Filter for all nodes
- filter="$prefix-"
- ;;
- colo)
- if [[ -n $forceDelete ]]; then
- filter=".*-"
- else
- filter="$prefix-"
- fi
- ;;
- *)
- echo "Error: Unknown cloud provider: $cloudProvider"
- ;;
- esac
- echo "Searching for instances: $filter"
- cloud_FindInstances "$filter" "$reclaimOnlyPreemptibleReservations"
- if [[ ${#instances[@]} -eq 0 ]]; then
- echo "No instances found matching '$filter'"
- else
- cloud_DeleteInstances $forceDelete
- fi
- wait
- if $externalNodes; then
- echo "Let's not delete the current configuration file"
- else
- rm -f "$configFile"
- fi
- $metricsWriteDatapoint "testnet-deploy net-delete-complete=1"
- }
- create_error_cleanup() {
- declare RC=$?
- if [[ "$RC" -ne 0 ]]; then
- delete
- fi
- exit $RC
- }
- case $command in
- delete)
- delete
- ;;
- create)
- [[ -n $additionalValidatorCount ]] || usage "Need number of nodes"
- delete
- $metricsWriteDatapoint "testnet-deploy net-create-begin=1"
- if $failOnValidatorBootupFailure; then
- trap create_error_cleanup EXIT
- fi
- rm -rf "$sshPrivateKey"{,.pub}
- # Note: using rsa because |aws ec2 import-key-pair| seems to fail for ecdsa
- ssh-keygen -t rsa -N '' -f "$sshPrivateKey"
- printNetworkInfo() {
- cat <<EOF
- ==[ Network composition ]===============================================================
- Bootstrap validator = $bootstrapLeaderMachineType
- Additional validators = $additionalValidatorCount x $validatorMachineType
- Client(s) = $clientNodeCount x $clientMachineType
- Blockstreamer = $blockstreamer
- ========================================================================================
- EOF
- }
- printNetworkInfo
- creationDate=$(date)
- creationInfo() {
- cat <<EOF
- Instance running since: $creationDate
- ========================================================================================
- EOF
- }
- declare startupScript="$netConfigDir"/instance-startup-script.sh
- cat > "$startupScript" <<EOF
- #!/usr/bin/env bash
- # autogenerated at $(date)
- set -ex
- if [[ -f /solana-scratch/.instance-startup-complete ]]; then
- echo reboot
- $(
- cd "$here"/scripts/
- if [[ -n $validatorAdditionalDiskSizeInGb ]]; then
- cat mount-additional-disk.sh
- fi
- cat ../../scripts/ulimit-n.sh
- )
- if [[ -x ~solana/solana/on-reboot ]]; then
- sudo -u solana ~solana/solana/on-reboot
- fi
- # Skip most setup on instance reboot
- exit 0
- fi
- cat > /etc/motd <<EOM
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- This instance has not been fully configured.
- See startup script log messages in /var/log/syslog for status:
- $ sudo cat /var/log/syslog | egrep \\(startup-script\\|cloud-init\)
- To block until setup is complete, run:
- $ until [[ -f /solana-scratch/.instance-startup-complete ]]; do sleep 1; done
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- $(creationInfo)
- EOM
- # Place the generated private key at /solana-scratch/id_ecdsa so it's retrievable by anybody
- # who is able to log into this machine
- mkdir -m 0777 /solana-scratch
- cat > /solana-scratch/id_ecdsa <<EOK
- $(cat "$sshPrivateKey")
- EOK
- cat > /solana-scratch/id_ecdsa.pub <<EOK
- $(cat "$sshPrivateKey.pub")
- EOK
- chmod 444 /solana-scratch/id_ecdsa
- USER=\$(id -un)
- export DEBIAN_FRONTEND=noninteractive
- $(
- cd "$here"/scripts/
- cat \
- disable-background-upgrades.sh \
- create-solana-user.sh \
- install-ag.sh \
- install-at.sh \
- install-certbot.sh \
- install-earlyoom.sh \
- install-iftop.sh \
- install-jq.sh \
- install-libssl.sh \
- install-rsync.sh \
- install-perf.sh \
- localtime.sh \
- network-config.sh \
- remove-docker-interface.sh \
- if [[ -n $validatorAdditionalDiskSizeInGb ]]; then
- cat mount-additional-disk.sh
- fi
- if [[ $selfDestructHours -gt 0 ]]; then
- cat <<EOSD
- # Setup GCE self-destruct
- cat >/solana-scratch/gce-self-destruct.sh <<'EOS'
- $(cat gce-self-destruct.sh)
- EOS
- EOSD
- cat <<'EOSD'
- # Populate terminal prompt update script
- cat >/solana-scratch/gce-self-destruct-ps1.sh <<'EOS'
- #!/usr/bin/env bash
- source "$(dirname "$0")/gce-self-destruct.sh"
- gce_self_destruct_ps1
- EOS
- chmod +x /solana-scratch/gce-self-destruct-ps1.sh
- # Append MOTD and PS1 replacement to .profile
- cat >>~solana/.profile <<'EOS'
- # Print self-destruct countdown on login
- source "/solana-scratch/gce-self-destruct.sh"
- gce_self_destruct_motd
- # Add self-destruct countdown to terminal prompt
- export PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]$(/solana-scratch/gce-self-destruct-ps1.sh):\[\033[01;34m\]\w\[\033[00m\]\$ '
- EOS
- EOSD
- cat <<EOSD
- source /solana-scratch/gce-self-destruct.sh
- gce_self_destruct_setup $selfDestructHours
- EOSD
- fi
- )
- cat > /etc/motd <<EOM
- See startup script log messages in /var/log/syslog for status:
- $ sudo cat /var/log/syslog | egrep \\(startup-script\\|cloud-init\)
- $(printNetworkInfo)
- $(creationInfo)
- EOM
- $(
- if [[ "$tmpfsAccounts" = "true" ]]; then
- cat <<'EOSWAP'
- # Setup swap/tmpfs for accounts
- tmpfsMountPoint=/mnt/solana-accounts
- swapDevice="/dev/nvme0n1"
- swapUUID="43076c54-7840-4e59-a368-2d164f8984fb"
- mkswap --uuid "$swapUUID" "$swapDevice"
- echo "UUID=$swapUUID swap swap defaults 0 0" >> /etc/fstab
- swapon "UUID=$swapUUID"
- mkdir -p -m 0777 "$tmpfsMountPoint"
- echo "tmpfs $tmpfsMountPoint tmpfs defaults,size=300G 0 0" >> /etc/fstab
- mount "$tmpfsMountPoint"
- EOSWAP
- fi
- )
- touch /solana-scratch/.instance-startup-complete
- EOF
- if $blockstreamer; then
- blockstreamerAddress=$customAddress
- else
- bootstrapLeaderAddress=$customAddress
- fi
- for zone in "${zones[@]}"; do
- cloud_Initialize "$prefix" "$zone"
- done
- if $externalNodes; then
- echo "Bootstrap validator is already configured"
- else
- cloud_CreateInstances "$prefix" "$prefix-bootstrap-validator" 1 \
- "$bootstrapLeaderMachineType" "${zones[0]}" "$validatorBootDiskSizeInGb" \
- "$startupScript" "$bootstrapLeaderAddress" "$bootDiskType" "$validatorAdditionalDiskSizeInGb" \
- "$maybePreemptible" "$sshPrivateKey"
- fi
- if [[ $additionalValidatorCount -gt 0 ]]; then
- num_zones=${#zones[@]}
- if [[ $additionalValidatorCount -gt $num_zones ]]; then
- numNodesPerZone=$((additionalValidatorCount / num_zones))
- numLeftOverNodes=$((additionalValidatorCount % num_zones))
- else
- numNodesPerZone=1
- numLeftOverNodes=0
- fi
- for ((i=((num_zones - 1)); i >= 0; i--)); do
- zone=${zones[i]}
- if [[ $i -eq 0 ]]; then
- numNodesPerZone=$((numNodesPerZone + numLeftOverNodes))
- fi
- cloud_CreateInstances "$prefix" "$prefix-$zone-validator" "$numNodesPerZone" \
- "$validatorMachineType" "$zone" "$validatorBootDiskSizeInGb" \
- "$startupScript" "" "$bootDiskType" "$validatorAdditionalDiskSizeInGb" \
- "$preemptible" "$sshPrivateKey" &
- done
- wait
- fi
- if [[ $clientNodeCount -gt 0 ]]; then
- cloud_CreateInstances "$prefix" "$prefix-client" "$clientNodeCount" \
- "$clientMachineType" "${zones[0]}" "$clientBootDiskSizeInGb" \
- "$startupScript" "" "$bootDiskType" "" "$maybePreemptible" "$sshPrivateKey"
- fi
- if $blockstreamer; then
- cloud_CreateInstances "$prefix" "$prefix-blockstreamer" "1" \
- "$blockstreamerMachineType" "${zones[0]}" "$validatorBootDiskSizeInGb" \
- "$startupScript" "$blockstreamerAddress" "$bootDiskType" "" "$maybePreemptible" "$sshPrivateKey"
- fi
- $metricsWriteDatapoint "testnet-deploy net-create-complete=1"
- prepareInstancesAndWriteConfigFile
- ;;
- config)
- failOnValidatorBootupFailure=false
- prepareInstancesAndWriteConfigFile
- ;;
- info)
- loadConfigFile
- printNode() {
- declare nodeType=$1
- declare ip=$2
- declare ipPrivate=$3
- declare zone=$4
- printf " %-16s | %-15s | %-15s | %s\n" "$nodeType" "$ip" "$ipPrivate" "$zone"
- }
- if $evalInfo; then
- echo "NET_NUM_VALIDATORS=${#validatorIpList[@]}"
- echo "NET_NUM_CLIENTS=${#clientIpList[@]}"
- echo "NET_NUM_BLOCKSTREAMERS=${#blockstreamerIpList[@]}"
- else
- printNode "Node Type" "Public IP" "Private IP" "Zone"
- echo "-------------------+-----------------+-----------------+--------------"
- fi
- nodeType=bootstrap-validator
- if [[ ${#validatorIpList[@]} -gt 0 ]]; then
- for i in $(seq 0 $(( ${#validatorIpList[@]} - 1)) ); do
- ipAddress=${validatorIpList[$i]}
- ipAddressPrivate=${validatorIpListPrivate[$i]}
- zone=${validatorIpListZone[$i]}
- if $evalInfo; then
- echo "NET_VALIDATOR${i}_IP=$ipAddress"
- else
- printNode $nodeType "$ipAddress" "$ipAddressPrivate" "$zone"
- fi
- nodeType=validator
- done
- fi
- if [[ ${#clientIpList[@]} -gt 0 ]]; then
- for i in $(seq 0 $(( ${#clientIpList[@]} - 1)) ); do
- ipAddress=${clientIpList[$i]}
- ipAddressPrivate=${clientIpListPrivate[$i]}
- zone=${clientIpListZone[$i]}
- if $evalInfo; then
- echo "NET_CLIENT${i}_IP=$ipAddress"
- else
- printNode client "$ipAddress" "$ipAddressPrivate" "$zone"
- fi
- done
- fi
- if [[ ${#blockstreamerIpList[@]} -gt 0 ]]; then
- for i in $(seq 0 $(( ${#blockstreamerIpList[@]} - 1)) ); do
- ipAddress=${blockstreamerIpList[$i]}
- ipAddressPrivate=${blockstreamerIpListPrivate[$i]}
- zone=${blockstreamerIpListZone[$i]}
- if $evalInfo; then
- echo "NET_BLOCKSTREAMER${i}_IP=$ipAddress"
- else
- printNode blockstreamer "$ipAddress" "$ipAddressPrivate" "$zone"
- fi
- done
- fi
- ;;
- status)
- cloud_StatusAll
- ;;
- *)
- usage "Unknown command: $command"
- esac
|