| 
									
										
										
										
											2017-06-01 11:52:18 +01:00
										 |  |  | #!/bin/bash
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # A script to boot a dev swarm cluster on a Linux host (typically in a Docker | 
					
						
							|  |  |  | # container started with swarm/dev/run.sh). | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # The cluster contains a bootnode, a geth node and multiple swarm nodes, with | 
					
						
							|  |  |  | # each node having its own data directory in a base directory passed with the | 
					
						
							|  |  |  | # --dir flag (default is swarm/dev/cluster). | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # To avoid using different ports for each node and to make networking more | 
					
						
							|  |  |  | # realistic, each node gets its own network namespace with IPs assigned from | 
					
						
							|  |  |  | # the 192.168.33.0/24 subnet: | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # bootnode: 192.168.33.2 | 
					
						
							|  |  |  | # geth:     192.168.33.3 | 
					
						
							|  |  |  | # swarm:    192.168.33.10{1,2,...,n} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | set -e | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" | 
					
						
							|  |  |  | source "${ROOT}/swarm/dev/scripts/util.sh" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # DEFAULT_BASE_DIR is the default base directory to store node data | 
					
						
							|  |  |  | DEFAULT_BASE_DIR="${ROOT}/swarm/dev/cluster" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # DEFAULT_CLUSTER_SIZE is the default swarm cluster size | 
					
						
							|  |  |  | DEFAULT_CLUSTER_SIZE=3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Linux bridge configuration for connecting the node network namespaces | 
					
						
							|  |  |  | BRIDGE_NAME="swarmbr0" | 
					
						
							|  |  |  | BRIDGE_IP="192.168.33.1" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # static bootnode configuration | 
					
						
							|  |  |  | BOOTNODE_IP="192.168.33.2" | 
					
						
							|  |  |  | BOOTNODE_PORT="30301" | 
					
						
							|  |  |  | BOOTNODE_KEY="32078f313bea771848db70745225c52c00981589ad6b5b49163f0f5ee852617d" | 
					
						
							|  |  |  | BOOTNODE_PUBKEY="760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d" | 
					
						
							|  |  |  | BOOTNODE_URL="enode://${BOOTNODE_PUBKEY}@${BOOTNODE_IP}:${BOOTNODE_PORT}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # static geth configuration | 
					
						
							|  |  |  | GETH_IP="192.168.33.3" | 
					
						
							|  |  |  | GETH_RPC_PORT="8545" | 
					
						
							|  |  |  | GETH_RPC_URL="http://${GETH_IP}:${GETH_RPC_PORT}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | usage() { | 
					
						
							|  |  |  |   cat >&2 <<USAGE | 
					
						
							|  |  |  | usage: $0 [options] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Boot a dev swarm cluster. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OPTIONS: | 
					
						
							|  |  |  |   -d, --dir DIR     Base directory to store node data [default: ${DEFAULT_BASE_DIR}] | 
					
						
							|  |  |  |   -s, --size SIZE   Size of swarm cluster [default: ${DEFAULT_CLUSTER_SIZE}] | 
					
						
							|  |  |  |   -h, --help        Show this message | 
					
						
							|  |  |  | USAGE | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | main() { | 
					
						
							|  |  |  |   local base_dir="${DEFAULT_BASE_DIR}" | 
					
						
							|  |  |  |   local cluster_size="${DEFAULT_CLUSTER_SIZE}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   parse_args "$@" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local pid_dir="${base_dir}/pids" | 
					
						
							|  |  |  |   local log_dir="${base_dir}/logs" | 
					
						
							|  |  |  |   mkdir -p "${base_dir}" "${pid_dir}" "${log_dir}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   stop_cluster | 
					
						
							|  |  |  |   create_network | 
					
						
							|  |  |  |   start_bootnode | 
					
						
							|  |  |  |   start_geth_node | 
					
						
							|  |  |  |   start_swarm_nodes | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | parse_args() { | 
					
						
							|  |  |  |   while true; do | 
					
						
							|  |  |  |     case "$1" in | 
					
						
							|  |  |  |       -h | --help) | 
					
						
							|  |  |  |         usage | 
					
						
							|  |  |  |         exit 0 | 
					
						
							|  |  |  |         ;; | 
					
						
							|  |  |  |       -d | --dir) | 
					
						
							|  |  |  |         if [[ -z "$2" ]]; then | 
					
						
							|  |  |  |           fail "--dir flag requires an argument" | 
					
						
							|  |  |  |         fi | 
					
						
							|  |  |  |         base_dir="$2" | 
					
						
							|  |  |  |         shift 2 | 
					
						
							|  |  |  |         ;; | 
					
						
							|  |  |  |       -s | --size) | 
					
						
							|  |  |  |         if [[ -z "$2" ]]; then | 
					
						
							|  |  |  |           fail "--size flag requires an argument" | 
					
						
							|  |  |  |         fi | 
					
						
							|  |  |  |         cluster_size="$2" | 
					
						
							|  |  |  |         shift 2 | 
					
						
							|  |  |  |         ;; | 
					
						
							|  |  |  |       *) | 
					
						
							|  |  |  |         break | 
					
						
							|  |  |  |         ;; | 
					
						
							|  |  |  |     esac | 
					
						
							|  |  |  |   done | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if [[ $# -ne 0 ]]; then | 
					
						
							|  |  |  |     usage | 
					
						
							|  |  |  |     fail "ERROR: invalid arguments: $@" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | stop_cluster() { | 
					
						
							|  |  |  |   info "stopping existing cluster" | 
					
						
							|  |  |  |   "${ROOT}/swarm/dev/scripts/stop-cluster.sh" --dir "${base_dir}" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # create_network creates a Linux bridge which is used to connect the node | 
					
						
							|  |  |  | # network namespaces together | 
					
						
							|  |  |  | create_network() { | 
					
						
							|  |  |  |   local subnet="${BRIDGE_IP}/24" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   info "creating ${subnet} network on ${BRIDGE_NAME}" | 
					
						
							|  |  |  |   ip link add name "${BRIDGE_NAME}" type bridge | 
					
						
							|  |  |  |   ip link set dev "${BRIDGE_NAME}" up | 
					
						
							|  |  |  |   ip address add "${subnet}" dev "${BRIDGE_NAME}" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # start_bootnode starts a bootnode which is used to bootstrap the geth and | 
					
						
							|  |  |  | # swarm nodes | 
					
						
							|  |  |  | start_bootnode() { | 
					
						
							|  |  |  |   local key_file="${base_dir}/bootnode.key" | 
					
						
							|  |  |  |   echo -n "${BOOTNODE_KEY}" > "${key_file}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local args=( | 
					
						
							|  |  |  |     --addr      "${BOOTNODE_IP}:${BOOTNODE_PORT}" | 
					
						
							|  |  |  |     --nodekey   "${key_file}" | 
					
						
							|  |  |  |     --verbosity "6" | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   start_node "bootnode" "${BOOTNODE_IP}" "$(which bootnode)" ${args[@]} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # start_geth_node starts a geth node with --datadir pointing at <base-dir>/geth | 
					
						
							|  |  |  | # and a single, unlocked account with password "geth" | 
					
						
							|  |  |  | start_geth_node() { | 
					
						
							|  |  |  |   local dir="${base_dir}/geth" | 
					
						
							|  |  |  |   mkdir -p "${dir}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local password="geth" | 
					
						
							|  |  |  |   echo "${password}" > "${dir}/password" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # create an account if necessary | 
					
						
							|  |  |  |   if [[ ! -e "${dir}/keystore" ]]; then | 
					
						
							|  |  |  |     info "creating geth account" | 
					
						
							|  |  |  |     create_account "${dir}" "${password}" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # get the account address | 
					
						
							|  |  |  |   local address="$(jq --raw-output '.address' ${dir}/keystore/*)" | 
					
						
							|  |  |  |   if [[ -z "${address}" ]]; then | 
					
						
							|  |  |  |     fail "failed to get geth account address" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local args=( | 
					
						
							|  |  |  |     --datadir   "${dir}" | 
					
						
							|  |  |  |     --networkid "321" | 
					
						
							|  |  |  |     --bootnodes "${BOOTNODE_URL}" | 
					
						
							|  |  |  |     --unlock    "${address}" | 
					
						
							|  |  |  |     --password  "${dir}/password" | 
					
						
							|  |  |  |     --rpc | 
					
						
							|  |  |  |     --rpcaddr   "${GETH_IP}" | 
					
						
							|  |  |  |     --rpcport   "${GETH_RPC_PORT}" | 
					
						
							|  |  |  |     --verbosity "6" | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   start_node "geth" "${GETH_IP}" "$(which geth)" ${args[@]} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | start_swarm_nodes() { | 
					
						
							|  |  |  |   for i in $(seq 1 ${cluster_size}); do | 
					
						
							|  |  |  |     start_swarm_node "${i}" | 
					
						
							|  |  |  |   done | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # start_swarm_node starts a swarm node with a name like "swarmNN" (where NN is | 
					
						
							|  |  |  | # a zero-padded integer like "07"), --datadir pointing at <base-dir>/<name> | 
					
						
							|  |  |  | # (e.g. <base-dir>/swarm07) and a single account with <name> as the password | 
					
						
							|  |  |  | start_swarm_node() { | 
					
						
							|  |  |  |   local num=$1 | 
					
						
							|  |  |  |   local name="swarm$(printf '%02d' ${num})" | 
					
						
							|  |  |  |   local ip="192.168.33.1$(printf '%02d' ${num})" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local dir="${base_dir}/${name}" | 
					
						
							|  |  |  |   mkdir -p "${dir}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local password="${name}" | 
					
						
							|  |  |  |   echo "${password}" > "${dir}/password" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # create an account if necessary | 
					
						
							|  |  |  |   if [[ ! -e "${dir}/keystore" ]]; then | 
					
						
							|  |  |  |     info "creating account for ${name}" | 
					
						
							|  |  |  |     create_account "${dir}" "${password}" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # get the account address | 
					
						
							|  |  |  |   local address="$(jq --raw-output '.address' ${dir}/keystore/*)" | 
					
						
							|  |  |  |   if [[ -z "${address}" ]]; then | 
					
						
							|  |  |  |     fail "failed to get swarm account address" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   local args=( | 
					
						
							|  |  |  |     --bootnodes    "${BOOTNODE_URL}" | 
					
						
							|  |  |  |     --datadir      "${dir}" | 
					
						
							|  |  |  |     --identity     "${name}" | 
					
						
							| 
									
										
										
										
											2017-06-30 10:10:41 +01:00
										 |  |  |     --ens-api      "${GETH_RPC_URL}" | 
					
						
							| 
									
										
										
										
											2017-06-01 11:52:18 +01:00
										 |  |  |     --bzznetworkid "321" | 
					
						
							|  |  |  |     --bzzaccount   "${address}" | 
					
						
							|  |  |  |     --password     "${dir}/password" | 
					
						
							|  |  |  |     --verbosity    "6" | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   start_node "${name}" "${ip}" "$(which swarm)" ${args[@]} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # start_node runs the node command as a daemon in a network namespace | 
					
						
							|  |  |  | start_node() { | 
					
						
							|  |  |  |   local name="$1" | 
					
						
							|  |  |  |   local ip="$2" | 
					
						
							|  |  |  |   local path="$3" | 
					
						
							|  |  |  |   local cmd_args=${@:4} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   info "starting ${name} with IP ${ip}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   create_node_network "${name}" "${ip}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # add a marker to the log file | 
					
						
							|  |  |  |   cat >> "${log_dir}/${name}.log" <<EOF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | 
					
						
							|  |  |  | Starting ${name} node - $(date) | 
					
						
							|  |  |  | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EOF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # run the command in the network namespace using start-stop-daemon to | 
					
						
							|  |  |  |   # daemonise the process, sending all output to the log file | 
					
						
							|  |  |  |   local daemon_args=( | 
					
						
							|  |  |  |     --start | 
					
						
							|  |  |  |     --background | 
					
						
							|  |  |  |     --no-close | 
					
						
							|  |  |  |     --make-pidfile | 
					
						
							|  |  |  |     --pidfile "${pid_dir}/${name}.pid" | 
					
						
							|  |  |  |     --exec "${path}" | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  |   if ! ip netns exec "${name}" start-stop-daemon ${daemon_args[@]} -- $cmd_args &>> "${log_dir}/${name}.log"; then | 
					
						
							|  |  |  |     fail "could not start ${name}, check ${log_dir}/${name}.log" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # create_node_network creates a network namespace and connects it to the Linux | 
					
						
							|  |  |  | # bridge using a veth pair | 
					
						
							|  |  |  | create_node_network() { | 
					
						
							|  |  |  |   local name="$1" | 
					
						
							|  |  |  |   local ip="$2" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # create the namespace | 
					
						
							|  |  |  |   ip netns add "${name}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # create the veth pair | 
					
						
							|  |  |  |   local veth0="veth${name}0" | 
					
						
							|  |  |  |   local veth1="veth${name}1" | 
					
						
							|  |  |  |   ip link add name "${veth0}" type veth peer name "${veth1}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # add one end to the bridge | 
					
						
							|  |  |  |   ip link set dev "${veth0}" master "${BRIDGE_NAME}" | 
					
						
							|  |  |  |   ip link set dev "${veth0}" up | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # add the other end to the namespace, rename it eth0 and give it the ip | 
					
						
							|  |  |  |   ip link set dev "${veth1}" netns "${name}" | 
					
						
							|  |  |  |   ip netns exec "${name}" ip link set dev "${veth1}" name "eth0" | 
					
						
							|  |  |  |   ip netns exec "${name}" ip link set dev "eth0" up | 
					
						
							|  |  |  |   ip netns exec "${name}" ip address add "${ip}/24" dev "eth0" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | create_account() { | 
					
						
							|  |  |  |   local dir=$1 | 
					
						
							|  |  |  |   local password=$2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   geth --datadir "${dir}" --password /dev/stdin account new <<< "${password}" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | main "$@" |