Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[testing] Add scheduled jobs to test bootstrap from mainnet and testnet #3172

Closed
wants to merge 11 commits into from
2 changes: 2 additions & 0 deletions .github/actionlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ self-hosted-runner:
labels:
- custom-arm64-focal
- custom-arm64-jammy
- net-outage-sim
- avalanche-avalanchego
48 changes: 48 additions & 0 deletions .github/actions/check-bootstrap/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: 'Check bootstrap for a network and state sync configuration'
description: 'Checks that bootstrap is possible for the given network and state sync configuration'

inputs:
network_id:
required: true
state_sync_enabled:
required: true
prometheus_id:
required: true
prometheus_password:
required: true
loki_id:
required: true
loki_password:
required: true

runs:
using: composite
steps:
- name: Setup Go
uses: ./.github/actions/setup-go-for-project

- name: Build AvalancheGo Binary
shell: bash
run: ./scripts/build.sh -r

- name: Check avalanchego version
shell: bash
run: ./build/avalanchego --version

- name: Run bootstrap for testnet with state-sync
uses: ./.github/actions/run-monitored-tmpnet-cmd
with:
run: go run ./tests/bootstrap --avalanchego-path=./build/avalanchego --network-id=${{ inputs.network_id }} --state-sync-enabled=${{ inputs.state_sync_enabled }}
prometheus_id: ${{ inputs.prometheus_id }}
prometheus_password: ${{ inputs.prometheus_password }}
loki_id: ${{ inputs.loki_id }}
loki_password: ${{ inputs.loki_password }}

# Skip creation of an artifact in favor of log collection to loki

- name: Check size of tmpnet path
if: always()
shell: bash
run: |
echo "Checking tmpnet disk usage:"
du -sh ~/.tmpnet
23 changes: 23 additions & 0 deletions .github/workflows/check-bootstrap-mainnet-full-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: 'Check Bootstrap (mainnet,full-sync)'

on:
# TODO(marun) Add a schedule
workflow_dispatch:

jobs:
check_bootstrap_mainnet_full_sync:
name: Check Bootstrap (mainnet,full-sync)
runs-on: avalanche-avalanchego
timeout-minutes: 7200 # 5 days, maximum allowed for jobs running on a self-hosted runner
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Check bootstrap (mainnet,full-sync)
uses: ./.github/actions/check-bootstrap
with:
network_id: 1 # mainnet
state_sync_enabled: false
prometheus_id: ${{ secrets.PROMETHEUS_ID || '' }}
prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
loki_id: ${{ secrets.LOKI_ID || '' }}
loki_password: ${{ secrets.LOKI_PASSWORD || '' }}
23 changes: 23 additions & 0 deletions .github/workflows/check-bootstrap-mainnet-state-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: 'Check Bootstrap (mainnet,state-sync)'

on:
# TODO(marun) Add a schedule
workflow_dispatch:

jobs:
check_bootstrap_mainnet_state_sync:
name: Check Bootstrap (mainnet,state-sync)
runs-on: avalanche-avalanchego
timeout-minutes: 7200 # 5 days, maximum allowed for jobs running on a self-hosted runner
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Check bootstrap (mainnet,state-sync)
uses: ./.github/actions/check-bootstrap
with:
network_id: 1 # mainnet
state_sync_enabled: true
prometheus_id: ${{ secrets.PROMETHEUS_ID || '' }}
prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
loki_id: ${{ secrets.LOKI_ID || '' }}
loki_password: ${{ secrets.LOKI_PASSWORD || '' }}
23 changes: 23 additions & 0 deletions .github/workflows/check-bootstrap-testnet-full-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: 'Check Bootstrap (testnet,full-sync)'

on:
# TODO(marun) Add a schedule
workflow_dispatch:

jobs:
check_bootstrap_testnet_full_sync:
name: Check Bootstrap (testnet,full-sync)
runs-on: avalanche-avalanchego
timeout-minutes: 7200 # 5 days, maximum allowed for jobs running on a self-hosted runner
abi87 marked this conversation as resolved.
Show resolved Hide resolved
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Check bootstrap (testnet,full-sync)
uses: ./.github/actions/check-bootstrap
with:
network_id: 5 # testnet
state_sync_enabled: false
prometheus_id: ${{ secrets.PROMETHEUS_ID || '' }}
prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
loki_id: ${{ secrets.LOKI_ID || '' }}
loki_password: ${{ secrets.LOKI_PASSWORD || '' }}
23 changes: 23 additions & 0 deletions .github/workflows/check-bootstrap-testnet-state-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: 'Check Bootstrap (testnet,state-sync)'

on:
# TODO(marun) Add a schedule
workflow_dispatch:

