solana/net/net.sh

495 lines
12 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
set -e
here=$(dirname "$0")
SOLANA_ROOT="$(cd "$here"/..; pwd)"
# shellcheck source=net/common.sh
source "$here"/common.sh
usage() {
exitcode=0
if [[ -n "$1" ]]; then
exitcode=1
echo "Error: $*"
fi
cat <<EOF
2018-09-04 15:16:25 -07:00
usage: $0 [start|stop|restart|sanity] [command-specific options]
2018-09-03 21:15:55 -07:00
Operate a configured testnet
2018-09-04 09:21:03 -07:00
start - Start the network
sanity - Sanity check the network
stop - Stop the network
restart - Shortcut for stop then start
update - Live update all network nodes
logs - Fetch remote logs from each network node
2018-09-03 21:15:55 -07:00
start/update-specific options:
2018-11-07 13:32:48 -08:00
-T [tarFilename] - Deploy the specified release tarball
2018-11-06 15:02:55 -08:00
-t edge|beta|stable|vX.Y.Z - Deploy the latest tarball release for the
specified release channel (edge|beta|stable) or release tag
(vX.Y.Z)
-f [cargoFeatures] - List of |cargo --feaures=| to activate
(ignored if -s or -S is specified)
-r - Reuse existing node/ledger configuration from a
2019-02-16 10:01:27 -08:00
previous |start| (ie, don't run ./multinode-demo/setup.sh).
-D /path/to/programs - Deploy custom programs from this location
2018-09-03 21:15:55 -07:00
sanity/start/update-specific options:
2018-09-03 22:33:40 -07:00
-o noLedgerVerify - Skip ledger verification
-o noValidatorSanity - Skip fullnode sanity
-o rejectExtraNodes - Require the exact number of nodes
2018-09-03 22:33:40 -07:00
2018-09-03 21:15:55 -07:00
stop-specific options:
none
logs-specific options:
none
Note: if RUST_LOG is set in the environment it will be propogated into the
network nodes.
EOF
exit $exitcode
}
releaseChannel=
2018-09-03 21:15:55 -07:00
deployMethod=local
2018-09-03 22:33:40 -07:00
sanityExtraArgs=
2018-09-04 23:01:07 -07:00
cargoFeatures=
skipSetup=false
updateNodes=false
customPrograms=
2018-09-03 21:15:55 -07:00
command=$1
[[ -n $command ]] || usage
shift
2019-03-02 17:08:46 -08:00
while getopts "h?T:t:o:f:r:D:" opt; do
case $opt in
h | \?)
usage
;;
2018-11-07 13:32:48 -08:00
T)
tarballFilename=$OPTARG
2019-03-02 17:08:46 -08:00
[[ -r $tarballFilename ]] || usage "File not readable: $tarballFilename"
2018-11-07 13:32:48 -08:00
deployMethod=tar
;;
t)
case $OPTARG in
2018-11-06 15:02:55 -08:00
edge|beta|stable|v*)
releaseChannel=$OPTARG
deployMethod=tar
;;
*)
usage "Invalid release channel: $OPTARG"
;;
esac
;;
2018-09-04 23:01:07 -07:00
f)
cargoFeatures=$OPTARG
;;
r)
skipSetup=true
;;
D)
customPrograms=$OPTARG
;;
2018-09-03 22:33:40 -07:00
o)
case $OPTARG in
noLedgerVerify|noValidatorSanity|rejectExtraNodes)
2018-09-03 22:33:40 -07:00
sanityExtraArgs="$sanityExtraArgs -o $OPTARG"
;;
*)
echo "Error: unknown option: $OPTARG"
exit 1
;;
esac
;;
*)
usage "Error: unhandled option: $opt"
;;
esac
done
loadConfigFile
build() {
declare MAYBE_DOCKER=
if [[ $(uname) != Linux ]]; then
source ci/rust-version.sh
MAYBE_DOCKER="ci/docker-run.sh +$rust_stable_docker_image"
fi
SECONDS=0
(
cd "$SOLANA_ROOT"
2018-09-04 09:21:03 -07:00
echo "--- Build started at $(date)"
set -x
rm -rf farf
2018-11-14 19:19:27 -08:00
if [[ -r target/perf-libs/env.sh ]]; then
# shellcheck source=/dev/null
source target/perf-libs/env.sh
fi
2018-11-16 08:04:46 -08:00
$MAYBE_DOCKER bash -c "
set -ex
scripts/cargo-install-all.sh farf \"$cargoFeatures\"
if [[ -n \"$customPrograms\" ]]; then
scripts/cargo-install-custom-programs.sh farf $customPrograms
fi
2018-11-16 08:04:46 -08:00
"
)
echo "Build took $SECONDS seconds"
}
2018-09-07 08:49:22 -07:00
startCommon() {
declare ipAddress=$1
2018-09-04 15:16:25 -07:00
test -d "$SOLANA_ROOT"
2019-01-08 22:11:31 -08:00
if $skipSetup; then
ssh "${sshOptions[@]}" "$ipAddress" "
set -x;
mkdir -p ~/solana/config{,-local}
rm -rf ~/config{,-local};
mv ~/solana/config{,-local} ~;
rm -rf ~/solana;
mkdir -p ~/solana ~/.cargo/bin;
mv ~/config{,-local} ~/solana/
"
else
ssh "${sshOptions[@]}" "$ipAddress" "
set -x;
rm -rf ~/solana;
mkdir -p ~/.cargo/bin
"
fi
[[ -z "$externalNodeSshKey" ]] || ssh-copy-id -f -i "$externalNodeSshKey" "${sshOptions[@]}" "solana@$ipAddress"
2018-09-04 15:16:25 -07:00
rsync -vPrc -e "ssh ${sshOptions[*]}" \
"$SOLANA_ROOT"/{fetch-perf-libs.sh,scripts,net,multinode-demo} \
"$ipAddress":~/solana/
}
startBootstrapLeader() {
declare ipAddress=$1
declare logFile="$2"
echo "--- Starting bootstrap leader: $ipAddress"
2018-09-08 13:48:17 -07:00
echo "start log: $logFile"
# Deploy local binaries to bootstrap fullnode. Other fullnodes and clients later fetch the
# binaries from it
(
set -x
2018-09-07 08:49:22 -07:00
startCommon "$ipAddress" || exit 1
2018-09-03 21:15:55 -07:00
case $deployMethod in
tar)
rsync -vPrc -e "ssh ${sshOptions[*]}" "$SOLANA_ROOT"/solana-release/bin/* "$ipAddress:~/.cargo/bin/"
;;
2018-09-03 21:15:55 -07:00
local)
2018-09-04 15:16:25 -07:00
rsync -vPrc -e "ssh ${sshOptions[*]}" "$SOLANA_ROOT"/farf/bin/* "$ipAddress:~/.cargo/bin/"
2018-09-03 21:15:55 -07:00
;;
*)
usage "Internal error: invalid deployMethod: $deployMethod"
;;
esac
2018-09-04 09:21:03 -07:00
ssh "${sshOptions[@]}" -n "$ipAddress" \
"./solana/net/remote/remote-node.sh \
$deployMethod \
bootstrap-leader \
$publicNetwork \
$entrypointIp \
2019-04-09 16:20:44 -07:00
$((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \
\"$RUST_LOG\" \
$skipSetup \
2018-12-05 17:33:32 -08:00
$leaderRotation \
"
2018-09-04 15:16:25 -07:00
) >> "$logFile" 2>&1 || {
cat "$logFile"
echo "^^^ +++"
exit 1
}
}
startNode() {
declare ipAddress=$1
declare nodeType=$2
declare logFile="$netLogDir/fullnode-$ipAddress.log"
echo "--- Starting $nodeType: $ipAddress"
2018-09-08 13:48:17 -07:00
echo "start log: $logFile"
(
set -x
2018-09-07 08:49:22 -07:00
startCommon "$ipAddress"
2018-09-04 09:21:03 -07:00
ssh "${sshOptions[@]}" -n "$ipAddress" \
"./solana/net/remote/remote-node.sh \
$deployMethod \
$nodeType \
$publicNetwork \
$entrypointIp \
2019-04-09 16:20:44 -07:00
$((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \
\"$RUST_LOG\" \
$skipSetup \
2018-12-05 17:33:32 -08:00
$leaderRotation \
"
2018-09-08 13:48:17 -07:00
) >> "$logFile" 2>&1 &
2018-09-04 09:21:03 -07:00
declare pid=$!
ln -sfT "fullnode-$ipAddress.log" "$netLogDir/fullnode-$pid.log"
2018-09-04 09:21:03 -07:00
pids+=("$pid")
}
startClient() {
declare ipAddress=$1
declare logFile="$2"
2018-09-04 15:16:25 -07:00
echo "--- Starting client: $ipAddress"
2018-09-08 13:48:17 -07:00
echo "start log: $logFile"
2018-09-03 22:33:40 -07:00
(
set -x
2018-09-07 08:49:22 -07:00
startCommon "$ipAddress"
2018-09-03 22:33:40 -07:00
ssh "${sshOptions[@]}" -f "$ipAddress" \
"./solana/net/remote/remote-client.sh $deployMethod $entrypointIp \"$RUST_LOG\""
2018-09-04 15:16:25 -07:00
) >> "$logFile" 2>&1 || {
cat "$logFile"
echo "^^^ +++"
exit 1
}
}
2018-09-03 22:33:40 -07:00
sanity() {
2018-09-06 13:00:01 -07:00
declare ok=true
2018-09-04 09:21:03 -07:00
echo "--- Sanity"
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-sanity-begin=1"
2019-04-09 16:20:44 -07:00
declare bootstrapLeader=${fullnodeIpList[0]}
declare blockstreamer=${blockstreamerIpList[0]}
2018-09-03 22:33:40 -07:00
(
set -x
2019-04-09 16:20:44 -07:00
echo "--- Sanity: $bootstrapLeader"
2018-09-04 14:36:35 -07:00
# shellcheck disable=SC2029 # remote-client.sh args are expanded on client side intentionally
2019-04-09 16:20:44 -07:00
ssh "${sshOptions[@]}" "$bootstrapLeader" \
"./solana/net/remote/remote-sanity.sh $sanityExtraArgs \"$RUST_LOG\""
2019-04-09 16:20:44 -07:00
# If there's a blockstreamer node run a reduced sanity check on it as well
if [[ -n $blockstreamer ]]; then
echo "--- Sanity: $blockstreamer"
# shellcheck disable=SC2029 # remote-client.sh args are expanded on client side intentionally
ssh "${sshOptions[@]}" "$blockstreamer" \
"./solana/net/remote/remote-sanity.sh $sanityExtraArgs -o noLedgerVerify -o noValidatorSanity \"$RUST_LOG\""
fi
2018-09-06 13:00:01 -07:00
) || ok=false
$metricsWriteDatapoint "testnet-deploy net-sanity-complete=1"
$ok || exit 1
2018-09-03 22:33:40 -07:00
}
2018-09-03 21:15:55 -07:00
2018-09-03 22:33:40 -07:00
start() {
2018-09-03 21:15:55 -07:00
case $deployMethod in
tar)
if [[ -n $releaseChannel ]]; then
rm -f "$SOLANA_ROOT"/solana-release.tar.bz2
(
set -x
curl -o "$SOLANA_ROOT"/solana-release.tar.bz2 \
http://solana-release.s3.amazonaws.com/"$releaseChannel"/solana-release-x86_64-unknown-linux-gnu.tar.bz2
)
tarballFilename="$SOLANA_ROOT"/solana-release.tar.bz2
fi
(
set -x
rm -rf "$SOLANA_ROOT"/solana-release
(cd "$SOLANA_ROOT"; tar jxv) < "$tarballFilename"
cat "$SOLANA_ROOT"/solana-release/version.yml
)
;;
2018-09-03 21:15:55 -07:00
local)
build
;;
*)
usage "Internal error: invalid deployMethod: $deployMethod"
;;
esac
echo "Deployment started at $(date)"
if $updateNodes; then
$metricsWriteDatapoint "testnet-deploy net-update-begin=1"
else
$metricsWriteDatapoint "testnet-deploy net-start-begin=1"
fi
declare bootstrapLeader=true
declare nodeType=fullnode
for ipAddress in "${fullnodeIpList[@]}" - "${blockstreamerIpList[@]}"; do
if [[ $ipAddress = - ]]; then
nodeType=blockstreamer
continue
fi
if $updateNodes; then
stopNode "$ipAddress"
fi
if $bootstrapLeader; then
SECONDS=0
declare bootstrapNodeDeployTime=
startBootstrapLeader "$ipAddress" "$netLogDir/bootstrap-leader-$ipAddress.log"
bootstrapNodeDeployTime=$SECONDS
$metricsWriteDatapoint "testnet-deploy net-bootnode-leader-started=1"
bootstrapLeader=false
SECONDS=0
pids=()
loopCount=0
else
startNode "$ipAddress" $nodeType
# Stagger additional node start time. If too many nodes start simultaneously
# the bootstrap node gets more rsync requests from the additional nodes than
# it can handle.
((loopCount++ % 2 == 0)) && sleep 2
fi
2018-09-04 09:21:03 -07:00
done
for pid in "${pids[@]}"; do
declare ok=true
wait "$pid" || ok=false
if ! $ok; then
cat "$netLogDir/fullnode-$pid.log"
2018-09-04 09:21:03 -07:00
echo ^^^ +++
exit 1
fi
done
2018-09-04 09:21:03 -07:00
$metricsWriteDatapoint "testnet-deploy net-fullnodes-started=1"
additionalNodeDeployTime=$SECONDS
if $updateNodes; then
for ipAddress in "${clientIpList[@]}"; do
stopNode "$ipAddress"
done
fi
sanity
2018-09-03 22:33:40 -07:00
2018-09-03 21:15:55 -07:00
SECONDS=0
for ipAddress in "${clientIpList[@]}"; do
startClient "$ipAddress" "$netLogDir/client-$ipAddress.log"
done
clientDeployTime=$SECONDS
if $updateNodes; then
$metricsWriteDatapoint "testnet-deploy net-update-complete=1"
else
$metricsWriteDatapoint "testnet-deploy net-start-complete=1"
fi
declare networkVersion=unknown
case $deployMethod in
tar)
networkVersion="$(
(
set -o pipefail
grep "^version: " "$SOLANA_ROOT"/solana-release/version.yml | head -n1 | cut -d\ -f2
) || echo "tar-unknown"
)"
;;
local)
networkVersion="$(git rev-parse HEAD || echo local-unknown)"
;;
*)
usage "Internal error: invalid deployMethod: $deployMethod"
;;
esac
$metricsWriteDatapoint "testnet-deploy version=\"${networkVersion:0:9}\""
2018-09-03 21:15:55 -07:00
echo
2018-09-08 14:12:32 -07:00
echo "+++ Deployment Successful"
echo "Bootstrap leader deployment took $bootstrapNodeDeployTime seconds"
echo "Additional fullnode deployment (${#fullnodeIpList[@]} full nodes, ${#blockstreamerIpList[@]} blockstreamer nodes) took $additionalNodeDeployTime seconds"
echo "Client deployment (${#clientIpList[@]} instances) took $clientDeployTime seconds"
echo "Network start logs in $netLogDir:"
ls -l "$netLogDir"
}
2018-09-07 08:49:22 -07:00
stopNode() {
local ipAddress=$1
2018-09-04 09:21:03 -07:00
echo "--- Stopping node: $ipAddress"
(
set -x
2019-01-09 21:06:58 -08:00
# shellcheck disable=SC2029 # It's desired that PS4 be expanded on the client side
ssh "${sshOptions[@]}" "$ipAddress" "
PS4=\"$PS4\"
2018-09-07 08:34:42 -07:00
set -x
! tmux list-sessions || tmux kill-session
for pid in solana/{net-stats,oom-monitor}.pid; do
pgid=\$(ps opgid= \$(cat \$pid) | tr -d '[:space:]')
sudo kill -- -\$pgid
done
for pattern in node solana- remote-; do
2018-09-07 08:34:42 -07:00
pkill -9 \$pattern
done
"
) || true
}
stop() {
SECONDS=0
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-stop-begin=1"
2018-09-03 21:15:55 -07:00
for ipAddress in "${fullnodeIpList[@]}" "${blockstreamerIpList[@]}" "${clientIpList[@]}"; do
2018-09-07 08:49:22 -07:00
stopNode "$ipAddress"
done
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-stop-complete=1"
echo "Stopping nodes took $SECONDS seconds"
}
2018-09-03 22:33:40 -07:00
case $command in
2018-09-04 09:21:03 -07:00
restart)
2018-09-03 22:33:40 -07:00
stop
start
;;
2018-09-04 09:21:03 -07:00
start)
start
;;
update)
$leaderRotation || {
echo Warning: unable to update because leader rotation is disabled
exit 1
}
skipSetup=true
updateNodes=true
start
;;
2018-09-03 22:33:40 -07:00
sanity)
sanity
;;
stop)
stop
;;
logs)
fetchRemoteLog() {
declare ipAddress=$1
declare log=$2
echo "--- fetching $log from $ipAddress"
(
set -x
timeout 30s scp "${sshOptions[@]}" \
2018-12-23 10:33:40 -08:00
"$ipAddress":solana/"$log".log "$netLogDir"/remote-"$log"-"$ipAddress".log
) || echo "failed to fetch log"
}
fetchRemoteLog "${fullnodeIpList[0]}" drone
for ipAddress in "${fullnodeIpList[@]}"; do
fetchRemoteLog "$ipAddress" fullnode
done
for ipAddress in "${clientIpList[@]}"; do
fetchRemoteLog "$ipAddress" client
done
for ipAddress in "${blockstreamerIpList[@]}"; do
fetchRemoteLog "$ipAddress" fullnode
done
;;
2018-09-03 22:33:40 -07:00
*)
echo "Internal error: Unknown command: $command"
exit 1
esac