2018-11-11 08:19:04 -08:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
set -e
|
2019-01-13 09:18:50 -08:00
|
|
|
|
2019-01-21 17:59:45 -08:00
|
|
|
skipSetup=false
|
2019-01-13 09:18:50 -08:00
|
|
|
iterations=1
|
2019-01-18 10:09:26 -08:00
|
|
|
restartInterval=never
|
2019-01-21 14:33:46 -08:00
|
|
|
rollingRestart=false
|
2019-01-13 09:18:50 -08:00
|
|
|
maybeNoLeaderRotation=
|
|
|
|
extraNodes=0
|
2019-05-07 11:37:06 -07:00
|
|
|
walletRpcPort=:8899
|
2019-01-13 09:18:50 -08:00
|
|
|
|
|
|
|
usage() {
|
|
|
|
exitcode=0
|
|
|
|
if [[ -n "$1" ]]; then
|
|
|
|
exitcode=1
|
|
|
|
echo "Error: $*"
|
|
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
usage: $0 [options...]
|
|
|
|
|
|
|
|
Start a local cluster and run sanity on it
|
|
|
|
|
|
|
|
options:
|
|
|
|
-i [number] - Number of times to run sanity (default: $iterations)
|
2019-01-18 10:09:26 -08:00
|
|
|
-k [number] - Restart the cluster after this number of sanity iterations (default: $restartInterval)
|
2019-01-21 14:33:46 -08:00
|
|
|
-R - Restart the cluster by incrementially stopping and restarting
|
|
|
|
nodes (at the cadence specified by -k). When disabled all
|
|
|
|
nodes will be first killed then restarted (default: $rollingRestart)
|
2019-01-13 09:18:50 -08:00
|
|
|
-b - Disable leader rotation
|
|
|
|
-x - Add an extra fullnode (may be supplied multiple times)
|
2019-01-16 16:08:18 -08:00
|
|
|
-r - Select the RPC endpoint hosted by a node that starts as
|
|
|
|
a validator node. If unspecified the RPC endpoint hosted by
|
|
|
|
the bootstrap leader will be used.
|
2019-01-21 17:59:45 -08:00
|
|
|
-c - Reuse existing node/ledger configuration from a previous sanity
|
|
|
|
run
|
2019-01-13 09:18:50 -08:00
|
|
|
|
|
|
|
EOF
|
|
|
|
exit $exitcode
|
|
|
|
}
|
2018-07-30 12:51:35 -07:00
|
|
|
|
|
|
|
cd "$(dirname "$0")"/..
|
2019-01-13 09:18:50 -08:00
|
|
|
|
2019-01-21 17:59:45 -08:00
|
|
|
while getopts "ch?i:k:brxR" opt; do
|
2019-01-13 09:18:50 -08:00
|
|
|
case $opt in
|
|
|
|
h | \?)
|
|
|
|
usage
|
|
|
|
;;
|
2019-01-21 17:59:45 -08:00
|
|
|
c)
|
|
|
|
skipSetup=true
|
|
|
|
;;
|
2019-01-13 09:18:50 -08:00
|
|
|
i)
|
|
|
|
iterations=$OPTARG
|
|
|
|
;;
|
2019-01-18 10:09:26 -08:00
|
|
|
k)
|
|
|
|
restartInterval=$OPTARG
|
|
|
|
;;
|
2019-01-13 09:18:50 -08:00
|
|
|
b)
|
2019-04-16 13:03:01 -07:00
|
|
|
maybeNoLeaderRotation="--stake 0"
|
2019-01-13 09:18:50 -08:00
|
|
|
;;
|
|
|
|
x)
|
|
|
|
extraNodes=$((extraNodes + 1))
|
|
|
|
;;
|
2019-01-16 16:08:18 -08:00
|
|
|
r)
|
2019-05-06 07:38:26 -07:00
|
|
|
walletRpcPort=":18899"
|
2019-01-16 16:08:18 -08:00
|
|
|
;;
|
2019-01-21 14:33:46 -08:00
|
|
|
R)
|
|
|
|
rollingRestart=true
|
|
|
|
;;
|
2019-01-13 09:18:50 -08:00
|
|
|
*)
|
|
|
|
usage "Error: unhandled option: $opt"
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
2018-12-13 21:44:50 -08:00
|
|
|
source ci/upload-ci-artifact.sh
|
2018-08-27 10:23:22 -07:00
|
|
|
source scripts/configure-metrics.sh
|
2018-07-30 12:51:35 -07:00
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
nodes=(
|
2019-01-13 09:18:50 -08:00
|
|
|
"multinode-demo/drone.sh"
|
2019-01-22 11:34:27 -08:00
|
|
|
"multinode-demo/bootstrap-leader.sh \
|
2019-03-04 15:53:18 -08:00
|
|
|
--enable-rpc-exit \
|
2019-01-22 11:34:27 -08:00
|
|
|
--init-complete-file init-complete-node1.log"
|
|
|
|
"multinode-demo/fullnode.sh \
|
|
|
|
$maybeNoLeaderRotation \
|
2019-03-04 15:53:18 -08:00
|
|
|
--enable-rpc-exit \
|
2019-01-22 11:34:27 -08:00
|
|
|
--init-complete-file init-complete-node2.log \
|
|
|
|
--rpc-port 18899"
|
2019-01-13 09:18:50 -08:00
|
|
|
)
|
|
|
|
|
2019-01-21 18:40:16 -08:00
|
|
|
for i in $(seq 1 $extraNodes); do
|
2019-01-22 11:34:27 -08:00
|
|
|
nodes+=(
|
|
|
|
"multinode-demo/fullnode.sh \
|
2019-04-17 18:03:58 -07:00
|
|
|
--label dyn$i \
|
2019-01-22 11:34:27 -08:00
|
|
|
--init-complete-file init-complete-node$((2 + i)).log \
|
|
|
|
$maybeNoLeaderRotation"
|
|
|
|
)
|
2019-01-13 09:18:50 -08:00
|
|
|
done
|
|
|
|
numNodes=$((2 + extraNodes))
|
|
|
|
|
2018-07-30 12:51:35 -07:00
|
|
|
pids=()
|
2019-01-13 09:18:50 -08:00
|
|
|
logs=()
|
2018-07-30 12:51:35 -07:00
|
|
|
|
2019-01-21 14:33:46 -08:00
|
|
|
getNodeLogFile() {
|
2019-01-22 07:39:10 -08:00
|
|
|
declare nodeIndex=$1
|
|
|
|
declare cmd=$2
|
2019-01-21 14:33:46 -08:00
|
|
|
declare baseCmd
|
|
|
|
baseCmd=$(basename "${cmd// */}" .sh)
|
2019-01-22 07:39:10 -08:00
|
|
|
echo "log-$baseCmd-$nodeIndex.txt"
|
2019-01-21 14:33:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
startNode() {
|
2019-01-22 07:39:10 -08:00
|
|
|
declare nodeIndex=$1
|
|
|
|
declare cmd=$2
|
2019-01-21 14:33:46 -08:00
|
|
|
echo "--- Start $cmd"
|
|
|
|
declare log
|
2019-01-22 07:39:10 -08:00
|
|
|
log=$(getNodeLogFile "$nodeIndex" "$cmd")
|
2019-01-21 14:33:46 -08:00
|
|
|
rm -f "$log"
|
|
|
|
$cmd > "$log" 2>&1 &
|
|
|
|
declare pid=$!
|
|
|
|
pids+=("$pid")
|
|
|
|
echo "pid: $pid"
|
2019-01-22 07:39:10 -08:00
|
|
|
echo "log: $log"
|
2019-01-21 14:33:46 -08:00
|
|
|
}
|
|
|
|
|
2019-01-22 11:34:27 -08:00
|
|
|
initCompleteFiles=()
|
|
|
|
waitForAllNodesToInit() {
|
|
|
|
echo "--- ${#initCompleteFiles[@]} nodes booting"
|
|
|
|
SECONDS=
|
|
|
|
for initCompleteFile in "${initCompleteFiles[@]}"; do
|
|
|
|
while [[ ! -r $initCompleteFile ]]; do
|
2019-01-23 15:06:08 -08:00
|
|
|
if [[ $SECONDS -ge 240 ]]; then
|
2019-01-23 07:47:18 -08:00
|
|
|
echo "^^^ +++"
|
2019-01-22 11:34:27 -08:00
|
|
|
echo "Error: $initCompleteFile not found in $SECONDS seconds"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
echo "Waiting for $initCompleteFile ($SECONDS)..."
|
|
|
|
sleep 2
|
|
|
|
done
|
|
|
|
echo "Found $initCompleteFile"
|
|
|
|
done
|
|
|
|
echo "All nodes finished booting in $SECONDS seconds"
|
|
|
|
}
|
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
startNodes() {
|
|
|
|
declare addLogs=false
|
|
|
|
if [[ ${#logs[@]} -eq 0 ]]; then
|
|
|
|
addLogs=true
|
|
|
|
fi
|
2019-01-22 11:34:27 -08:00
|
|
|
initCompleteFiles=()
|
2019-01-22 07:39:10 -08:00
|
|
|
for i in $(seq 0 $((${#nodes[@]} - 1))); do
|
|
|
|
declare cmd=${nodes[$i]}
|
2019-01-22 11:34:27 -08:00
|
|
|
|
|
|
|
if [[ "$i" -ne 0 ]]; then # 0 == drone, skip it
|
|
|
|
declare initCompleteFile="init-complete-node$i.log"
|
|
|
|
rm -f "$initCompleteFile"
|
|
|
|
initCompleteFiles+=("$initCompleteFile")
|
|
|
|
fi
|
2019-01-22 07:39:10 -08:00
|
|
|
startNode "$i" "$cmd"
|
2019-01-18 10:09:26 -08:00
|
|
|
if $addLogs; then
|
2019-01-22 07:39:10 -08:00
|
|
|
logs+=("$(getNodeLogFile "$i" "$cmd")")
|
2019-01-18 10:09:26 -08:00
|
|
|
fi
|
|
|
|
done
|
2019-01-22 11:34:27 -08:00
|
|
|
|
|
|
|
waitForAllNodesToInit
|
2019-01-18 10:09:26 -08:00
|
|
|
}
|
2018-07-30 12:51:35 -07:00
|
|
|
|
2019-01-21 14:33:46 -08:00
|
|
|
killNode() {
|
|
|
|
declare pid=$1
|
|
|
|
echo "kill $pid"
|
|
|
|
set +e
|
|
|
|
if kill "$pid"; then
|
|
|
|
wait "$pid"
|
|
|
|
else
|
2019-01-23 07:47:18 -08:00
|
|
|
echo "^^^ +++"
|
|
|
|
echo "Warning: unable to kill $pid"
|
2019-01-21 14:33:46 -08:00
|
|
|
fi
|
|
|
|
set -e
|
|
|
|
}
|
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
killNodes() {
|
2019-03-04 15:53:18 -08:00
|
|
|
[[ ${#pids[@]} -gt 0 ]] || return
|
|
|
|
|
|
|
|
# Try to use the RPC exit API to cleanly exit the first two nodes
|
|
|
|
# (dynamic nodes, -x, are just killed since their RPC port is not known)
|
|
|
|
echo "--- RPC exit"
|
|
|
|
for port in 8899 18899; do
|
|
|
|
(
|
|
|
|
set -x
|
|
|
|
curl --retry 5 --retry-delay 2 --retry-connrefused \
|
|
|
|
-X POST -H 'Content-Type: application/json' \
|
|
|
|
-d '{"jsonrpc":"2.0","id":1, "method":"fullnodeExit"}' \
|
|
|
|
http://localhost:$port
|
|
|
|
)
|
|
|
|
done
|
|
|
|
|
|
|
|
# Give the nodes a splash of time to cleanly exit before killing them
|
|
|
|
sleep 2
|
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
echo "--- Killing nodes"
|
2018-07-30 12:51:35 -07:00
|
|
|
for pid in "${pids[@]}"; do
|
2019-01-21 14:33:46 -08:00
|
|
|
killNode "$pid"
|
|
|
|
done
|
|
|
|
pids=()
|
|
|
|
}
|
|
|
|
|
|
|
|
rollingNodeRestart() {
|
|
|
|
if [[ ${#logs[@]} -ne ${#nodes[@]} ]]; then
|
2019-01-23 07:47:18 -08:00
|
|
|
echo "^^^ +++"
|
2019-01-21 14:33:46 -08:00
|
|
|
echo "Error: log/nodes array length mismatch"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
if [[ ${#pids[@]} -ne ${#nodes[@]} ]]; then
|
2019-01-23 07:47:18 -08:00
|
|
|
echo "^^^ +++"
|
2019-01-21 14:33:46 -08:00
|
|
|
echo "Error: pids/nodes array length mismatch"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
declare oldPids=("${pids[@]}")
|
|
|
|
for i in $(seq 0 $((${#logs[@]} - 1))); do
|
|
|
|
declare pid=${oldPids[$i]}
|
|
|
|
declare cmd=${nodes[$i]}
|
|
|
|
if [[ $i -eq 0 ]]; then
|
|
|
|
# First cmd should be the drone, don't restart it.
|
|
|
|
[[ "$cmd" = "multinode-demo/drone.sh" ]]
|
|
|
|
pids+=("$pid")
|
2018-07-30 12:51:35 -07:00
|
|
|
else
|
2019-01-21 14:33:46 -08:00
|
|
|
echo "--- Restarting $pid: $cmd"
|
|
|
|
killNode "$pid"
|
|
|
|
# Delay 20 seconds to ensure the remaining cluster nodes will
|
|
|
|
# hit CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS (currently 15 seconds) for the
|
|
|
|
# node that was just stopped
|
|
|
|
echo "(sleeping for 20 seconds)"
|
|
|
|
sleep 20
|
2019-01-22 11:34:27 -08:00
|
|
|
|
|
|
|
declare initCompleteFile="init-complete-node$i.log"
|
|
|
|
rm -f "$initCompleteFile"
|
|
|
|
initCompleteFiles+=("$initCompleteFile")
|
2019-01-22 07:39:10 -08:00
|
|
|
startNode "$i" "$cmd"
|
2018-07-30 12:51:35 -07:00
|
|
|
fi
|
|
|
|
done
|
2019-01-21 14:33:46 -08:00
|
|
|
|
|
|
|
# 'Atomically' remove the old pids from the pids array
|
|
|
|
declare oldPidsList
|
|
|
|
oldPidsList="$(printf ":%s" "${oldPids[@]}"):"
|
|
|
|
declare newPids=("${pids[0]}") # 0 = drone pid
|
|
|
|
for pid in "${pids[@]}"; do
|
|
|
|
[[ $oldPidsList =~ :$pid: ]] || {
|
|
|
|
newPids+=("$pid")
|
|
|
|
}
|
|
|
|
done
|
|
|
|
pids=("${newPids[@]}")
|
2019-01-22 11:34:27 -08:00
|
|
|
|
|
|
|
waitForAllNodesToInit
|
2018-08-07 09:29:58 -07:00
|
|
|
}
|
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
verifyLedger() {
|
|
|
|
for ledger in bootstrap-leader fullnode; do
|
|
|
|
echo "--- $ledger ledger verification"
|
|
|
|
(
|
|
|
|
source multinode-demo/common.sh
|
|
|
|
set -x
|
|
|
|
$solana_ledger_tool --ledger "$SOLANA_CONFIG_DIR"/$ledger-ledger verify
|
|
|
|
) || flag_error
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2018-08-07 09:29:58 -07:00
|
|
|
shutdown() {
|
|
|
|
exitcode=$?
|
2019-01-18 10:09:26 -08:00
|
|
|
killNodes
|
2018-08-07 09:29:58 -07:00
|
|
|
|
|
|
|
set +e
|
2018-07-30 12:51:35 -07:00
|
|
|
|
2018-08-07 09:29:58 -07:00
|
|
|
echo "--- Upload artifacts"
|
2019-01-13 09:18:50 -08:00
|
|
|
for log in "${logs[@]}"; do
|
|
|
|
upload-ci-artifact "$log"
|
|
|
|
tail "$log"
|
2018-07-30 12:51:35 -07:00
|
|
|
done
|
2018-07-30 17:09:05 -07:00
|
|
|
|
|
|
|
exit $exitcode
|
2018-07-30 12:51:35 -07:00
|
|
|
}
|
|
|
|
|
2018-07-30 17:09:05 -07:00
|
|
|
trap shutdown EXIT INT
|
2018-07-30 12:51:35 -07:00
|
|
|
|
|
|
|
set -e
|
|
|
|
|
2019-01-13 09:18:50 -08:00
|
|
|
declare iteration=1
|
|
|
|
|
2018-08-14 11:31:52 -07:00
|
|
|
flag_error() {
|
2019-01-13 09:18:50 -08:00
|
|
|
echo "Failed (iteration: $iteration/$iterations)"
|
2018-08-14 11:31:52 -07:00
|
|
|
echo "^^^ +++"
|
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
2019-01-21 17:59:45 -08:00
|
|
|
if ! $skipSetup; then
|
|
|
|
multinode-demo/setup.sh
|
|
|
|
else
|
|
|
|
verifyLedger
|
|
|
|
fi
|
2019-01-18 10:09:26 -08:00
|
|
|
startNodes
|
2019-01-22 09:26:57 -08:00
|
|
|
lastTransactionCount=
|
|
|
|
enforceTransactionCountAdvance=true
|
2019-01-13 09:18:50 -08:00
|
|
|
while [[ $iteration -le $iterations ]]; do
|
|
|
|
echo "--- Node count ($iteration)"
|
|
|
|
(
|
|
|
|
source multinode-demo/common.sh
|
|
|
|
set -x
|
|
|
|
client_id=/tmp/client-id.json-$$
|
|
|
|
$solana_keygen -o $client_id || exit $?
|
2019-04-22 14:51:20 -07:00
|
|
|
$solana_gossip spy --num-nodes-exactly $numNodes || exit $?
|
2019-01-13 09:18:50 -08:00
|
|
|
rm -rf $client_id
|
|
|
|
) || flag_error
|
|
|
|
|
2019-01-16 16:08:18 -08:00
|
|
|
echo "--- RPC API: bootstrap-leader getTransactionCount ($iteration)"
|
2019-01-13 09:18:50 -08:00
|
|
|
(
|
|
|
|
set -x
|
|
|
|
curl --retry 5 --retry-delay 2 --retry-connrefused \
|
|
|
|
-X POST -H 'Content-Type: application/json' \
|
|
|
|
-d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' \
|
2019-01-30 20:19:10 -08:00
|
|
|
-o log-transactionCount.txt \
|
2019-01-13 09:18:50 -08:00
|
|
|
http://localhost:8899
|
2019-01-30 20:19:10 -08:00
|
|
|
cat log-transactionCount.txt
|
2019-01-13 09:18:50 -08:00
|
|
|
) || flag_error
|
|
|
|
|
2019-01-16 16:08:18 -08:00
|
|
|
echo "--- RPC API: fullnode getTransactionCount ($iteration)"
|
|
|
|
(
|
|
|
|
set -x
|
|
|
|
curl --retry 5 --retry-delay 2 --retry-connrefused \
|
|
|
|
-X POST -H 'Content-Type: application/json' \
|
|
|
|
-d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' \
|
|
|
|
http://localhost:18899
|
|
|
|
) || flag_error
|
|
|
|
|
2019-01-22 09:26:57 -08:00
|
|
|
# Verify transaction count as reported by the bootstrap-leader node is advancing
|
2019-01-30 20:19:10 -08:00
|
|
|
transactionCount=$(sed -e 's/{"jsonrpc":"2.0","result":\([0-9]*\),"id":1}/\1/' log-transactionCount.txt)
|
2019-01-22 09:26:57 -08:00
|
|
|
if [[ -n $lastTransactionCount ]]; then
|
|
|
|
echo "--- Transaction count check: $lastTransactionCount < $transactionCount"
|
|
|
|
if $enforceTransactionCountAdvance; then
|
|
|
|
if [[ $lastTransactionCount -ge $transactionCount ]]; then
|
|
|
|
echo "Error: Transaction count is not advancing"
|
|
|
|
echo "* lastTransactionCount: $lastTransactionCount"
|
|
|
|
echo "* transactionCount: $transactionCount"
|
|
|
|
flag_error
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
echo "enforceTransactionCountAdvance=false"
|
|
|
|
fi
|
|
|
|
enforceTransactionCountAdvance=true
|
|
|
|
fi
|
|
|
|
lastTransactionCount=$transactionCount
|
|
|
|
|
2019-01-13 09:18:50 -08:00
|
|
|
echo "--- Wallet sanity ($iteration)"
|
2019-01-18 12:09:25 -08:00
|
|
|
flag_error_if_no_leader_rotation() {
|
|
|
|
# TODO: Stop ignoring wallet sanity failures when leader rotation is enabled
|
|
|
|
# once https://github.com/solana-labs/solana/issues/2474 is fixed
|
|
|
|
if [[ -n $maybeNoLeaderRotation ]]; then
|
|
|
|
flag_error
|
2019-01-22 09:26:57 -08:00
|
|
|
else
|
|
|
|
# Wallet error occurred (and was ignored) so transactionCount may not
|
|
|
|
# advance on the next iteration
|
|
|
|
enforceTransactionCountAdvance=false
|
2019-01-18 12:09:25 -08:00
|
|
|
fi
|
|
|
|
}
|
2019-01-13 09:18:50 -08:00
|
|
|
(
|
|
|
|
set -x
|
2019-05-06 07:38:26 -07:00
|
|
|
timeout 60s scripts/wallet-sanity.sh --url http://127.0.0.1"$walletRpcPort"
|
2019-01-18 12:09:25 -08:00
|
|
|
) || flag_error_if_no_leader_rotation
|
2019-01-13 09:18:50 -08:00
|
|
|
|
|
|
|
iteration=$((iteration + 1))
|
2019-01-09 11:39:27 -08:00
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
if [[ $restartInterval != never && $((iteration % restartInterval)) -eq 0 ]]; then
|
2019-01-21 14:33:46 -08:00
|
|
|
if $rollingRestart; then
|
|
|
|
rollingNodeRestart
|
|
|
|
else
|
|
|
|
killNodes
|
|
|
|
verifyLedger
|
|
|
|
startNodes
|
|
|
|
fi
|
2019-01-18 10:09:26 -08:00
|
|
|
fi
|
|
|
|
done
|
2018-08-14 11:31:52 -07:00
|
|
|
|
2019-01-18 10:09:26 -08:00
|
|
|
killNodes
|
|
|
|
verifyLedger
|
2018-07-30 12:51:35 -07:00
|
|
|
|
|
|
|
echo +++
|
2019-01-13 09:18:50 -08:00
|
|
|
echo "Ok ($iterations iterations)"
|
|
|
|
|
2018-07-30 12:51:35 -07:00
|
|
|
exit 0
|