jobs:
check_bootstrap_testnet_state_sync:
name: Check Bootstrap (testnet,state-sync)
runs-on: avalanche-avalanchego
timeout-minutes: 4320 # 3 days
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Check bootstrap (testnet,state-sync)
uses: ./.github/actions/check-bootstrap
with:
network_id: 5 # testnet
state_sync_enabled: true
prometheus_id: ${{ secrets.PROMETHEUS_ID || '' }}
prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
loki_id: ${{ secrets.LOKI_ID || '' }}
loki_password: ${{ secrets.LOKI_PASSWORD || '' }}
6 changes: 6 additions & 0 deletions .github/workflows/publish_docker_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@ jobs:
DOCKER_PASS: ${{ secrets.docker_pass }}
DOCKER_IMAGE: ${{ secrets.docker_repo }}
run: scripts/build_image.sh
- name: Build and publish boostrap tester image to DockerHub
env:
DOCKER_USERNAME: ${{ secrets.docker_username }}
DOCKER_PASS: ${{ secrets.docker_pass }}
IMAGE_PREFIX: avaplatform
run: bash -x scripts/build_bootstrap_tester_image.sh
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/compose-spec/compose-go v1.20.2
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0
github.com/ethereum/go-ethereum v1.13.8
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/btree v1.1.2
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
Expand Down
13 changes: 13 additions & 0 deletions scripts/build_bootstrap_tester.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

set -euo pipefail

if ! [[ "$0" =~ scripts/build_bootstrap_tester.sh ]]; then
echo "must be run from repository root"
exit 255
fi

source ./scripts/constants.sh

echo "Building bootstrap tester..."
go build -o ./build/bootstrap-tester ./tests/bootstrap/
57 changes: 57 additions & 0 deletions scripts/build_bootstrap_tester_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env bash

set -euo pipefail

# Directory above this script
AVALANCHE_PATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd .. && pwd )

source ./scripts/constants.sh

IMAGE_NAME="bootstrap-tester"

IMAGE_TAG="${IMAGE_TAG:-}"
if [[ -z "${IMAGE_TAG}" ]]; then
# Default to tagging with the commit hash
IMAGE_TAG="${commit_hash}"
fi

# Build the avalanchego image
DOCKER_CMD="docker buildx build"

# TODO(marun) Figure out how best to support this in CI and locally
PLATFORMS="${PLATFORMS:-}"

# Build the avalanchego image local-only
PLATFORMS="${PLATFORMS}" IMAGE_PREFIX= ./scripts/build_image.sh
AVALANCHEGO_NODE_IMAGE="avalanchego:${IMAGE_TAG}"

# Specifying an image prefix will ensure the image is pushed after build
IMAGE_PREFIX="${IMAGE_PREFIX:-}"
if [[ -n "${IMAGE_PREFIX}" ]]; then
IMAGE_NAME="${IMAGE_PREFIX}/${IMAGE_NAME}"
if [[ -n "${PLATFORMS}" ]]; then
DOCKER_CMD="${DOCKER_CMD} --platform=${PLATFORMS}"
fi
DOCKER_CMD="${DOCKER_CMD} --push"

# Tag the image as latest for the master branch
if [[ "${image_tag}" == "master" ]]; then
DOCKER_CMD="${DOCKER_CMD} -t ${IMAGE_NAME}:latest"
fi

# A populated DOCKER_USERNAME env var triggers login
if [[ -n "${DOCKER_USERNAME:-}" ]]; then
echo "$DOCKER_PASS" | docker login --username "$DOCKER_USERNAME" --password-stdin
fi
fi

# The dockerfiles don't specify the golang version to minimize the changes required to bump
# the version. Instead, the golang version is provided as an argument.
GO_VERSION="$(go list -m -f '{{.GoVersion}}')"

PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"

# Build the image for the bootstrap tester
${DOCKER_CMD} -t "${IMAGE_NAME}:${IMAGE_TAG}" \
--build-arg GO_VERSION="${GO_VERSION}" --build-arg AVALANCHEGO_NODE_IMAGE="${AVALANCHEGO_NODE_IMAGE}" \
-f "${AVALANCHE_PATH}/tests/bootstrap/Dockerfile" "${AVALANCHE_PATH}"
5 changes: 5 additions & 0 deletions scripts/build_image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ if [[ "${DOCKER_IMAGE}" == *"/"* ]]; then
echo "$DOCKER_PASS" | docker login --username "$DOCKER_USERNAME" --password-stdin
fi
else
PLATFORMS="${PLATFORMS:-}"
if [[ -n "${PLATFORMS}" ]]; then
DOCKER_CMD="${DOCKER_CMD} --platform=${PLATFORMS}"
fi

# Build a single-arch image since the image name does not include a slash which
# indicates that a registry is not available.
#
Expand Down
57 changes: 57 additions & 0 deletions tests/bootstrap/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# The version is supplied as a build argument rather than hard-coded
# to minimize the cost of version changes.
ARG GO_VERSION

