Skip to content

Commit

Permalink
[tmpnet] Enable deployment to kube
Browse files Browse the repository at this point in the history
  • Loading branch information
maru-ava committed Feb 27, 2025
1 parent 687dbc4 commit 9919ff2
Show file tree
Hide file tree
Showing 30 changed files with 1,672 additions and 346 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ jobs:
prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
loki_username: ${{ secrets.LOKI_ID || '' }}
loki_password: ${{ secrets.LOKI_PASSWORD || '' }}
e2e_kube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-go-for-project
- name: Run e2e tests
shell: bash
run: bash -x ./scripts/tests.e2e.kube.sh
env:
PROMETHEUS_USERNAME: ${{ secrets.PROMETHEUS_ID || '' }}
PROMETHEUS_PASSWORD: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
LOKI_USERNAME: ${{ secrets.LOKI_ID || '' }}
LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD || '' }}
GH_REPO: ${{ github.repository_owner }}/${{ github.event.repository.name }}
GH_WORKFLOW: ${{ github.workflow }}
GH_RUN_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_number }}
GH_RUN_ATTEMPT: ${{ github.run_attempt }}
GH_JOB_ID: ${{ github.job }}
- name: Export kind logs
shell: bash
run: kind export logs /tmp/kind-logs
- name: Upload kind logs
uses: actions/upload-artifact@v4
with:
name: kind-logs
path: /tmp/kind-logs
if-no-files-found: error
e2e_existing_network:
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_antithesis_images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,5 @@ else
"${AVALANCHE_PATH}/build/antithesis/xsvm" \
"AVALANCHEGO_PATH=${AVALANCHE_PATH}/build/avalanchego AVAGO_PLUGIN_DIR=${AVALANCHE_PATH}/build/plugins"

build_antithesis_images_for_avalanchego "${TEST_SETUP}" "${IMAGE_PREFIX}" "${AVALANCHE_PATH}/vms/example/xsvm/Dockerfile"
build_antithesis_images_for_avalanchego "${TEST_SETUP}" "${IMAGE_PREFIX}" "${AVALANCHE_PATH}/tests/antithesis/xsvm/Dockerfile.node"
fi
22 changes: 22 additions & 0 deletions scripts/build_xsvm_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

set -euo pipefail

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

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

# TODO(marun) This image name should be configurable
DOCKER_IMAGE="localhost:5001/avalanchego"

# Build the avalancehgo node image
FORCE_TAG_LATEST=1 SKIP_BUILD_RACE=1 DOCKER_IMAGE="${DOCKER_IMAGE}" ./scripts/build_image.sh

# TODO(marun) conditionally push the image to the registry
GO_VERSION="$(go list -m -f '{{.GoVersion}}')"
docker buildx build --build-arg GO_VERSION="${GO_VERSION}" --build-arg AVALANCHEGO_NODE_IMAGE="${DOCKER_IMAGE}" \
--push -t "${DOCKER_IMAGE}-xsvm" -f "${AVALANCHE_PATH}/vms/example/xsvm/Dockerfile" .
54 changes: 54 additions & 0 deletions scripts/tests.e2e.kube.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash

set -euo pipefail

# Run e2e tests against nodes deployed to a kind cluster.

# TODO(marun)
# - Support testing against a remote cluster
# - Convert to golang to simplify reuse
# - Make idempotent to simplify development and debugging

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

./scripts/ensure_kube_cluster.sh

KUBE_CONTEXT="${KUBE_CONTEXT:-kind-kind}"
# TODO(marun) Make the namespace configurable
NAMESPACE=tmpnet
PATH="${PWD}/bin:$PATH" kubectl create namespace "${NAMESPACE}" || true

if [[ -z "${SKIP_BUILD_IMAGE:-}" ]]; then
bash -x ./scripts/build_xsvm_image.sh
fi

MONITORING_NAMESPACE=ci-monitoring
PATH="${PWD}/bin:$PATH" kubectl create namespace "${MONITORING_NAMESPACE}" || true

# Deploy promtail if credentials are available
if [[ -n "${LOKI_USERNAME:-}" && -n "${LOKI_PASSWORD:-}" ]]; then
kubectl --namespace="${MONITORING_NAMESPACE}" create secret generic loki-credentials \
--from-literal=username="${LOKI_USERNAME}" \
--from-literal=password="${LOKI_PASSWORD}" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl --namespace="${MONITORING_NAMESPACE}" apply -f ./tests/fixture/tmpnet/yaml/promtail-daemonset.yaml
else
echo "LOKI_USERNAME and LOKI_PASSWORD not set, skipping deployment of promtail"
fi

