diff --git a/.gitattributes b/.gitattributes index e488d3e6b0..11bddac905 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,3 +12,4 @@ *.ttf binary *.woff binary *.woff2 binary +goss-linux-amd64 binary diff --git a/Jenkinsfile b/Jenkinsfile index 3584623b7f..c9c2185ae9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,7 +22,8 @@ if (env.BRANCH_NAME == "master") { ]) } -def docker_image = 'docker:18.06.0-ce-dind' +def docker_image_dind = 'docker:18.06.0-ce-dind' +def docker_image = 'docker:18.06.0-ce' def build_image = 'pegasyseng/pantheon-build:0.0.5-jdk11' def abortPreviousBuilds() { @@ -50,7 +51,7 @@ try { def stage_name = "Unit tests node: " node { checkout scm - docker.image(docker_image).withRun('--privileged') { d -> + docker.image(docker_image_dind).withRun('--privileged') { d -> docker.image(build_image).inside("--link ${d.id}:docker") { try { stage(stage_name + 'Prepare') { @@ -65,6 +66,8 @@ try { archiveArtifacts 'build/reports/**' archiveArtifacts 'build/distributions/**' + stash allowEmpty: true, includes: 'build/distributions/pantheon-*.tar.gz', name: 'distTarBall' + junit '**/build/test-results/**/*.xml' } } @@ -74,7 +77,7 @@ try { def stage_name = "Reference tests node: " node { checkout scm - docker.image(docker_image).withRun('--privileged') { d -> + docker.image(docker_image_dind).withRun('--privileged') { d -> docker.image(build_image).inside("--link ${d.id}:docker") { try { stage(stage_name + 'Prepare') { @@ -98,7 +101,7 @@ try { def stage_name = "Integration tests node: " node { checkout scm - docker.image(docker_image).withRun('--privileged') { d -> + docker.image(docker_image_dind).withRun('--privileged') { d -> docker.image(build_image).inside("--link ${d.id}:docker") { try { stage(stage_name + 'Prepare') { @@ -131,7 +134,7 @@ try { def stage_name = "Acceptance tests node: " node { checkout scm - docker.image(docker_image).withRun('--privileged') { d -> + docker.image(docker_image_dind).withRun('--privileged') { d -> docker.image(build_image).inside("--link ${d.id}:docker") { try { stage(stage_name + 'Prepare') { @@ -152,6 +155,35 @@ try { } } } + if (env.BRANCH_NAME == "master") { + node { + checkout scm + unstash 'distTarBall' + docker.image(docker_image_dind).withRun('--privileged') { d -> + docker.image(docker_image).inside("-e DOCKER_HOST=tcp://docker:2375 --link ${d.id}:docker") { + stage('build image') { + sh "cd docker && cp ../build/distributions/pantheon-*.tar.gz ." + pantheon = docker.build("pegasyseng/pantheon-develop:develop", "docker") + } + try { + stage('test image') { + sh "apk add bash" + sh "mkdir -p docker/reports" + sh "cd docker && bash test.sh pegasyseng/pantheon-develop:develop" + } + } finally { + junit 'docker/reports/*.xml' + sh "rm -rf docker/reports" + } + stage('push image') { + docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-pegasysengci') { + pantheon.push() + } + } + } + } + } + } } catch (e) { currentBuild.result = 'FAILURE' } finally { diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 0000000000..91b4176f64 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1 @@ +pantheon-*.tar.gz diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000..cee14d28a9 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,21 @@ +FROM openjdk:11.0.2-jre-slim-stretch + +COPY pantheon-*.tar.gz /tmp/. +RUN tar xzf /tmp/pantheon-*.tar.gz -C /tmp && \ + rm /tmp/pantheon-*.tar.gz && \ + mv /tmp/pantheon-* /opt/pantheon + + +RUN mkdir /var/lib/pantheon +RUN mkdir /etc/pantheon/ +COPY entrypoint.sh /opt/pantheon/pantheon-entrypoint.sh +RUN chmod +x /opt/pantheon/pantheon-entrypoint.sh + +WORKDIR /var/lib/pantheon +VOLUME ["/var/lib/pantheon"] + +EXPOSE 8545 8546 30303 + +ENV PANTHEON_OPTS="-Dpantheon.docker=true" + +ENTRYPOINT ["/opt/pantheon/pantheon-entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000000..53f780b3ce --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,205 @@ +#!/bin/bash + +set -e + +if ! { [ "${1#-}" != "$1" ] || [ "$#" == 0 ] || [ "$1" == "blocks" ] || [ "$1" == "public-key" ] || [ "$1" == "password" ]; }; then + exec "$@" +fi + +p2plistenset=false +rpclistenset=false +wslistenset=false + +for i in "$@"; do + case "$i" in + --rpc-http-host) rpclistenset=true ;; + --rpc-http-host=*) rpclistenset=true ;; + --rpc-http-port) rpclistenset=true ;; + --rpc-http-port=*) rpclistenset=true ;; + --rpc-ws-host) wslistenset=true ;; + --rpc-ws-host=*) wslistenset=true ;; + --rpc-ws-port) wslistenset=true ;; + --rpc-ws-port=*) wslistenset=true ;; + --p2p-host) p2plistenset=true ;; + --p2p-host=*) p2plistenset=true ;; + --p2p-port) p2plistenset=true ;; + --p2p-port=*) p2plistenset=true ;; + esac +done + +if $p2plistenset ; then + echo "ERROR: p2p host and port cannot be set by argument under docker, define your custom port by mapping it into the containers 30303 port" + exit 1 +else + set -- "--p2p-host=0.0.0.0" "$@" + set -- "--p2p-port=30303" "$@" +fi + +if $rpclistenset ; then + echo "ERROR: rpc http host and port cannot be set by argument under docker, define your custom port by mapping it into the containers 8545 port" + exit 1 +else + set -- "--rpc-http-host=0.0.0.0" "$@" + set -- "--rpc-http-port=8545" "$@" +fi + +if $wslistenset ; then + echo "ERROR: rpc ws host and port cannot be set by argument under docker, define your custom port by mapping it into the containers 8546 port" + exit 1 +else + set -- "--rpc-ws-host=0.0.0.0" "$@" + set -- "--rpc-ws-port=8546" "$@" +fi + +if [ "$P2P_ENABLED" == "false" ] || [ "$P2P_ENABLED" == "0" ]; then + set -- "--p2p-enabled=false" "$@" +fi + +if [ "$DISCOVERY_ENABLED" == "false" ] || [ "$DISCOVERY_ENABLED" == "0" ]; then + set -- "--discovery-enabled=false" "$@" +fi + +if [[ ! -z "$BOOTNODES" ]]; then + set -- "--bootnodes=$BOOTNODES" "$@" +fi + +if [[ ! -z "$MAX_PEERS" ]]; then + set -- "$@" "--max-peers=$MAX_PEERS" +fi + +if [[ ! -z "$BANNED_NODE_IDS" ]]; then + set -- "--banned-node-ids=$BANNED_NODE_IDS" "$@" +fi + +if [[ ! -z "$BANNED_NODE_ID" ]]; then + set -- "--banned-node-id=$BANNED_NODE_ID" "$@" +fi + +if [[ ! -z "$SYNC_MODE" ]]; then + set -- "--sync-mode=$SYNC_MODE" "$@" +fi + +if [[ ! -z "$NETWORK" ]]; then + set -- "--network=$NETWORK" "$@" +fi + +if [[ ! -z "$NETWORK_ID" ]]; then + set -- "--network-id=$NETWORK_ID" "$@" +fi + +if [ "$RPC_HTTP_ENABLED" == "true" ] || [ "$RPC_HTTP_ENABLED" == "1" ]; then + set -- "--rpc-http-enabled=true" "$@" +fi + +if [[ ! -z "$RPC_HTTP_CORS_ORIGINS" ]]; then + set -- "--rpc-http-cors-origins=$RPC_HTTP_CORS_ORIGINS" "$@" +fi + +if [[ ! -z "$RPC_HTTP_API" ]]; then + set -- "--rpc-http-api=$RPC_HTTP_API" "$@" +fi + +if [[ ! -z "$RPC_HTTP_APIS" ]]; then + set -- "--rpc-http-apis=$RPC_HTTP_APIS" "$@" +fi + +if [ "$RPC_WS_ENABLED" == "true" ] || [ "$RPC_WS_ENABLED" == "1" ]; then + set -- "--rpc-ws-enabled=true" "$@" +fi + +if [[ ! -z "$RPC_WS_API" ]]; then + set -- "--rpc-ws-api=$RPC_WS_API" "$@" +fi + +if [[ ! -z "$RPC_WS_APIS" ]]; then + set -- "--rpc-ws-apis=$RPC_WS_APIS" "$@" +fi + +if [[ ! -z "$RPC_WS_REFRESH_DELAY" ]]; then + set -- "--rpc-ws-refresh_delay=$RPC_WS_REFRESH_DELAY" "$@" +fi + +if [ "$METRICS_ENABLED" == "true" ] || [ "$METRICS_ENABLED" == "1" ]; then + set -- "--metrics-enabled=true" "$@" +fi + +if [[ ! -z "$METRICS_HOST" ]]; then + set -- "--metrics-host=$METRICS_HOST" "$@" +fi + +if [[ ! -z "$METRICS_PORT" ]]; then + set -- "--metrics-port=$METRICS_PORT" "$@" +fi + +if [ "$METRICS_PUSH_ENABLED" == "true" ] || [ "$METRICS_PUSH_ENABLED" == "1" ]; then + set -- "--metrics-push-enabled=true" "$@" +fi + +if [[ ! -z "$METRICS_PUSH_INTERVAL" ]]; then + set -- "--metrics-push-interval=$METRICS_PUSH_INTERVAL" "$@" +fi + +if [[ ! -z "$METRICS_PUSH_HOST" ]]; then + set -- "--metrics-push-host=$METRICS_PUSH_HOST" "$@" +fi + +if [[ ! -z "$METRICS_PUSH_PORT" ]]; then + set -- "--metrics-push-port=$METRICS_PUSH_PORT" "$@" +fi + + +if [[ ! -z "$METRICS_PUSH_PROMETHEUS_JOB" ]]; then + set -- "--metrics-push-prometheus-job=$METRICS_PUSH_PROMETHEUS_JOB" "$@" +fi + +if [[ ! -z "$HOST_WHITELIST" ]]; then + set -- "--host-whitelist=$HOST_WHITELIST" "$@" +fi + +if [[ ! -z "$LOGGING" ]]; then + set -- "--logging=$LOGGING" "$@" +fi + +if [ "$MINER_ENABLED" == "true" ] || [ "$MINER_ENABLED" == "1" ]; then + set -- "--miner-enabled=true" "$@" +fi + +if [[ ! -z "$MINER_COINBASE" ]]; then + set -- "--miner-coinbase=$MINER_COINBASE" "$@" +fi + +if [[ ! -z "$MIN_GAS_PRICE" ]]; then + set -- "--min-gas-price=$MIN_GAS_PRICE" "$@" +fi + +if [[ ! -z "$MINER_EXTRA_DATA" ]]; then + set -- "--miner-extra-data=$MINER_EXTRA_DATA" "$@" +fi + +if [ "$PERMISSIONS_NODES_ENABLED" == "true" ] || [ "$PERMISSIONS_NODES_ENABLED" == "1" ]; then + set -- "--permissions_nodes_enabled=true" "$@" +fi + +if [ "$PERMISSIONS_ACCOUNTS_ENABLED" == "true" ] || [ "$PERMISSIONS_ACCOUNTS_ENABLED" == "1" ]; then + set -- "--permissions_accounts_enabled=true" "$@" +fi + +if [ "$PRIVACY_ENABLED" == "true" ] || [ "$PRIVACY_ENABLED" == "1" ]; then + set -- "--privacy-enabled=true" "$@" +fi + +if [[ ! -z "$PRIVACY_URL" ]]; then + set -- "--privacy-url=$PRIVACY_URL" "$@" +fi + +if [[ ! -z "$PRIVACY_PUBLIC_KEY_FILE" ]]; then + set -- "--privacy-public-key-file=$PRIVACY_PUBLIC_KEY_FILE" "$@" +fi + +if [[ ! -z "$PRIVACY_PRECOMPILED_ADDRESS" ]]; then + set -- "--privacy-precompiled-address=$PRIVACY_PRECOMPILED_ADDRESS" "$@" +fi + +set -- "/opt/pantheon/bin/pantheon" "$@" + +exec "$@" diff --git a/docker/test.sh b/docker/test.sh new file mode 100755 index 0000000000..d3c0ee92d4 --- /dev/null +++ b/docker/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +export GOSS_PATH=tests/goss-linux-amd64 +export GOSS_OPTS="$GOSS_OPTS --format junit" +export GOSS_FILES_STRATEGY=cp +DOCKER_IMAGE=$1 + +i=0 + +# Test for normal unconfigured startup +GOSS_FILES_PATH=tests/01 bash tests/dgoss run $DOCKER_IMAGE > ./reports/01.xml || i=`expr $i + 1` + +exit $i diff --git a/docker/tests/01/goss.yaml b/docker/tests/01/goss.yaml new file mode 100644 index 0000000000..b66b8c2ce1 --- /dev/null +++ b/docker/tests/01/goss.yaml @@ -0,0 +1,65 @@ +file: + /etc/pantheon: + exists: true + mode: "0755" + owner: root + group: root + filetype: directory + contains: [] + /opt/pantheon/bin/pantheon: + exists: true + mode: "0755" + owner: root + group: root + filetype: file + contains: [] + /opt/pantheon/pantheon-entrypoint.sh: + exists: true + mode: "0755" + owner: root + group: root + filetype: file + contains: [] + /tmp/pantheon.tar.gz: + exists: false + contains: [] + /var/lib/pantheon: + exists: true + mode: "0755" + owner: root + group: root + filetype: directory + contains: [] + /var/lib/pantheon/database: + exists: true + mode: "0755" + owner: root + group: root + filetype: directory + contains: [] + /var/lib/pantheon/key: + exists: true + mode: "0600" + owner: root + group: root + filetype: file + contains: [] +package: + libc6: + installed: true +port: + tcp:8545: + listening: false + tcp:8546: + listening: false + tcp:30303: + listening: true + ip: + - 0.0.0.0 + udp:30303: + listening: true + ip: + - 0.0.0.0 +process: + java: + running: true diff --git a/docker/tests/01/goss_wait.yaml b/docker/tests/01/goss_wait.yaml new file mode 100644 index 0000000000..1d99c358ba --- /dev/null +++ b/docker/tests/01/goss_wait.yaml @@ -0,0 +1,5 @@ +port: + tcp:30303: + listening: true + ip: + - 0.0.0.0 diff --git a/docker/tests/dgoss b/docker/tests/dgoss new file mode 100755 index 0000000000..e2e06174e1 --- /dev/null +++ b/docker/tests/dgoss @@ -0,0 +1,113 @@ +#!/bin/bash + +set -e + +USAGE="USAGE: $(basename "$0") [run|edit] " +GOSS_FILES_PATH="${GOSS_FILES_PATH:-.}" + +info() { + echo -e "INFO: $*" >&2; +} +error() { + echo -e "ERROR: $*" >&2; + exit 1; +} + +cleanup() { + set +e + { kill "$log_pid" && wait "$log_pid"; } 2> /dev/null + rm -rf "$tmp_dir" + if [[ $id ]];then + info "Deleting container" + docker rm -vf "$id" > /dev/null + fi +} + +run(){ + # Copy in goss + cp "${GOSS_PATH}" "$tmp_dir/goss" + chmod 755 "$tmp_dir/goss" + [[ -e "${GOSS_FILES_PATH}/goss.yaml" ]] && cp "${GOSS_FILES_PATH}/goss.yaml" "$tmp_dir" + [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]] && cp "${GOSS_FILES_PATH}/goss_wait.yaml" "$tmp_dir" + [[ ! -z "${GOSS_VARS}" ]] && [[ -e "${GOSS_FILES_PATH}/${GOSS_VARS}" ]] && cp "${GOSS_FILES_PATH}/${GOSS_VARS}" "$tmp_dir" + + # Switch between mount or cp files strategy + GOSS_FILES_STRATEGY=${GOSS_FILES_STRATEGY:="mount"} + case "$GOSS_FILES_STRATEGY" in + mount) + info "Starting docker container" + id=$(docker run -d -v "$tmp_dir:/goss:z" "${@:2}") + docker logs -f "$id" > "$tmp_dir/docker_output.log" 2>&1 & + ;; + cp) + info "Creating docker container" + id=$(docker create ${@:2}) + info "Copy goss files into container" + docker cp $tmp_dir/. $id:/goss + info "Starting docker container" + docker start $id > /dev/null + ;; + *) error "Wrong goss files strategy used! Correct options are \"mount\" or \"cp\"." + esac + + log_pid=$! + info "Container ID: ${id:0:8}" +} + +get_docker_file() { + if docker exec "$id" sh -c "test -e $1" > /dev/null;then + mkdir -p "${GOSS_FILES_PATH}" + info "Copied '$1' from container to '${GOSS_FILES_PATH}'" + docker cp "$id:$1" "${GOSS_FILES_PATH}" + fi +} + +# Main +tmp_dir=$(mktemp -d /tmp/tmp.XXXXXXXXXX) +chmod 777 "$tmp_dir" +trap 'ret=$?;cleanup;exit $ret' EXIT + +GOSS_PATH="${GOSS_PATH:-$(which goss 2> /dev/null || true)}" +[[ $GOSS_PATH ]] || { error "Couldn't find goss installation, please set GOSS_PATH to it"; } +[[ ${GOSS_OPTS+x} ]] || GOSS_OPTS="--color --format documentation" +[[ ${GOSS_WAIT_OPTS+x} ]] || GOSS_WAIT_OPTS="-r 30s -s 1s > /dev/null" +GOSS_SLEEP=${GOSS_SLEEP:-0.2} + +case "$1" in + run) + run "$@" + if [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]]; then + info "Found goss_wait.yaml, waiting for it to pass before running tests" + if [[ -z "${GOSS_VARS}" ]]; then + if ! docker exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml validate $GOSS_WAIT_OPTS"; then + error "goss_wait.yaml never passed" + fi + else + if ! docker exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_WAIT_OPTS"; then + error "goss_wait.yaml never passed" + fi + fi + fi + [[ $GOSS_SLEEP ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; } + # info "Container health" + # if ! docker top $id; then + # docker logs $id + # fi + info "Running Tests" + if [[ -z "${GOSS_VARS}" ]]; then + docker exec "$id" sh -c "/goss/goss -g /goss/goss.yaml validate $GOSS_OPTS" + else + docker exec "$id" sh -c "/goss/goss -g /goss/goss.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_OPTS" + fi + ;; + edit) + run "$@" + info "Run goss add/autoadd to add resources" + docker exec -it "$id" sh -c 'cd /goss; PATH="/goss:$PATH" exec sh' + get_docker_file "/goss/goss.yaml" + get_docker_file "/goss/goss_wait.yaml" + [[ ! -z "${GOSS_VARS}" ]] && get_docker_file "/goss/${GOSS_VARS}" + ;; + *) + error "$USAGE" +esac diff --git a/docker/tests/goss-linux-amd64 b/docker/tests/goss-linux-amd64 new file mode 100644 index 0000000000..62f2325a29 Binary files /dev/null and b/docker/tests/goss-linux-amd64 differ