# AVALANCHEGO_NODE_IMAGE needs to identify an existing node image and should include the tag
ARG AVALANCHEGO_NODE_IMAGE

# ============= Compilation Stage ================
# Always use the native platform to ensure fast builds
FROM --platform=$BUILDPLATFORM golang:$GO_VERSION-bullseye AS builder

WORKDIR /builder_workdir

ARG TARGETPLATFORM
ARG BUILDPLATFORM

# Configure a cross-compiler if the target platform differs from the build platform.
#
# build_env.sh is used to capture the environmental changes required by the build step since RUN
# environment state is not otherwise persistent.
# TODO(marun) Extract a common multi-platform builder from this and the avalanchego Dockerfile.
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ] && [ "$BUILDPLATFORM" != "linux/arm64" ]; then \
apt-get update && apt-get install -y gcc-aarch64-linux-gnu && \
echo "export CC=aarch64-linux-gnu-gcc" > ./build_env.sh \
; elif [ "$TARGETPLATFORM" = "linux/amd64" ] && [ "$BUILDPLATFORM" != "linux/amd64" ]; then \
apt-get update && apt-get install -y gcc-x86-64-linux-gnu && \
echo "export CC=x86_64-linux-gnu-gcc" > ./build_env.sh \
; else \
echo "export CC=gcc" > ./build_env.sh \
; fi

# Copy and download avalanche dependencies using go mod
COPY go.mod .
COPY go.sum .
RUN go mod download

# Copy the code into the container
COPY . .

# Ensure pre-existing builds are not available for inclusion in the final image
RUN [ -d ./build ] && rm -rf ./build/* || true

# Build tester binary
RUN . ./build_env.sh && \
echo "{CC=$CC, TARGETPLATFORM=$TARGETPLATFORM, BUILDPLATFORM=$BUILDPLATFORM}" && \
export GOARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) && \
./scripts/build_bootstrap_tester.sh

# ============= Cleanup Stage ================
FROM $AVALANCHEGO_NODE_IMAGE AS execution

COPY --from=builder /builder_workdir/build/bootstrap-tester /avalanchego/build/bootstrap-tester

# Clear the CMD set by the base image
CMD [ "" ]

ENTRYPOINT [ "/avalanchego/build/bootstrap-tester" ]
56 changes: 56 additions & 0 deletions tests/bootstrap/jwt/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"flag"
"fmt"
"io/ioutil"

Check failure on line 6 in tests/bootstrap/jwt/main.go

View workflow job for this annotation

GitHub Actions / Lint

import 'io/ioutil' is not allowed from list 'packages': io/ioutil is deprecated. Use package io or os instead. (depguard)
"log"
"time"

"github.com/golang-jwt/jwt/v4"
)

func main() {
// Define command-line flags
appID := flag.String("app-id", "", "GitHub App App ID")

Check failure on line 15 in tests/bootstrap/jwt/main.go

View workflow job for this annotation

GitHub Actions / Lint

Duplicate words (App) found (dupword)
privateKeyPath := flag.String("private-key-path", "", "Path to the GitHub App private key file")
expiryDuration := flag.Duration("expiry", 10*time.Minute, "JWT expiration duration (e.g., 10m, 1h)")

// Parse command-line flags
flag.Parse()

// Validate required flags
if *appID == "" || *privateKeyPath == "" {
log.Fatalf("Both app-id and private-key-path are required")

Check failure on line 24 in tests/bootstrap/jwt/main.go

View workflow job for this annotation

GitHub Actions / Lint

string-format: no format directive, use log.Fatal instead (revive)
}

// Read the private key file
privateKeyData, err := ioutil.ReadFile(*privateKeyPath)
if err != nil {
log.Fatalf("Error reading private key file: %v", err)
}

// Parse the private key
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyData)
if err != nil {
log.Fatalf("Error parsing private key: %v", err)
}

// Create the JWT claims
claims := jwt.MapClaims{
"iat": time.Now().Unix(), // Issued at time
"exp": time.Now().Add(*expiryDuration).Unix(), // JWT expiration time
"iss": *appID, // GitHub App App ID

Check failure on line 43 in tests/bootstrap/jwt/main.go

View workflow job for this annotation

GitHub Actions / Lint

Duplicate words (App) found (dupword)
}

// Create the JWT
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
jwtToken, err := token.SignedString(privateKey)
if err != nil {

Check failure on line 49 in tests/bootstrap/jwt/main.go

View workflow job for this annotation

GitHub Actions / Lint

empty-lines: extra empty line at the end of a block (revive)
log.Fatalf("Error signing JWT: %v", err)

}

Check failure on line 52 in tests/bootstrap/jwt/main.go

View workflow job for this annotation

GitHub Actions / Lint

unnecessary trailing newline (whitespace)

// Output the JWT token
fmt.Print(jwtToken)
}
Loading
Loading