mirror of https://github.com/ethereum/go-ethereum
swarm/dev: add development environment (#14332)
This PR adds a Swarm development environment which can be run in a Docker container and provides scripts for building binaries and running Swarm clusters.pull/14567/head
parent
727eadacca
commit
0036e2a747
@ -0,0 +1,2 @@ |
||||
bin/* |
||||
cluster/* |
@ -0,0 +1,2 @@ |
||||
bin/* |
||||
cluster/* |
@ -0,0 +1,42 @@ |
||||
FROM ubuntu:xenial |
||||
|
||||
# install build + test dependencies |
||||
RUN apt-get update && \ |
||||
apt-get install --yes --no-install-recommends \ |
||||
ca-certificates \ |
||||
curl \ |
||||
fuse \ |
||||
g++ \ |
||||
gcc \ |
||||
git \ |
||||
iproute2 \ |
||||
iputils-ping \ |
||||
less \ |
||||
libc6-dev \ |
||||
make \ |
||||
pkg-config \ |
||||
&& \ |
||||
apt-get clean |
||||
|
||||
# install Go |
||||
ENV GO_VERSION 1.8.1 |
||||
RUN curl -fSLo golang.tar.gz "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" && \ |
||||
tar -xzf golang.tar.gz -C /usr/local && \ |
||||
rm golang.tar.gz |
||||
ENV GOPATH /go |
||||
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH |
||||
|
||||
# install docker CLI |
||||
RUN curl -fSLo docker.tar.gz https://get.docker.com/builds/Linux/x86_64/docker-17.04.0-ce.tgz && \ |
||||
tar -xzf docker.tar.gz -C /usr/local/bin --strip-components=1 docker/docker && \ |
||||
rm docker.tar.gz |
||||
|
||||
# install jq |
||||
RUN curl -fSLo /usr/local/bin/jq https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 && \ |
||||
chmod +x /usr/local/bin/jq |
||||
|
||||
# install govendor |
||||
RUN go get -u github.com/kardianos/govendor |
||||
|
||||
# add custom bashrc |
||||
ADD bashrc /root/.bashrc |
@ -0,0 +1,14 @@ |
||||
.PHONY: build cluster test |
||||
|
||||
default: build |
||||
|
||||
build: |
||||
go build -o bin/swarm github.com/ethereum/go-ethereum/cmd/swarm
|
||||
go build -o bin/geth github.com/ethereum/go-ethereum/cmd/geth
|
||||
go build -o bin/bootnode github.com/ethereum/go-ethereum/cmd/bootnode
|
||||
|
||||
cluster: build |
||||
scripts/boot-cluster.sh
|
||||
|
||||
test: |
||||
go test -v github.com/ethereum/go-ethereum/swarm/...
|
@ -0,0 +1,20 @@ |
||||
Swarm development environment |
||||
============================= |
||||
|
||||
The Swarm development environment is a Linux bash shell which can be run in a |
||||
Docker container and provides a predictable build and test environment. |
||||
|
||||
### Start the Docker container |
||||
|
||||
Run the `run.sh` script to build the Docker image and run it, you will then be |
||||
at a bash prompt inside the `swarm/dev` directory. |
||||
|
||||
### Build binaries |
||||
|
||||
Run `make` to build the `swarm`, `geth` and `bootnode` binaries into the |
||||
`swarm/dev/bin` directory. |
||||
|
||||
### Boot a cluster |
||||
|
||||
Run `make cluster` to start a 3 node Swarm cluster, or run |
||||
`scripts/boot-cluster.sh --size N` to boot a cluster of size N. |
@ -0,0 +1,21 @@ |
||||
export ROOT="${GOPATH}/src/github.com/ethereum/go-ethereum" |
||||
export PATH="${ROOT}/swarm/dev/bin:${PATH}" |
||||
|
||||
cd "${ROOT}/swarm/dev" |
||||
|
||||
cat <<WELCOME |
||||
|
||||
============================================= |
||||
|
||||
Welcome to the swarm development environment. |
||||
|
||||
- Run 'make' to build the swarm, geth and bootnode binaries |
||||
- Run 'make test' to run the swarm unit tests |
||||
- Run 'make cluster' to start a swarm cluster |
||||
- Run 'exit' to exit the development environment |
||||
|
||||
See the 'scripts' directory for some useful scripts. |
||||
|
||||
============================================= |
||||
|
||||
WELCOME |
@ -0,0 +1,90 @@ |
||||
#!/usr/bin/env bash |
||||
# |
||||
# A script to build and run the Swarm development environment using Docker. |
||||
|
||||
set -e |
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)" |
||||
|
||||
# DEFAULT_NAME is the default name for the Docker image and container |
||||
DEFAULT_NAME="swarm-dev" |
||||
|
||||
usage() { |
||||
cat >&2 <<USAGE |
||||
usage: $0 [options] |
||||
|
||||
Build and run the Swarm development environment. |
||||
|
||||
Depends on Docker being installed locally. |
||||
|
||||
OPTIONS: |
||||
-n, --name NAME Docker image and container name [default: ${DEFAULT_NAME}] |
||||
-d, --docker-args ARGS Custom args to pass to 'docker run' (e.g. '-p 8000:8000' to expose a port) |
||||
-h, --help Show this message |
||||
USAGE |
||||
} |
||||
|
||||
main() { |
||||
local name="${DEFAULT_NAME}" |
||||
local docker_args="" |
||||
parse_args "$@" |
||||
build_image |
||||
run_image |
||||
} |
||||
|
||||
parse_args() { |
||||
while true; do |
||||
case "$1" in |
||||
-h | --help) |
||||
usage |
||||
exit 0 |
||||
;; |
||||
-n | --name) |
||||
if [[ -z "$2" ]]; then |
||||
echo "ERROR: --name flag requires an argument" >&2 |
||||
exit 1 |
||||
fi |
||||
name="$2" |
||||
shift 2 |
||||
;; |
||||
-d | --docker-args) |
||||
if [[ -z "$2" ]]; then |
||||
echo "ERROR: --docker-args flag requires an argument" >&2 |
||||
exit 1 |
||||
fi |
||||
docker_args="$2" |
||||
shift 2 |
||||
;; |
||||
*) |
||||
break |
||||
;; |
||||
esac |
||||
done |
||||
|
||||
if [[ $# -ne 0 ]]; then |
||||
usage |
||||
echo "ERROR: invalid arguments" >&2 |
||||
exit 1 |
||||
fi |
||||
} |
||||
|
||||
build_image() { |
||||
docker build --tag "${name}" "${ROOT}/swarm/dev" |
||||
} |
||||
|
||||
run_image() { |
||||
exec docker run \ |
||||
--privileged \ |
||||
--interactive \ |
||||
--tty \ |
||||
--rm \ |
||||
--hostname "${name}" \ |
||||
--name "${name}" \ |
||||
--volume "${ROOT}:/go/src/github.com/ethereum/go-ethereum" \ |
||||
--volume "/var/run/docker.sock:/var/run/docker.sock" \ |
||||
${docker_args} \ |
||||
"${name}" \ |
||||
/bin/bash |
||||
} |
||||
|
||||
main "$@" |
@ -0,0 +1,288 @@ |
||||
#!/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}" |
||||
--ethapi "${GETH_RPC_URL}" |
||||
--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 "$@" |
@ -0,0 +1,96 @@ |
||||
#!/bin/bash |
||||
# |
||||
# A script to upload random data to a swarm cluster. |
||||
# |
||||
# Example: |
||||
# |
||||
# random-uploads.sh --addr 192.168.33.101:8500 --size 40k --count 1000 |
||||
|
||||
set -e |
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" |
||||
source "${ROOT}/swarm/dev/scripts/util.sh" |
||||
|
||||
DEFAULT_ADDR="localhost:8500" |
||||
DEFAULT_UPLOAD_SIZE="40k" |
||||
DEFAULT_UPLOAD_COUNT="1000" |
||||
|
||||
usage() { |
||||
cat >&2 <<USAGE |
||||
usage: $0 [options] |
||||
|
||||
Upload random data to a Swarm cluster. |
||||
|
||||
OPTIONS: |
||||
-a, --addr ADDR Swarm API address [default: ${DEFAULT_ADDR}] |
||||
-s, --size SIZE Individual upload size [default: ${DEFAULT_UPLOAD_SIZE}] |
||||
-c, --count COUNT Number of uploads [default: ${DEFAULT_UPLOAD_COUNT}] |
||||
-h, --help Show this message |
||||
USAGE |
||||
} |
||||
|
||||
main() { |
||||
local addr="${DEFAULT_ADDR}" |
||||
local upload_size="${DEFAULT_UPLOAD_SIZE}" |
||||
local upload_count="${DEFAULT_UPLOAD_COUNT}" |
||||
|
||||
parse_args "$@" |
||||
|
||||
info "uploading ${upload_count} ${upload_size} random files to ${addr}" |
||||
|
||||
for i in $(seq 1 ${upload_count}); do |
||||
info "upload ${i} / ${upload_count}:" |
||||
do_random_upload |
||||
echo |
||||
done |
||||
} |
||||
|
||||
do_random_upload() { |
||||
curl -fsSL -X POST --data-binary "$(random_data)" "http://${addr}/bzzr:/" |
||||
} |
||||
|
||||
random_data() { |
||||
dd if=/dev/urandom of=/dev/stdout bs="${upload_size}" count=1 2>/dev/null |
||||
} |
||||
|
||||
parse_args() { |
||||
while true; do |
||||
case "$1" in |
||||
-h | --help) |
||||
usage |
||||
exit 0 |
||||
;; |
||||
-a | --addr) |
||||
if [[ -z "$2" ]]; then |
||||
fail "--addr flag requires an argument" |
||||
fi |
||||
addr="$2" |
||||
shift 2 |
||||
;; |
||||
-s | --size) |
||||
if [[ -z "$2" ]]; then |
||||
fail "--size flag requires an argument" |
||||
fi |
||||
upload_size="$2" |
||||
shift 2 |
||||
;; |
||||
-c | --count) |
||||
if [[ -z "$2" ]]; then |
||||
fail "--count flag requires an argument" |
||||
fi |
||||
upload_count="$2" |
||||
shift 2 |
||||
;; |
||||
*) |
||||
break |
||||
;; |
||||
esac |
||||
done |
||||
|
||||
if [[ $# -ne 0 ]]; then |
||||
usage |
||||
fail "ERROR: invalid arguments: $@" |
||||
fi |
||||
} |
||||
|
||||
main "$@" |
@ -0,0 +1,98 @@ |
||||
#!/bin/bash |
||||
# |
||||
# A script to shutdown a dev swarm cluster. |
||||
|
||||
set -e |
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" |
||||
source "${ROOT}/swarm/dev/scripts/util.sh" |
||||
|
||||
DEFAULT_BASE_DIR="${ROOT}/swarm/dev/cluster" |
||||
|
||||
usage() { |
||||
cat >&2 <<USAGE |
||||
usage: $0 [options] |
||||
|
||||
Shutdown a dev swarm cluster. |
||||
|
||||
OPTIONS: |
||||
-d, --dir DIR Base directory [default: ${DEFAULT_BASE_DIR}] |
||||
-h, --help Show this message |
||||
USAGE |
||||
} |
||||
|
||||
main() { |
||||
local base_dir="${DEFAULT_BASE_DIR}" |
||||
|
||||
parse_args "$@" |
||||
|
||||
local pid_dir="${base_dir}/pids" |
||||
|
||||
stop_swarm_nodes |
||||
stop_node "geth" |
||||
stop_node "bootnode" |
||||
delete_network |
||||
} |
||||
|
||||
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 |
||||
;; |
||||
*) |
||||
break |
||||
;; |
||||
esac |
||||
done |
||||
|
||||
if [[ $# -ne 0 ]]; then |
||||
usage |
||||
fail "ERROR: invalid arguments: $@" |
||||
fi |
||||
} |
||||
|
||||
stop_swarm_nodes() { |
||||
for name in $(ls "${pid_dir}" | grep -oP 'swarm\d+'); do |
||||
stop_node "${name}" |
||||
done |
||||
} |
||||
|
||||
stop_node() { |
||||
local name=$1 |
||||
local pid_file="${pid_dir}/${name}.pid" |
||||
|
||||
if [[ -e "${pid_file}" ]]; then |
||||
info "stopping ${name}" |
||||
start-stop-daemon \ |
||||
--stop \ |
||||
--pidfile "${pid_file}" \ |
||||
--remove-pidfile \ |
||||
--oknodo \ |
||||
--retry 15 |
||||
fi |
||||
|
||||
if ip netns list | grep -qF "${name}"; then |
||||
ip netns delete "${name}" |
||||
fi |
||||
|
||||
if ip link show "veth${name}0" &>/dev/null; then |
||||
ip link delete dev "veth${name}0" |
||||
fi |
||||
} |
||||
|
||||
delete_network() { |
||||
if ip link show "swarmbr0" &>/dev/null; then |
||||
ip link delete dev "swarmbr0" |
||||
fi |
||||
} |
||||
|
||||
main "$@" |
@ -0,0 +1,53 @@ |
||||
# shared shell functions |
||||
|
||||
info() { |
||||
local msg="$@" |
||||
local timestamp="$(date +%H:%M:%S)" |
||||
say "===> ${timestamp} ${msg}" "green" |
||||
} |
||||
|
||||
warn() { |
||||
local msg="$@" |
||||
local timestamp=$(date +%H:%M:%S) |
||||
say "===> ${timestamp} WARN: ${msg}" "yellow" >&2 |
||||
} |
||||
|
||||
fail() { |
||||
local msg="$@" |
||||
say "ERROR: ${msg}" "red" >&2 |
||||
exit 1 |
||||
} |
||||
|
||||
# say prints the given message to STDOUT, using the optional color if |
||||
# STDOUT is a terminal. |
||||
# |
||||
# usage: |
||||
# |
||||
# say "foo" - prints "foo" |
||||
# say "bar" "red" - prints "bar" in red |
||||
# say "baz" "green" - prints "baz" in green |
||||
# say "qux" "red" | tee - prints "qux" with no colour |
||||
# |
||||
say() { |
||||
local msg=$1 |
||||
local color=$2 |
||||
|
||||
if [[ -n "${color}" ]] && [[ -t 1 ]]; then |
||||
case "${color}" in |
||||
red) |
||||
echo -e "\033[1;31m${msg}\033[0m" |
||||
;; |
||||
green) |
||||
echo -e "\033[1;32m${msg}\033[0m" |
||||
;; |
||||
yellow) |
||||
echo -e "\033[1;33m${msg}\033[0m" |
||||
;; |
||||
*) |
||||
echo "${msg}" |
||||
;; |
||||
esac |
||||
else |
||||
echo "${msg}" |
||||
fi |
||||
} |
Loading…
Reference in new issue