# Deploy prometheus agent if credentials are available
if [[ -n "${PROMETHEUS_USERNAME:-}" && -n "${PROMETHEUS_PASSWORD:-}" ]]; then
kubectl --namespace="${MONITORING_NAMESPACE}" create secret generic prometheus-credentials \
--from-literal=username="${PROMETHEUS_USERNAME}" \
--from-literal=password="${PROMETHEUS_PASSWORD}" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl --namespace="${MONITORING_NAMESPACE}" apply -f ./tests/fixture/tmpnet/yaml/prometheus-agent.yaml
else
echo "PROMETHEUS_USERNAME and PROMETHEUS_PASSWORD not set, skipping deployment of prometheus agent"
fi

E2E_SERIAL=1 KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}" PATH="${PWD}/bin:$PATH" \
bash -x ./scripts/tests.e2e.sh --runtime=kube --image-name=localhost:5001/avalanchego-xsvm
19 changes: 11 additions & 8 deletions scripts/tests.e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ fi
# the instructions to build non-portable BLST.
source ./scripts/constants.sh

# Enable subnet testing by building xsvm
./scripts/build_xsvm.sh
echo ""

# Ensure an absolute path to avoid dependency on the working directory
# of script execution.
AVALANCHEGO_PATH="$(realpath "${AVALANCHEGO_PATH:-./build/avalanchego}")"
E2E_ARGS="--avalanchego-path=${AVALANCHEGO_PATH}"
E2E_ARGS="${*:-}"
if ! [[ "${E2E_ARGS}" =~ "--runtime=kube" ]]; then
# If not running in kubernetes, use the local avalanchego binary
AVALANCHEGO_PATH="$(realpath "${AVALANCHEGO_PATH:-./build/avalanchego}")"
E2E_ARGS+=" --avalanchego-path=${AVALANCHEGO_PATH}"

# Enable subnet testing by building the xsvm binary
./scripts/build_xsvm.sh
fi

#################################
# Determine ginkgo args
Expand Down Expand Up @@ -58,5 +61,5 @@ else
fi

