Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions test/integration/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ import (
"time"
)

// TestBenchmark provisions a validator chain + RPC fleet for the load suite.
// seiload drive + report upload are not yet wired (see TODO below).
// TestBenchmark provisions a validator chain + RPC fleet, drives seiload against
// the fleet for the configured duration, and asserts the chain stayed live under
// load. The load suite.
//
// Inputs (env, mirroring k8s_nightly.yml):
//
// SEI_CHAIN_ID per-run chain id (e.g. bench-<run-id>) [required]
// SEID_IMAGE seid image under test [required]
// SEI_RUN_ID unique run id (sei.io/harness-run) [default: SEI_CHAIN_ID]
// SEI_NAMESPACE shared nightly namespace [default: SDK default]
// SEI_CHAIN_ID per-run chain id (e.g. bench-<run-id>) [required]
// SEID_IMAGE seid image under test [required]
// SEILOAD_IMAGE sei-load benchmark image [required]
// SEI_RUN_ID unique run id (sei.io/harness-run) [default: SEI_CHAIN_ID]
// SEI_NAMESPACE shared nightly namespace [default: SDK default]
// SEILOAD_PROFILE profile name in seiload-profiles [default: nightly_evm_transfer]
// DURATION_MINUTES seiload run length [default: 10]
// SEILOAD_COMMIT_ID sei-chain commit label for metrics [default: ""]
//
// Deadlines: the CronJob MUST run this with `-test.timeout 0` (or safely above
// the scenario timeout). A -test.timeout breach panics and bypasses t.Cleanup,
Expand All @@ -30,13 +35,17 @@ func TestBenchmark(t *testing.T) {

chainID := mustEnv(t, "SEI_CHAIN_ID")
s := spec{
chainID: chainID,
runID: envOr("SEI_RUN_ID", chainID),
namespace: envOr("SEI_NAMESPACE", ""),
seidImage: mustEnv(t, "SEID_IMAGE"),
validators: 4,
rpcNodes: 2, // seiload fans across both via the EVM endpoint list
timeout: 90 * time.Minute,
chainID: chainID,
runID: envOr("SEI_RUN_ID", chainID),
namespace: envOr("SEI_NAMESPACE", ""),
seidImage: mustEnv(t, "SEID_IMAGE"),
validators: 4,
rpcNodes: 2, // seiload fans across both via the EVM endpoint list
timeout: 90 * time.Minute,
seiloadImage: mustEnv(t, "SEILOAD_IMAGE"),
seiloadProfile: envOr("SEILOAD_PROFILE", "nightly_evm_transfer"),
seiloadCommit: envOr("SEILOAD_COMMIT_ID", ""),
durationMin: envInt(t, "DURATION_MINUTES", 10),
}

ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
Expand All @@ -49,20 +58,15 @@ func TestBenchmark(t *testing.T) {
defer stopSignals()

c := openClient(ctx, t)
cs := clientset(t)

ch, err := provision(ctx, t, c, s)
cleanupChain(t, ch)
if err != nil {
t.Fatalf("provision: %v", err)
}

t.Logf("provisioned %s: %d validators + %d RPC followers; EVM endpoints=%v",
s.chainID, s.validators, len(ch.rpcNodes), ch.evmEndpoints())

// TODO: drive seiload as a decoupled unit — apply its own manifest
// parameterized with ch.evmEndpoints(), stamped sei.io/harness-run; wait,
// read the report from S3, assert TPS/receipts. seiload's Job spec is not
// constructed here.
t.Skipf("provisioned %s (%d validators + %d followers); seiload drive + report not yet wired — tearing down",
s.chainID, s.validators, len(ch.rpcNodes))
runSeiload(ctx, t, cs, ch, s)
}
33 changes: 28 additions & 5 deletions test/integration/harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"net/http"
"os"
"strconv"
"testing"
"time"

Expand All @@ -30,22 +31,30 @@ import (
_ "github.com/sei-protocol/sei-k8s-controller/sdk/sei/provider/k8s"
)

// runLabelKey marks a run's resources for the nightly label-GC sweep — the only
// reaper on abnormal exit (shared namespace), since t.Cleanup is skipped on
// SIGKILL or a -test.timeout breach. provision stamps it on the network + every
// node; a suite's directly-applied seiload Job and fault CRs must stamp it too.
// runLabelKey marks a run's resources for the abnormal-exit reaper (t.Cleanup is
// skipped on SIGKILL / a -test.timeout breach). provision stamps it on the
// network + every node; a suite's directly-applied seiload Job + fault CRs stamp
// it too. The matching nightly label-GC sweep is a pending platform deliverable;
// until it ships, normal-exit teardown (t.Cleanup) + the SeiNetwork
// DeletionPolicy cascade are the cleanup path.
const runLabelKey = "sei.io/harness-run"

// spec is the typed input shared by the suites — the local-Go-state replacement
// for the per-run workflow-vars contract.
type spec struct {
chainID string // SeiNetwork name == genesis chain id; also the peer-selector value and per-run discriminator
runID string // unique per run; the sei.io/harness-run label value
namespace string // shared nightly namespace (D2); "" => SDK client default (SA namespace)
namespace string // shared nightly namespace; "" => the SDK client's resolved default
seidImage string // seid container image under test
validators int // genesis validator count (>= 1)
rpcNodes int // standalone RPC followers; named <chain>-rpc-0..N-1
timeout time.Duration // overall scenario deadline (drives ctx, kept < CronJob activeDeadlineSeconds)

// seiload inputs (load suite)
seiloadImage string // sei-load benchmark image
seiloadProfile string // profile name in the seiload-profiles ConfigMap
seiloadCommit string // sei-chain commit label for the run's metrics
durationMin int // seiload run length, minutes
}

// chain is the live provisioned topology a suite runs load against and asserts
Expand Down Expand Up @@ -214,3 +223,17 @@ func mustEnv(t *testing.T, key string) string {
}
return v
}

// envInt reads an integer env var or a fallback; a non-integer value fails fast.
func envInt(t *testing.T, key string, fallback int) int {
t.Helper()
v := os.Getenv(key)
if v == "" {
return fallback
}
n, err := strconv.Atoi(v)
if err != nil {
t.Fatalf("integration suite: env %s=%q is not an integer: %v", key, v, err)
}
return n
}
67 changes: 67 additions & 0 deletions test/integration/seiload_job.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
apiVersion: batch/v1
kind: Job
metadata:
name: seiload-{{.RunID}}
labels:
app.kubernetes.io/name: seiload
sei.io/harness-run: "{{.RunID}}"
spec:
backoffLimit: 0
activeDeadlineSeconds: {{.DeadlineSeconds}}
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
# podMonitor selects this for Prometheus scrape (metrics continuity).
app.kubernetes.io/name: seiload
sei.io/harness-run: "{{.RunID}}"
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: seiload
image: {{.Image}}
args:
- --config
- /etc/seiload/profile.json
- --duration={{.DurationMinutes}}m
- --post-summary-flush-delay=45s
- --track-receipts=true
ports:
- name: metrics
containerPort: 9090
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
env:
- name: SEILOAD_RUN_ID
value: "{{.RunID}}"
- name: SEILOAD_CHAIN_ID
value: "{{.ChainID}}"
- name: SEILOAD_COMMIT_ID
value: "{{.Commit}}"
- name: SEILOAD_WORKLOAD
value: nightly
volumeMounts:
- name: profile
mountPath: /etc/seiload
readOnly: true
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
volumes:
- name: profile
configMap:
name: {{.ProfileCM}}
Loading
Loading