Documentation
¶
Overview ¶
Package simtest is the in-process fleet-simulation scaffold described in ADR 0009. It stands up the real management server behind an httptest listener over an in-memory store, then drives it with virtual agents that speak the real enrollment and proof-of-possession poll protocol — the only thing virtualized is the Nebula node the server never observes. Tests use it to assert fleet-scale invariants (config convergence, tenant isolation, token single-use, ...) that per-request unit tests cannot reach.
It is a test-support library: it is only ever imported from _test.go files, so it is excluded from production binaries. It takes a minimal TB rather than importing the testing package directly.
Index ¶
- type Agent
- type Event
- type Harness
- func (h *Harness) API(method, path string, body, out any) int
- func (h *Harness) APIAs(key, method, path string, body, out any) int
- func (h *Harness) Advance(d time.Duration)
- func (h *Harness) CreateHost(networkID, name, role, nebulaIP string) (hostID, token string)
- func (h *Harness) CreateHostUnderCA(networkID, caID, name, ip string) string
- func (h *Harness) CreateNetwork(name string, cidrs ...string) string
- func (h *Harness) Enroll(networkID, name, nebulaIP string) *Agent
- func (h *Harness) NewTenant(name string) *Tenant
- func (h *Harness) SetClock(t time.Time)
- type Journal
- type Option
- type PollResponse
- type TB
- type Tenant
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Agent ¶
type Agent struct {
HostID string
Name string
Fingerprint string
// contains filtered or unexported fields
}
Agent is a virtual host: an enrolled identity that speaks the real ADR 0004 poll protocol. It tracks only what an agent legitimately knows — its fingerprint and signing key — never server-side state.
func (*Agent) DrainToConverged ¶
DrainToConverged polls until the server stops shipping config (steady state), returning the number of polls. It fails the test if convergence is not reached within maxPolls — a stuck host is itself a convergence failure.
func (*Agent) Poll ¶
func (a *Agent) Poll(h *Harness) PollResponse
Poll performs one signed GET /api/v1/agent/updates, the real ADR 0004 poll.
type Event ¶
type Event struct {
Step int
Actor string // virtual agent name, or operator action source
Action string // enroll | poll | bump-version | block | ...
Target string // host ID or network ID the action touched
Status int // HTTP status, when applicable
Note string // short human-readable detail
}
Event is one entry in the simulation journal. The journal is what makes a fleet-scale failure legible (ADR 0009): instead of "assertion failed", a violated invariant prints the minimal trace of what happened to the entities involved.
type Harness ¶
type Harness struct {
Server *httptest.Server
Store *store.SQLiteStore
CA *models.CA
Journal *Journal
// contains filtered or unexported fields
}
Harness is a running management server plus the handles a test needs to drive it: the store (for direct assertions and config-version reads), the default CA, and a shared event Journal.
func New ¶
New brings up a fresh in-memory server with one admin operator, an API key, and a default CA — the same shape as the integration e2e harness, exposed as a reusable library.
func (*Harness) API ¶
API issues an authenticated admin API request and returns the decoded JSON body (into out, if non-nil) and status code. It fails the test on transport errors so callers can focus on status assertions.
func (*Harness) APIAs ¶
APIAs is like API but authenticates with the given API key — used by multi-tenant isolation tests to act as a specific Tenant.
func (*Harness) Advance ¶
Advance moves the harness clock forward by d (freezing at now first if it was still live).
func (*Harness) CreateHost ¶
CreateHost creates a host row and returns its ID and single-use enrollment token. role may be "" (regular host), "lighthouse", or "relay".
func (*Harness) CreateHostUnderCA ¶
CreateHostUnderCA store-creates an enrolled-shaped host row owned by caID on the given network, returning its ID. Bypasses the create API so isolation tests can plant a host under one tenant's CA directly.
func (*Harness) CreateNetwork ¶
CreateNetwork creates a network and returns its ID.
func (*Harness) Enroll ¶
Enroll creates and enrolls a regular host, returning a ready-to-poll Agent.
func (*Harness) NewTenant ¶
NewTenant store-creates a non-admin operator with its own API key and CA, returning a Tenant whose Key authenticates as that operator. Hosts created under the tenant's CAID are owned by it; another tenant must not be able to read or mutate them (authz gates on CA ownership — internal/api/authz.go).
type Journal ¶
type Journal struct {
// contains filtered or unexported fields
}
Journal is a concurrency-safe append-only event log shared across a fleet.
type Option ¶
type Option func(*options)
Option configures a Harness.
func WithFileStore ¶
func WithFileStore() Option
WithFileStore backs the harness with a temp-file SQLite database instead of ":memory:". The in-memory store pins MaxOpenConns(1), which serializes every query and masks concurrency bugs; a file-backed store uses the production pool (WAL, multiple connections), so concurrency invariants (token single-use, IP-uniqueness races) exercise the real path. Required for any test that relies on connection-level concurrency.
type PollResponse ¶
type PollResponse struct {
Status int
Reason string `json:"reason"` // set on 403/410 revocation bodies
HasUpdates bool `json:"has_updates"`
ConfigYAML *string `json:"config_yaml"`
CertificatePEM *string `json:"certificate_pem"`
RekeyRequired bool `json:"rekey_required"`
Blocklist []string `json:"blocklist"`
}
PollResponse is the subset of the agent-updates response the simulation asserts against.