#################################
# shellcheck disable=SC2086
./bin/ginkgo ${GINKGO_ARGS} -v ./tests/e2e -- "${E2E_ARGS[@]}" "${@}"
# shellcheck disable=SC2086,SC2068
./bin/ginkgo ${GINKGO_ARGS} -v ./tests/e2e -- ${E2E_ARGS[@]}
17 changes: 16 additions & 1 deletion tests/antithesis/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ var (
// simplify usage by main entrypoints. If the provided network includes a subnet, the initial DB state for
// the subnet will be created and written to the target path.
func GenerateComposeConfig(network *tmpnet.Network, baseImageName string) error {
// TODO(marun) Is there a better way to ensure parity between the configuration that initializes the database and the configuration used at runtime?
if network.DefaultFlags == nil {
network.DefaultFlags = tmpnet.FlagsMap{}
}
network.DefaultFlags.SetDefaults(tmpnet.DefaultTestFlags())

targetPath := os.Getenv(targetPathEnvName)
if len(targetPath) == 0 {
return errTargetPathEnvVarNotSet
Expand Down Expand Up @@ -66,7 +72,16 @@ func GenerateComposeConfig(network *tmpnet.Network, baseImageName string) error
return fmt.Errorf("failed to get bootstrap volume path: %w", err)
}

if err := initBootstrapDB(network, avalancheGoPath, pluginDir, bootstrapVolumePath); err != nil {
network.DefaultRuntimeConfig = tmpnet.NodeRuntimeConfig{
AvalancheGoPath: avalancheGoPath,
}
// TODO(marun) Need to have a standard way of initializing a network
if network.DefaultFlags == nil {
network.DefaultFlags = tmpnet.FlagsMap{}
}
network.DefaultFlags[config.PluginDirKey] = pluginDir

if err := initBootstrapDB(network, bootstrapVolumePath); err != nil {
return fmt.Errorf("failed to initialize db volumes: %w", err)
}
}
Expand Down
4 changes: 1 addition & 3 deletions tests/antithesis/init_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,14 @@ func getBootstrapVolumePath(targetPath string) (string, error) {
// Bootstraps a local process-based network, creates its subnets and chains, and copies
// the resulting db state from one of the nodes to the provided path. The path will be
// created if it does not already exist.
func initBootstrapDB(network *tmpnet.Network, avalancheGoPath string, pluginDir string, destPath string) error {
func initBootstrapDB(network *tmpnet.Network, destPath string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*2)
defer cancel()
if err := tmpnet.BootstrapNewNetwork(
ctx,
tests.NewDefaultLogger(""),
network,
"",
avalancheGoPath,
pluginDir,
); err != nil {
return fmt.Errorf("failed to bootstrap network: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/c/dynamic_fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() {

ginkgo.It("should ensure that the gas price is affected by load", func() {
tc.By("creating a new private network to ensure isolation from other tests")
privateNetwork := tmpnet.NewDefaultNetwork("avalanchego-e2e-dynamic-fees")
privateNetwork := tmpnet.NewDefaultNetwork(tc.Log(), "avalanchego-e2e-dynamic-fees")
e2e.GetEnv(tc).StartPrivateNetwork(privateNetwork)

// Avoid emitting a spec-scoped metrics link for the shared
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
// Run in every ginkgo process

// Initialize the local test environment from the global state
e2e.InitSharedTestEnvironment(ginkgo.GinkgoT(), envBytes)
e2e.InitSharedTestEnvironment(e2e.NewTestContext(), envBytes)
})
15 changes: 11 additions & 4 deletions tests/e2e/faultinjection/duplicate_node_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ var _ = ginkgo.Describe("Duplicate node handling", func() {
ginkgo.It("should ensure that a given Node ID (i.e. staking keypair) can be used at most once on a network", func() {
network := e2e.GetEnv(tc).GetNetwork()

if network.DefaultRuntimeConfig.KubeRuntimeConfig != nil {
// Enabling this test for kube requires supporting a flexible name mapping
ginkgo.Skip("This test is not supported on kube because it relies on the mapping of network uuid + nodeid -> statefulset name")
}

tc.By("creating new node")
node1 := e2e.AddEphemeralNode(tc, network, tmpnet.FlagsMap{})
e2e.WaitForHealthy(tc, node1)
Expand All @@ -43,10 +48,10 @@ var _ = ginkgo.Describe("Duplicate node handling", func() {
// the same node ID.
config.DataDirKey: fmt.Sprintf("%s-second", node1Flags[config.DataDirKey]),
}
node2 := e2e.AddEphemeralNode(tc, network, node2Flags)
node2 := e2e.AddEphemeralNodeWithWaitForHealth(tc, network, node2Flags, false /* waitForHealth */)

tc.By("checking that the second new node fails to become healthy before timeout")
err := tmpnet.WaitForHealthy(tc.DefaultContext(), node2)
err := tmpnet.WaitForHealthyNode(tc.DefaultContext(), tc.Log(), node2)
require.ErrorIs(err, context.DeadlineExceeded)

tc.By("stopping the first new node")
Expand All @@ -68,7 +73,8 @@ func checkConnectedPeers(tc tests.TestContext, existingNodes []*tmpnet.Node, new
require := require.New(tc)

// Collect the node ids of the new node's peers
infoClient := info.NewClient(newNode.URI)
uri := e2e.GetLocalURI(tc, newNode)
infoClient := info.NewClient(uri)
peers, err := infoClient.Peers(tc.DefaultContext(), nil)
require.NoError(err)
peerIDs := set.NewSet[ids.NodeID](len(existingNodes))
Expand All @@ -81,7 +87,8 @@ func checkConnectedPeers(tc tests.TestContext, existingNodes []*tmpnet.Node, new
require.True(peerIDs.Contains(existingNode.NodeID))

// Check that the new node is a peer
infoClient := info.NewClient(existingNode.URI)
uri := e2e.GetLocalURI(tc, existingNode)
infoClient := info.NewClient(uri)
peers, err := infoClient.Peers(tc.DefaultContext(), nil)
require.NoError(err)
isPeer := false
Expand Down
9 changes: 6 additions & 3 deletions tests/fixture/bootstrapmonitor/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,16 @@ func buildImage(tc tests.TestContext, imageName string, forceNewHash bool, scrip
require.NoError(err, "Image build failed: %s", output)
}

func newNodeStatefulSet(name string, flags map[string]string) *appsv1.StatefulSet {
func newNodeStatefulSet(name string, flags tmpnet.FlagsMap) *appsv1.StatefulSet {
statefulSet := tmpnet.NewNodeStatefulSet(
name,
true, /* generateName */
latestAvalanchegoImage,
nodeContainerName,
volumeName,
volumeSize,
nodeDataDir,
nil,
flags,
)

Expand All @@ -282,8 +284,9 @@ func newNodeStatefulSet(name string, flags map[string]string) *appsv1.StatefulSe
return statefulSet
}

func defaultPodFlags() map[string]string {
return tmpnet.DefaultPodFlags(constants.LocalName, nodeDataDir)
func defaultPodFlags() tmpnet.FlagsMap {
// TODO(marun) DefaultPodFlags is only used by bootstrap monitor - maybe inline
return tmpnet.DefaultPodFlags(constants.LocalName, nodeDataDir, false /* sybilProtectionEnabled */)
}

// waitForPodCondition waits until the specified pod reports the specified condition
Expand Down
Loading

0 comments on commit 9919ff2

Please sign in to comment.