#!/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 cpuBootstrapLeaderMachineType="--custom-cpu 12 --custom-memory 32GB --min-cpu-platform Intel%20Skylake" gpuBootstrapLeaderMachineType="$cpuBootstrapLeaderMachineType --accelerator count=1,type=nvidia-tesla-p100" 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 # NOTE: At this time only the p3dn.24xlarge EC2 instance type has GPU and # AVX-512 support. The default, p2.xlarge, does not support # AVX-512 gpuBootstrapLeaderMachineType=p2.xlarge 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 gpuBootstrapLeaderMachineType=Standard_NC12 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 gpuBootstrapLeaderMachineType=1 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=500 clientBootDiskSizeInGb=75 validatorAdditionalDiskSizeInGb= externalNodes=false failOnValidatorBootupFailure=true preemptible=true evalInfo=false publicNetwork=false letsEncryptDomainName= enableGpu=false 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 <> "$configFile" else rm -f "$geoipConfigFile" cat >> "$configFile" <> "$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 < "$startupScript" < /etc/motd < /solana-scratch/id_ecdsa < /solana-scratch/id_ecdsa.pub </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 < /etc/motd <= 0; i--)); do zone=${zones[i]} if [[ $i -eq 0 ]]; then numNodesPerZone=$((numNodesPerZone + numLeftOverNodes)) fi cloud_CreateInstances "$prefix" "$prefix-$zone-validator" "$numNodesPerZone" \ "$enableGpu" "$validatorMachineType" "$zone" "$validatorBootDiskSizeInGb" \ "$startupScript" "" "$bootDiskType" "$validatorAdditionalDiskSizeInGb" \ "$preemptible" "$sshPrivateKey" & done wait fi if [[ $clientNodeCount -gt 0 ]]; then cloud_CreateInstances "$prefix" "$prefix-client" "$clientNodeCount" \ "$enableGpu" "$clientMachineType" "${zones[0]}" "$clientBootDiskSizeInGb" \ "$startupScript" "" "$bootDiskType" "" "$maybePreemptible" "$sshPrivateKey" fi if $blockstreamer; then cloud_CreateInstances "$prefix" "$prefix-blockstreamer" "1" \ "$enableGpu" "$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