From 8eb7c7f06ae595f825e8ea735b9a076008e46ac0 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Mon, 18 Jul 2016 10:21:55 +0100 Subject: [PATCH] swarm: Support for dockerized networks --- containers/docker/swarm-alpine/Dockerfile | 28 ++ containers/docker/swarm-alpine/swarm.sh | 40 ++ swarm/cmd/swarm/swarm | 444 +++++++--------------- 3 files changed, 210 insertions(+), 302 deletions(-) create mode 100644 containers/docker/swarm-alpine/Dockerfile create mode 100644 containers/docker/swarm-alpine/swarm.sh diff --git a/containers/docker/swarm-alpine/Dockerfile b/containers/docker/swarm-alpine/Dockerfile new file mode 100644 index 0000000000..19d946700d --- /dev/null +++ b/containers/docker/swarm-alpine/Dockerfile @@ -0,0 +1,28 @@ +# Docker container spec for building the swarm branch of go-ethereum. +# +# The build process it potentially longer running but every effort was made to +# produce a very minimalistic container that can be reused many times without +# needing to constantly rebuild. +FROM alpine:3.3 + +# Build go-ethereum on the fly and delete all build tools afterwards +RUN \ + apk add --update go git make gcc musl-dev && \ + git clone https://github.com/ethersphere/go-ethereum && \ + (cd go-ethereum && git checkout s/sworm-rc3-update) && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apk del go git make gcc musl-dev && \ + rm -rf /go-ethereum && rm -rf /var/cache/apk/* + +# Make sure bash and jq is available for easier wrapper implementation +RUN apk add --update bash jq + +# Inject the startup script +ADD swarm.sh /swarm.sh +RUN chmod +x /swarm.sh + +# Export the usual networking ports to allow outside access to the node +EXPOSE 8545 8546 30303 + +ENTRYPOINT ["/swarm.sh"] diff --git a/containers/docker/swarm-alpine/swarm.sh b/containers/docker/swarm-alpine/swarm.sh new file mode 100644 index 0000000000..8e4ab0e36a --- /dev/null +++ b/containers/docker/swarm-alpine/swarm.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Startup script to initialize and boot a go-ethereum instance as a swarm node. +# +# This script assumes the following files: +# - `geth` binary is located in the filesystem root +# - `genesis.json` file is located in the filesystem root + +# Immediately abort the script on any error encountered +set -e + +if [ "$SWARM_NETWORK_ID" = "" ]; then export SWARM_NETWORK_ID=322; fi + +if [ ! -f "/swarm/data/nodekey" ]; then + # First run + echo "Initializing swarm node..." + mkdir -p /swarm/data + mkdir -p /swarm/enodes + mkdir -p /swarm/pids + /geth --datadir=/swarm/data --password=<(echo -n) account new +fi + +/geth --dev \ + --maxpeers=40 \ + --shh=false \ + --networkid=$SWARM_NETWORK_ID \ + --bzznoswap \ + --verbosity=6 \ + --vmodule=swarm/*=5,discover=5 \ + --datadir=/swarm/data \ + --bzzaccount=0 \ + --unlock=0 \ + --port=30303 \ + --bzzport=32200 \ + --nat=none \ + --rpc \ + --rpcaddr="0.0.0.0" \ + --rpccorsdomain="*" \ + --password=<(echo -n) \ + $* diff --git a/swarm/cmd/swarm/swarm b/swarm/cmd/swarm/swarm index d9f5b099db..8ff2cc61a9 100755 --- a/swarm/cmd/swarm/swarm +++ b/swarm/cmd/swarm/swarm @@ -1,24 +1,18 @@ #!/bin/bash + +if [ "$SWARM_DOCKER_TAG" = "" ]; then export SWARM_DOCKER_TAG="arachnid/swarm:latest"; fi + +# Local directory, for legacy commands involving the geth binary if [ "$GETH_DIR" = "" ]; then if [ "$GOPATH" = "" ]; then echo "either GETH_DIR or GOPATH environment variable must be set"; exit 1; fi export GETH_DIR=$GOPATH/src/github.com/ethereum/go-ethereum fi -if [ "$GETH" = "" ]; then - export GETH=$GETH_DIR/geth -fi - -if [ "$SWARM_NETWORK_ID" = "" ]; then export SWARM_NETWORK_ID=322; fi - -if [ "$SWARM_DIR" = "" ]; then export SWARM_DIR=$HOME/bzz; fi - -if [ "$IP_ADDR" = "" ]; then - # export IP_ADDR=`curl ipecho.net/plain 2>/dev/null;echo f` - export IP_ADDR= -fi +CONTAINER_DATADIR="/swarm/data" +CONTAINER_SOCKET="$CONTAINER_DATADIR/geth.ipc" +GETH="/geth --datadir=$CONTAINER_DATADIR" root=$SWARM_DIR -network_id=$SWARM_NETWORK_ID cmd=$1 shift @@ -30,38 +24,55 @@ function randomfile { dd if=/dev/urandom of=/dev/stdout bs=1024 count=$1 2>/dev/null } -# swarm attach 00 brings up a console attached to a running instance -function attach { +# swarm container 00 returns the id of the docker container for node n +function container { + docker ps -a --filter="label=swarm" --filter="label=id=$1" --format="{{.ID}}" +} + +# swarm containers returns the IDs of all docker containers for the swarm +function containers { + docker ps -a --filter="label=swarm" --format="{{.ID}}" +} + +# returns the node number of a container +function nodenum { + docker inspect --format "{{.Config.Labels.id}}" $1 +} + +function dockerexec { id=$1 shift - echo "attaching console to instance $id" - cmd="$GETH --datadir=$dir/data/$id $* attach ipc:$dir/data/$id/geth.ipc" - # echo $cmd - eval $cmd - } + if [ "$id" = "all" ]; then + for containerid in $(containers); do + docker exec -it $containerid $* + done + else + docker exec -it $(container $id) $* + fi +} + +function dgeth { + id=$1 + shift + dockerexec $id $GETH $* +} # swarm attach 00 brings up a console attached to a running instance +function attach { + echo "attaching console to instance $id" + dgeth $1 attach ipc:/$CONTAINER_SOCKET + } + +# swarm execute 00 runs the specified command in an instance function execute { id=$1 shift - # attach $id --exec "'$*' " - cmd="$GETH --datadir=$dir/data/$id --exec '$*' attach ipc:$dir/data/$id/geth.ipc" - # echo $cmd - eval $cmd + dgeth $id --exec "$*" attach ipc:/$CONTAINER_SOCKET } # swarm hive 00 displays the kademlia table of the given running instance function hive { - if [ "$1" = "all" ]; then - N=`ls -1 -d $dir/data/* |wc -l` - for ((i=0;i $log 2>&1 &" - - $GETH $opts --password=<(echo -n $id) > "$log" 2>&1 & # comment out if you pipe it to a tty etc. - ln -sf "$log" "$linklog" - - # wait until ready + docker start $containerid >/dev/null + # wait until ready ((j=0)) while true; do - execute $id "net" > /dev/null 2>&1 && break + execute $id "net" >/dev/null 2>&1 && break sleep 1 echo -n "." if ((j++>10)); then @@ -213,179 +173,99 @@ function start { exit 1 fi done - echo -n "started - " - pid=`ps auxwww|grep geth|grep "ty=$id"|grep -v grep|awk '{print $2}'` - echo "pid: $pid" - echo $pid > $dir/pids/$id.pid + + # add other peers + for othercontainerid in $livenodes; do + otherid=$(nodenum $othercontainerid) + otherenode=$(enode $otherid) + execute $id "admin.addPeer($otherenode)" >/dev/null + done +} + +function start { + id=$1 + shift + + if [ "$id" = "all" ]; then + for containerid in $(containers); do + startcontainer $containerid + echo "started instance $(nodenum $containerid)" + done + echo "started all instances" + else + startcontainer $(container $id) + echo "started instance $id" + fi } -# setup 00 creates the direcories for instance +# setup 00 creates the docker container for an instance function setup { id=$1 shift - mkdir -p $dir/data/$id - mkdir -p $dir/enodes - mkdir -p $dir/pids - mkdir -p $dir/log + docker create $(rawoptions $id) >/dev/null } -# creates the swarm base account for an instance -function create-account { - id=$1 - datadir=$dir/data/$id - # if we do not have an account, create one - # will not prompt for password, we use the double digit instance id as passwd - # NEVER EVER USE THESE ACCOUNTS FOR INTERACTING WITH A LIVE CHAIN - keystoredir="$datadir/keystore/" - # echo "KeyStore dir: $keystoredir" - if [ ! -d "$keystoredir" ]; then - # echo "create an account with password $id [DO NOT EVER USE THIS ON LIVE]" - # mkdir -p $datadir/keystore - $GETH --datadir=$datadir --password=<(echo -n $id) account new >/dev/null 2>&1 - # create account with password 00, 01, ... - # note that the account key will be stored also separately outside - # datadir - # this way you can safely clear the data directory and still keep your key - # under /keystore/dd - # LS=$(ls $datadir/keystore) - # echo $LS - while [ ! -d "$keystoredir" ]; do - echo "." - ((i++)) - if ((i>10)); then break; fi - sleep 1 - done - # echo "copying keys $datadir/keystore $root/keystore/$id" - mkdir -p $dir/keystore/$id - cp -R "$keystoredir" $dir/keystore/$id - fi +function stopcontainer { + containerid=$1 + shift + status=`docker inspect --format="{{.State.Status}}" $containerid` + if [ "$status" = "running" ]; then + echo "stopping instance $(nodenum $containerid)" + docker stop $containerid >/dev/null + fi } -# shuts down a running instance, cleans the pid +# shuts down a running instance function stop { id=$1 shift - if [ $id = "all" ]; then - procs=`cat $dir/pids/*.pid 2>/dev/null |perl -pe 's/^\s+//;s/\s+\\$//;s/\s+/\n/g'` - # echo "stopping processes $procs" - for p in $procs; do - shutdown $p + if [ "$id" = "all" ]; then + for containerid in $(containers); do + stopcontainer $containerid done - rm -rf $dir/pids/* else - pid=$dir/pids/$id.pid - if [ -f $pid ]; then - echo "stopping instance $id, pid="`cat $pid` - shutdown `cat $pid` - rm $pid - fi + stopcontainer $(container $id) fi } -# shutdown kills the node with interrupt 2 - if it resits falls back to -9 after 10s -function shutdown { - echo -n "stopping $1..." - kill -2 $1 - while true; do - ps auxwww|grep geth|grep -v grep|awk '{print $2}'|grep -ql $1 || break - if ((i++>5)); then - echo "not stopping. killing it" - kill -QUIT $1 - break - fi - echo '.' - sleep 1 - done - echo "stopped" -} - # swarm restart 00 calls stop and start function restart { id=$1 shift - if [ $id = "all" ]; then - stop all - N=`ls -d1 $dir/data/*|wc -l` - cluster $N $* - else - stop $id - start $id $* - fi + stop $id + start $id $* } -# swarm init X sets up and starts a new client instance +# swarm init X sets up and starts a new cluster ########################################################## # # IT WIPES THE DATABASE # ########################################################## function init { - killall geth - reset all + destroy all cluster $* - enode all - connect all } -# reset wipes the datadirs of the instance +function destroy { + id=$1 + shift + stop $id + if [ "$id" = "all" ]; then + for containerid in $(containers); do + docker rm $containerid + done + else + containerid=$(container id) + docker rm $containerid + fi +} + +# reset replaces a node with a blank instance function reset { - id=$1 - shift - if [ $id = "all" ]; then - rm -rf $dir - else - rm -rf$dir/*/$id* - fi -} - -# enode displays the instance's enode address -# swarm enode all writes all instances' enodes in a file -function enode { - id=$1 - shift - - if [ $id = "all" ]; then - json=$dir/static-nodes.json - enodes=$dir/enodes.lst - cmd=$dir/connect.js - rm -f $enodes $json $cmd - # build a static nodes(-like) list of all enodes of the local cluster - echo "[" >> $json - N=`ls -1 -d $dir/data/* |wc -l` - for ((i=0;i $f - echo -n "admin.addPeer(" >> $cmd - cat "$f" | perl -pe 's/\s*$//' >> $cmd - echo ");" >> $cmd - cat $f |perl -pe 's/"//g'>> $enodes - cat $f >> $json - echo "," >> $json - done - echo "\"\"]" >> $json - cat $enodes - else - # echo "local IP: $ip_addr " - execute $id 'admin.nodeInfo.enode' |perl -pe "s/\[\:\:\]/$IP_ADDR/ " - fi -} - -# connect sources the local or remote set of peers and connects the node to the peers -function connect { - id=$1 - shift - if [ $id = "all" ]; then - N=`ls -1 -d $dir/data/* |wc -l` - for ((i=0;i&2 file=`basename $2` - /usr/bin/time -f "latency: %e" swarm execute $1 "bzz.upload(\"$2\", \"$file\")"|tail -n1> /tmp/key + /usr/bin/time -f "latency: %e" execute $1 "bzz.upload(\"$2\", \"$file\")"|tail -n1> /tmp/key cat /tmp/key } @@ -458,22 +334,6 @@ function down { echo "found OK" } -# static info about an instance (available even if node is off) -function info { - echo "swarm node information" - echo "ROOTDIR: $root" - echo "DATADIR: $dir/data/$1" - echo "LOGFILE: $dir/log/$1.log" - echo "HTTPAPI: http://localhost:322$1" - echo "ETHPORT: 303$1" - echo "RPCPORT: 302$1" - echo "ACCOUNT:" 0x`ls -1 $dir/data/$1/bzz` - echo "CHEQUEB:" `cat $dir/data/$1/bzz/*/config.json|grep Contract|awk -F\" '{print $4}'` - echo "ROOTDIR: $root" - echo "DATADIR: $dir/data/$1" - echo "LOGFILE: $dir/log/$1.log" -} - # live into about an instance function status { echo -n "account balance: " @@ -493,24 +353,6 @@ function peers { execute $1 'admin.peers' } -# add peers into an instance (connection not guaranteed) -function addpeers { - id=$1 - peers=$2 - if [ $id = "all" ]; then - N=`ls -1 -d $dir/data/* |wc -l` - for ((i=0;i