Files
spinifex/tests/e2e/multinode/tests_test.go
T
Josh Siviour 57bfbc20df test: listener invariant static guard + e2e runtime check (#782)
* test: add the listener invariant static guard and e2e runtime check

Adds both halves the plan called for: a static test in
spinifex/network/invariants that scans install scripts and config
templates for a bare OVSDB ptcp remote, a wildcard bind on a
Cluster/Encap port, or a bind port absent from the inventory, and an
e2e test in tests/e2e/multinode that asserts the same on live ss
-tulnp output. Both read
docs/security/network-connections/README.md "## 1. Inbound
Listeners" as their fixture through a shared, non-test parser package
(spinifex/network/listenerinventory), so a wildcard exception has to
be declared in the doc's own prose rather than hardcoded in either
test.

* fix: make the listener wildcard exception fail closed on an explicit marker

The prior heuristic granted an exception for any unnegated "wildcard"
or "0.0.0.0" mention within 40 characters of the port's row. That
fails open: a future row phrased as "avoids the wildcard" or with a
negation further upstream than the window would silently exempt
itself. Replace it with a single exact literal phrase, "binds the
wildcard by design", that a row must contain to grant the exception —
no other wording counts, so an unrecognized mention denies rather
than grants. Update the two rows that need it (5300, 500/4500) and
document the marker's role in the inventory's own prose.

* docs: record that the Geneve tunnel socket binds the wildcard by design
2026-08-13 12:29:26 +10:00

111 lines
3.9 KiB
Go

//go:build e2e
package multinode
import (
"testing"
)
// Top-level Test* wrappers, each delegating to a runX function. Parallel tests
// share the read-only singleton trio; sequential tests mutate cluster state
// (stop/start nodes, predastore, EIP pool) and are ordered so the cluster is
// fully restabilised before later tests probe the trio.
//
// Pre-flight (/dev/kvm writability, peer SSH reachability) runs inside
// requireMultiNodeFixture itself, so the package fixture singleton fails
// fast with a clear message before any Test* body executes.
// Baseline test owns a dedicated SG and self-cleaning instances; it runs
// before needInstanceTrio so the default SG/subnet/route table are in
// pristine state.
func TestMultinodeSameSGCrossHostComms(t *testing.T) {
runMultinodeSameSGCrossHostComms(t, requireMultiNodeFixture(t))
}
func TestMultinodeClusterHealth(t *testing.T) {
t.Parallel()
runClusterHealth(t, requireMultiNodeFixture(t))
}
// TestMultinodeDNS is sequential because it launches guests and briefly stops
// one Northstar unit while exercising resolver failover.
func TestMultinodeDNS(t *testing.T) {
runMultinodeDNS(t, requireMultiNodeFixture(t))
}
// TestMultinodeJetStreamReplicas is read-only over NATS; parallel so it resumes
// after the sequential node-failure/recovery tests have restabilised the cluster.
func TestMultinodeJetStreamReplicas(t *testing.T) {
t.Parallel()
runJetStreamReplicas(t, requireMultiNodeFixture(t))
}
// TestMultinodeVolumeDurability is sequential and declared before CrossNodeOps
// (which destabilises trio[0]'s sshd) so its guest-SSH probes hit settled VMs.
// Touches predastore state.
func TestMultinodeVolumeDurability(t *testing.T) {
runVolumeDurability(t, requireMultiNodeFixture(t))
}
// TestMultinodeCrossNodeGateway is sequential: asserts a stable per-gateway instance count
// that concurrent launches/terminates would break.
func TestMultinodeCrossNodeGateway(t *testing.T) {
runCrossNodeGateway(t, requireMultiNodeFixture(t))
}
// TestMultinodeInstanceStatusFanout is sequential and declared before
// CrossNodeOps (which stops trio[0]) so the trio is still spread across nodes
// when the fan-out spread is measured.
func TestMultinodeInstanceStatusFanout(t *testing.T) {
runInstanceStatusFanout(t, requireMultiNodeFixture(t))
}
// TestMultinodeCrossNodeOps is sequential: stops/starts trio[0].
func TestMultinodeCrossNodeOps(t *testing.T) {
runCrossNodeOps(t, requireMultiNodeFixture(t))
}
func TestMultinodeNodeFailure(t *testing.T) {
runNodeFailure(t, requireMultiNodeFixture(t))
}
func TestMultinodeNodeRecovery(t *testing.T) {
runNodeRecovery(t, requireMultiNodeFixture(t))
}
// TestMultinodeOVNRaft is sequential: stops ovn-central on the NB leader to
// prove DB failover, restoring it in cleanup before later tests run.
func TestMultinodeOVNRaft(t *testing.T) {
runOVNRaft(t, requireMultiNodeFixture(t))
}
// TestMultinodeFirewallChassisQuorum is sequential: it edits one node's config
// and restarts that node's daemon, restoring both before it returns.
func TestMultinodeFirewallChassisQuorum(t *testing.T) {
runFirewallChassisQuorum(t, requireMultiNodeFixture(t))
}
// TestMultinodeSpread is sequential after NodeRecovery; owns 10.100.0.0/16 + EIP pool.
func TestMultinodeSpread(t *testing.T) {
runSpread(t, requireMultiNodeFixture(t))
}
// TestMultinodeVPCNetworking owns 10.200.0.0/16 (no EIP use); safe to run in parallel.
func TestMultinodeVPCNetworking(t *testing.T) {
t.Parallel()
runVPCNetworking(t, requireMultiNodeFixture(t))
}
// TestMultinodeIPSec is read-only over SSH; launches no AWS resources.
func TestMultinodeIPSec(t *testing.T) {
t.Parallel()
runIPSec(t, requireMultiNodeFixture(t))
}
// TestMultinodeListenerInvariant is read-only over SSH; launches no AWS
// resources and mutates no cluster state.
func TestMultinodeListenerInvariant(t *testing.T) {
t.Parallel()
runListenerInvariant(t, requireMultiNodeFixture(t))
}