github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/dev/scripts/boot-cluster.sh (about)

     1  #!/bin/bash
     2  #
     3  # A script to boot a dev swarm cluster on a Linux host (typically in a Docker
     4  # container started with swarm/dev/run.sh).
     5  #
     6  # The cluster contains a bootnode, a geth node and multiple swarm nodes, with
     7  # each node having its own data directory in a base directory passed with the
     8  # --dir flag (default is swarm/dev/cluster).
     9  #
    10  # To avoid using different ports for each node and to make networking more
    11  # realistic, each node gets its own network namespace with IPs assigned from
    12  # the 192.168.33.0/24 subnet:
    13  #
    14  # bootnode: 192.168.33.2
    15  # geth:     192.168.33.3
    16  # swarm:    192.168.33.10{1,2,...,n}
    17  
    18  set -e
    19  
    20  ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
    21  source "${ROOT}/swarm/dev/scripts/util.sh"
    22  
    23  # DEFAULT_BASE_DIR is the default base directory to store node data
    24  DEFAULT_BASE_DIR="${ROOT}/swarm/dev/cluster"
    25  
    26  # DEFAULT_CLUSTER_SIZE is the default swarm cluster size
    27  DEFAULT_CLUSTER_SIZE=3
    28  
    29  # Linux bridge configuration for connecting the node network namespaces
    30  BRIDGE_NAME="swarmbr0"
    31  BRIDGE_IP="192.168.33.1"
    32  
    33  # static bootnode configuration
    34  BOOTNODE_IP="192.168.33.2"
    35  BOOTNODE_PORT="30301"
    36  BOOTNODE_KEY="32078f313bea771848db70745225c52c00981589ad6b5b49163f0f5ee852617d"
    37  BOOTNODE_PUBKEY="760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"
    38  BOOTNODE_URL="enode://${BOOTNODE_PUBKEY}@${BOOTNODE_IP}:${BOOTNODE_PORT}"
    39  
    40  # static geth configuration
    41  GETH_IP="192.168.33.3"
    42  GETH_RPC_PORT="8545"
    43  GETH_RPC_URL="http://${GETH_IP}:${GETH_RPC_PORT}"
    44  
    45  usage() {
    46    cat >&2 <<USAGE
    47  usage: $0 [options]
    48  
    49  Boot a dev swarm cluster.
    50  
    51  OPTIONS:
    52    -d, --dir DIR     Base directory to store node data [default: ${DEFAULT_BASE_DIR}]
    53    -s, --size SIZE   Size of swarm cluster [default: ${DEFAULT_CLUSTER_SIZE}]
    54    -h, --help        Show this message
    55  USAGE
    56  }
    57  
    58  main() {
    59    local base_dir="${DEFAULT_BASE_DIR}"
    60    local cluster_size="${DEFAULT_CLUSTER_SIZE}"
    61  
    62    parse_args "$@"
    63  
    64    local pid_dir="${base_dir}/pids"
    65    local log_dir="${base_dir}/logs"
    66    mkdir -p "${base_dir}" "${pid_dir}" "${log_dir}"
    67  
    68    stop_cluster
    69    create_network
    70    start_bootnode
    71    start_geth_node
    72    start_swarm_nodes
    73  }
    74  
    75  parse_args() {
    76    while true; do
    77      case "$1" in
    78        -h | --help)
    79          usage
    80          exit 0
    81          ;;
    82        -d | --dir)
    83          if [[ -z "$2" ]]; then
    84            fail "--dir flag requires an argument"
    85          fi
    86          base_dir="$2"
    87          shift 2
    88          ;;
    89        -s | --size)
    90          if [[ -z "$2" ]]; then
    91            fail "--size flag requires an argument"
    92          fi
    93          cluster_size="$2"
    94          shift 2
    95          ;;
    96        *)
    97          break
    98          ;;
    99      esac
   100    done
   101  
   102    if [[ $# -ne 0 ]]; then
   103      usage
   104      fail "ERROR: invalid arguments: $@"
   105    fi
   106  }
   107  
   108  stop_cluster() {
   109    info "stopping existing cluster"
   110    "${ROOT}/swarm/dev/scripts/stop-cluster.sh" --dir "${base_dir}"
   111  }
   112  
   113  # create_network creates a Linux bridge which is used to connect the node
   114  # network namespaces together
   115  create_network() {
   116    local subnet="${BRIDGE_IP}/24"
   117  
   118    info "creating ${subnet} network on ${BRIDGE_NAME}"
   119    ip link add name "${BRIDGE_NAME}" type bridge
   120    ip link set dev "${BRIDGE_NAME}" up
   121    ip address add "${subnet}" dev "${BRIDGE_NAME}"
   122  }
   123  
   124  # start_bootnode starts a bootnode which is used to bootstrap the geth and
   125  # swarm nodes
   126  start_bootnode() {
   127    local key_file="${base_dir}/bootnode.key"
   128    echo -n "${BOOTNODE_KEY}" > "${key_file}"
   129  
   130    local args=(
   131      --addr      "${BOOTNODE_IP}:${BOOTNODE_PORT}"
   132      --nodekey   "${key_file}"
   133      --verbosity "6"
   134    )
   135  
   136    start_node "bootnode" "${BOOTNODE_IP}" "$(which bootnode)" ${args[@]}
   137  }
   138  
   139  # start_geth_node starts a geth node with --datadir pointing at <base-dir>/geth
   140  # and a single, unlocked account with password "geth"
   141  start_geth_node() {
   142    local dir="${base_dir}/geth"
   143    mkdir -p "${dir}"
   144  
   145    local password="geth"
   146    echo "${password}" > "${dir}/password"
   147  
   148    # create an account if necessary
   149    if [[ ! -e "${dir}/keystore" ]]; then
   150      info "creating geth account"
   151      create_account "${dir}" "${password}"
   152    fi
   153  
   154    # get the account address
   155    local address="$(jq --raw-output '.address' ${dir}/keystore/*)"
   156    if [[ -z "${address}" ]]; then
   157      fail "failed to get geth account address"
   158    fi
   159  
   160    local args=(
   161      --datadir   "${dir}"
   162      --networkid "321"
   163      --bootnodes "${BOOTNODE_URL}"
   164      --unlock    "${address}"
   165      --password  "${dir}/password"
   166      --rpc
   167      --rpcaddr   "${GETH_IP}"
   168      --rpcport   "${GETH_RPC_PORT}"
   169      --verbosity "6"
   170    )
   171  
   172    start_node "geth" "${GETH_IP}" "$(which geth)" ${args[@]}
   173  }
   174  
   175  start_swarm_nodes() {
   176    for i in $(seq 1 ${cluster_size}); do
   177      start_swarm_node "${i}"
   178    done
   179  }
   180  
   181  # start_swarm_node starts a swarm node with a name like "swarmNN" (where NN is
   182  # a zero-padded integer like "07"), --datadir pointing at <base-dir>/<name>
   183  # (e.g. <base-dir>/swarm07) and a single account with <name> as the password
   184  start_swarm_node() {
   185    local num=$1
   186    local name="swarm$(printf '%02d' ${num})"
   187    local ip="192.168.33.1$(printf '%02d' ${num})"
   188  
   189    local dir="${base_dir}/${name}"
   190    mkdir -p "${dir}"
   191  
   192    local password="${name}"
   193    echo "${password}" > "${dir}/password"
   194  
   195    # create an account if necessary
   196    if [[ ! -e "${dir}/keystore" ]]; then
   197      info "creating account for ${name}"
   198      create_account "${dir}" "${password}"
   199    fi
   200  
   201    # get the account address
   202    local address="$(jq --raw-output '.address' ${dir}/keystore/*)"
   203    if [[ -z "${address}" ]]; then
   204      fail "failed to get swarm account address"
   205    fi
   206  
   207    local args=(
   208      --bootnodes    "${BOOTNODE_URL}"
   209      --datadir      "${dir}"
   210      --identity     "${name}"
   211      --ens-api      "${GETH_RPC_URL}"
   212      --bzznetworkid "321"
   213      --bzzaccount   "${address}"
   214      --password     "${dir}/password"
   215      --verbosity    "6"
   216    )
   217  
   218    start_node "${name}" "${ip}" "$(which swarm)" ${args[@]}
   219  }
   220  
   221  # start_node runs the node command as a daemon in a network namespace
   222  start_node() {
   223    local name="$1"
   224    local ip="$2"
   225    local path="$3"
   226    local cmd_args=${@:4}
   227  
   228    info "starting ${name} with IP ${ip}"
   229  
   230    create_node_network "${name}" "${ip}"
   231  
   232    # add a marker to the log file
   233    cat >> "${log_dir}/${name}.log" <<EOF
   234  
   235  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   236  Starting ${name} node - $(date)
   237  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   238  
   239  EOF
   240  
   241    # run the command in the network namespace using start-stop-daemon to
   242    # daemonise the process, sending all output to the log file
   243    local daemon_args=(
   244      --start
   245      --background
   246      --no-close
   247      --make-pidfile
   248      --pidfile "${pid_dir}/${name}.pid"
   249      --exec "${path}"
   250    )
   251    if ! ip netns exec "${name}" start-stop-daemon ${daemon_args[@]} -- $cmd_args &>> "${log_dir}/${name}.log"; then
   252      fail "could not start ${name}, check ${log_dir}/${name}.log"
   253    fi
   254  }
   255  
   256  # create_node_network creates a network namespace and connects it to the Linux
   257  # bridge using a veth pair
   258  create_node_network() {
   259    local name="$1"
   260    local ip="$2"
   261  
   262    # create the namespace
   263    ip netns add "${name}"
   264  
   265    # create the veth pair
   266    local veth0="veth${name}0"
   267    local veth1="veth${name}1"
   268    ip link add name "${veth0}" type veth peer name "${veth1}"
   269  
   270    # add one end to the bridge
   271    ip link set dev "${veth0}" master "${BRIDGE_NAME}"
   272    ip link set dev "${veth0}" up
   273  
   274    # add the other end to the namespace, rename it eth0 and give it the ip
   275    ip link set dev "${veth1}" netns "${name}"
   276    ip netns exec "${name}" ip link set dev "${veth1}" name "eth0"
   277    ip netns exec "${name}" ip link set dev "eth0" up
   278    ip netns exec "${name}" ip address add "${ip}/24" dev "eth0"
   279  }
   280  
   281  create_account() {
   282    local dir=$1
   283    local password=$2
   284  
   285    geth --datadir "${dir}" --password /dev/stdin account new <<< "${password}"
   286  }
   287  
   288  main "$@"