mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
62e7f983ee
* test: assert the gateway->vpcd->OVN crossing in the integration tier
The tier already ran the real VPCServiceImpl for the AWS-facing half, but
nothing consumed the vpc.* events it published: the SG topics were answered
by a canned {"success":true} and the fire-and-forget topology topics were
dropped. Both sides of that wire were tested only against stubs, so the join
was asserted nowhere below the live e2e tier.
Wire a real subscribers.Subscriber over a real in-process OVN Northbound DB
(ovntest.StartNB), opt-in per test so only tests that assert OVN state pay
the startup cost. StartDaemonLite gains WithRealVPCD to suppress its stubs,
since a stub and the subscriber both answering one request-reply would race.
Scope is the crossing, not NB row shapes — those stay with the reconcile
scenario tests. Everything host-dependent (ovn-controller, OVS, taps, netns,
nftables, packet paths) remains the live tier's claim.
* fix: make the integration tier re-runnable and quiet the ensure probe
Two defects the vpcd/OVN work surfaced.
The tier was green at -count=1 and red package-wide at -count=2. Test
accounts were keyed by t.Name(), which is unique among tests but repeats
across passes in one TestMain, so a second pass inherited the first pass's
JetStream namespace — including an ECR signing key sealed with a master key
StartGateway regenerates per call. Key each account off a counter instead.
TestRunInstances_ClientTokenIdempotency then collided with itself for the
same reason: its dedup store outlives a pass, so its fixed ClientToken made
the second pass's opening launch look like a replay. Suffix the tokens per
invocation.
ensureNamedRowOps' wait-op is built to fail fast when the row it guards
already exists, which is the ordinary idempotent path, but transactOps
reported it as "OVSDB operation failed" at ERROR. Every re-ensure emitted
one, drowning real faults in the log sink. Classify that one result as the
probe outcome it is and log it at Debug.
* test: cover transactOps' failed-result paths
The classifier split one log line into two branches and neither was
exercised: transactOps sat at 31% because EnsureLogicalRouter's cache
lookup absorbs a re-ensure before it ever transacts, so no unit test
reached a failed operation result.
Drive both kinds directly against a real NB — the guard probe finding an
existing row, and two same-named inserts violating the schema's name
index — since only the second is a fault and they now log at different
levels. transactOps goes to 94%; the remainder is the transport-error
return, which needs a broken connection to reach.
89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
//go:build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mulgadc/spinifex/spinifex/network/ovn"
|
|
"github.com/mulgadc/spinifex/spinifex/network/subscribers"
|
|
vpcdfixture "github.com/mulgadc/spinifex/tests/fixtures/vpcd"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// VPCDLite is a real vpcd event consumer over a real in-process OVN
|
|
// Northbound DB. OVN is the connected client, for reading NB rows back.
|
|
type VPCDLite struct {
|
|
OVN ovn.Client
|
|
}
|
|
|
|
// StartVPCDLite subscribes a real network/subscribers.Subscriber — the same
|
|
// production type a live vpcd runs — to every vpc.* lifecycle topic, backed by
|
|
// managers driving a genuine in-process OVN NB DB.
|
|
//
|
|
// This closes the one seam the tier otherwise leaves open. StartDaemonLite
|
|
// already runs the real VPCServiceImpl for the AWS-facing half, so an API call
|
|
// is authenticated, validated and persisted for real; but the vpc.* events it
|
|
// publishes had no consumer, so the SG topics were answered by a canned ack
|
|
// and the fire-and-forget topology topics were dropped. With this wired, an
|
|
// SDK call is asserted all the way through to OVN NB rows.
|
|
//
|
|
// Call BEFORE StartDaemonLite, and pass WithRealVPCD to it — see the ordering
|
|
// and double-responder notes on that option.
|
|
//
|
|
// What is deliberately absent: ovn-controller, so nothing translates NB to SB
|
|
// to OpenFlow, and no OVS bridge, tap, netns or nftables rule exists. Correct
|
|
// NB rows do not imply correct flows; that remains the live tier's claim.
|
|
func StartVPCDLite(t *testing.T, gw *Gateway) *VPCDLite {
|
|
t.Helper()
|
|
|
|
fixture := vpcdfixture.Start(t)
|
|
|
|
subscriber, err := subscribers.New(fixture.Subscribers)
|
|
require.NoError(t, err, "construct vpcd subscriber")
|
|
|
|
subs, err := subscriber.Subscribe(gw.NATSConn)
|
|
require.NoError(t, err, "subscribe vpcd topics")
|
|
t.Cleanup(func() {
|
|
for _, s := range subs {
|
|
_ = s.Unsubscribe()
|
|
}
|
|
})
|
|
|
|
return &VPCDLite{OVN: fixture.OVN}
|
|
}
|
|
|
|
// nbPollInterval/nbPollTimeout bound the wait in awaitNB. Topology events are
|
|
// fire-and-forget (utils.PublishEvent), so there is no reply to synchronise
|
|
// on: the only honest assertion is to poll until the row appears. The timeout
|
|
// is generous relative to an in-process OVSDB round-trip, since it is only
|
|
// ever paid in full by a genuine failure.
|
|
const (
|
|
nbPollInterval = 10 * time.Millisecond
|
|
nbPollTimeout = 5 * time.Second
|
|
)
|
|
|
|
// awaitNB polls check until it returns nil, failing the test with the last
|
|
// error once nbPollTimeout elapses. Use it for anything driven by a
|
|
// fire-and-forget vpc.* event; the synchronous SG topics need no polling.
|
|
func awaitNB(t *testing.T, what string, check func(ctx context.Context) error) {
|
|
t.Helper()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), nbPollTimeout)
|
|
defer cancel()
|
|
|
|
var lastErr error
|
|
for {
|
|
if lastErr = check(ctx); lastErr == nil {
|
|
return
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
t.Fatalf("timed out waiting for %s: %v", what, lastErr)
|
|
case <-time.After(nbPollInterval):
|
|
}
|
|
}
|
|
}
|