From a2a326afc3d67f7b77ada2787253a703419e85fd Mon Sep 17 00:00:00 2001 From: joshsiv-mulga Date: Mon, 25 May 2026 15:07:59 +1000 Subject: [PATCH] =?UTF-8?q?refactor(vpcd):=20relocate=20services/vpcd=20?= =?UTF-8?q?=E2=86=92=20spinifex/vpcd;=20drop=20dead=20aliases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 4 Slice D (mulga-siv-125.6). Final cleanup of the network-redesign package layout. services/vpcd/ disappears entirely; the launcher remains an in-process Service impl, just rehomed under spinifex/vpcd/ (no separate binary — spx vpcd start stays the cobra entrypoint). - Delete services/vpcd/mock_ovn.go + ovn.go (compat aliases now dead post Slice C; only callsite was launchService's NewLiveOVNClient, inlined to ovn.NewLiveClient). - git-rename services/vpcd/vpcd.go + vpcd_test.go to spinifex/vpcd/. - Retarget three importers (spinifex/service/service.go + service_test.go, cmd/spinifex/cmd/service.go) to the new path. - Fix stale services/vpcd / TopologyHandler comments in network/reconcile/ + network/external/types.go + spinifex/vpcd/vpcd.go. - service-interfaces.yaml vpcd path updated. - Plan doc §13/§14/§16 + §9.2/§19 updated: drop cmd/vpcd/main.go (was speculative; never built) in favour of in-process Service impl at spinifex/vpcd/vpcd.go. Plan §16 Created list now lists subscribers/. Phase 4 plan items 1+2 (remove services/vpcd, entrypoint location) satisfied. Items 3+4 remain blocked on mulga-siv-125.4 + 125.5 (bundled join token, plan archive). --- cmd/spinifex/cmd/service.go | 2 +- docs/service-interfaces.yaml | 2 +- spinifex/network/external/types.go | 5 ++--- spinifex/network/reconcile/apply.go | 4 +--- spinifex/network/reconcile/drift.go | 2 +- spinifex/network/reconcile/intent.go | 2 +- spinifex/network/reconcile/lock.go | 4 ++-- spinifex/network/reconcile/reconcile.go | 4 ++-- spinifex/service/service.go | 2 +- spinifex/service/service_test.go | 2 +- spinifex/services/vpcd/mock_ovn.go | 11 ----------- spinifex/services/vpcd/ovn.go | 18 ------------------ spinifex/{services => }/vpcd/vpcd.go | 9 +++++---- spinifex/{services => }/vpcd/vpcd_test.go | 0 14 files changed, 18 insertions(+), 49 deletions(-) delete mode 100644 spinifex/services/vpcd/mock_ovn.go delete mode 100644 spinifex/services/vpcd/ovn.go rename spinifex/{services => }/vpcd/vpcd.go (99%) rename spinifex/{services => }/vpcd/vpcd_test.go (100%) diff --git a/cmd/spinifex/cmd/service.go b/cmd/spinifex/cmd/service.go index ad5adf37e..ecc91265c 100644 --- a/cmd/spinifex/cmd/service.go +++ b/cmd/spinifex/cmd/service.go @@ -29,7 +29,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/services/predastore" "github.com/mulgadc/spinifex/spinifex/services/spinifexui" "github.com/mulgadc/spinifex/spinifex/services/viperblockd" - "github.com/mulgadc/spinifex/spinifex/services/vpcd" + "github.com/mulgadc/spinifex/spinifex/vpcd" "github.com/spf13/cobra" "github.com/spf13/viper" ) diff --git a/docs/service-interfaces.yaml b/docs/service-interfaces.yaml index e45e33089..f92c6f276 100644 --- a/docs/service-interfaces.yaml +++ b/docs/service-interfaces.yaml @@ -131,7 +131,7 @@ services: - "s3.*" vpcd: - path: spinifex/services/vpcd + path: spinifex/vpcd # OVN-backed VPC / subnet / SG / NAT / IGW data plane. subscribes: - "vpc.create" diff --git a/spinifex/network/external/types.go b/spinifex/network/external/types.go index 80b8fddd5..0f4af9f56 100644 --- a/spinifex/network/external/types.go +++ b/spinifex/network/external/types.go @@ -6,9 +6,8 @@ package external // from GwLrpRangeStart..GwLrpRangeEnd (auto-derived from the WAN subnet // when unset). The upstream-DHCP source model is gone (mulga-siv-125.3.3). // -// This type duplicates services/vpcd/vpcd.go's ExternalPoolConfig. Dedup is -// out of scope for this bead; the two will merge when L2 (topology) grows -// the L5-driving methods and vpcd's local copy disappears. +// Duplicates spinifex/vpcd.ExternalPoolConfig pending dedup when vpcd's +// Config grows to use this shared type directly. type ExternalPoolConfig struct { Name string RangeStart string diff --git a/spinifex/network/reconcile/apply.go b/spinifex/network/reconcile/apply.go index ef3fe9cdf..1c8c66d0b 100644 --- a/spinifex/network/reconcile/apply.go +++ b/spinifex/network/reconcile/apply.go @@ -293,9 +293,7 @@ func (r *reconciler) applyNATGWs(ctx context.Context, intent IntentState, _ Actu } // subnetGatewayCIDR returns the .1 host of the subnet's CIDR and the -// prefix bit count. Matches the legacy services/vpcd/topology.go's -// subnetGateway, kept local to the reconcile package so the cross-package -// dependency on services/vpcd can be dropped. +// prefix bit count. Kept local to the reconcile package. func subnetGatewayCIDR(prefix netip.Prefix) (string, int, error) { if !prefix.IsValid() { return "", 0, fmt.Errorf("invalid prefix") diff --git a/spinifex/network/reconcile/drift.go b/spinifex/network/reconcile/drift.go index 891316fcc..a93c70790 100644 --- a/spinifex/network/reconcile/drift.go +++ b/spinifex/network/reconcile/drift.go @@ -24,7 +24,7 @@ var DriftInterval = 5 * time.Minute // missed acquire (another vpcd is leader, or JetStream is unreachable) is // logged at Debug and the tick is skipped — the next tick retries. // -// Replaces services/vpcd.ReconcileSGsLoop (30s, SG-only). The 5-minute +// Replaces the legacy ReconcileSGsLoop (30s, SG-only). The 5-minute // interval matches parent plan §11.4. func DriftLoop(ctx context.Context, rec Reconciler, nc *nats.Conn, localAZ, holder string) { js, err := nc.JetStream() diff --git a/spinifex/network/reconcile/intent.go b/spinifex/network/reconcile/intent.go index 2f45f32b8..69765f009 100644 --- a/spinifex/network/reconcile/intent.go +++ b/spinifex/network/reconcile/intent.go @@ -90,7 +90,7 @@ func matchesLocalAZ(vpcAZ, localAZ string) bool { } // keyIsVersion skips the per-bucket version-marker key. Mirrors what every -// other KV consumer in the tree does (services/vpcd/reconcile.go etc). +// other KV consumer in the tree does. func keyIsVersion(key string) bool { return key == utils.VersionKey } func loadVPCs(js nats.JetStreamContext, localAZ string, out map[string]topology.VPCSpec) (map[string]struct{}, error) { diff --git a/spinifex/network/reconcile/lock.go b/spinifex/network/reconcile/lock.go index 1e12413fd..6325e6cfa 100644 --- a/spinifex/network/reconcile/lock.go +++ b/spinifex/network/reconcile/lock.go @@ -32,8 +32,8 @@ var ( // elected=false so a follow-up retry (systemd Restart=on-failure) can run. // // Exported with the same contract as the legacy -// services/vpcd.AcquireReconcileLeader — call sites in services/vpcd/vpcd.go -// switch to this package by changing the import path only. +// AcquireReconcileLeader; spinifex/vpcd/vpcd.go is the sole production +// caller via launchService. func AcquireLeader(nc *nats.Conn, holder string) (func(), bool) { js, _ := nc.JetStream() diff --git a/spinifex/network/reconcile/reconcile.go b/spinifex/network/reconcile/reconcile.go index f005bba0b..c0e63cbbc 100644 --- a/spinifex/network/reconcile/reconcile.go +++ b/spinifex/network/reconcile/reconcile.go @@ -8,8 +8,8 @@ // The reconciler replaces the legacy five-pass startup sequence // (`Reconcile` + `ReconcileFromKV` + `ReconcileSGsOnce` + // `RetrofitAllExternalLocalnetOptions` + `RetrofitAllGatewayPortNetworks`) -// in `services/vpcd`. A 5-minute drift goroutine drives the same call -// periodically, replacing the 30-second `ReconcileSGsLoop`. +// the vpcd daemon ran on boot. A 5-minute drift goroutine drives the same +// call periodically, replacing the 30-second `ReconcileSGsLoop`. // // Multi-AZ scoping: `IntentState` is built by `LoadIntentFromKV(ctx, js, // localAZ)`. The filter rule is `vpc.AZ == "" || vpc.AZ == localAZ` — diff --git a/spinifex/service/service.go b/spinifex/service/service.go index 66fd13926..716651448 100644 --- a/spinifex/service/service.go +++ b/spinifex/service/service.go @@ -9,7 +9,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/services/spinifex" "github.com/mulgadc/spinifex/spinifex/services/spinifexui" "github.com/mulgadc/spinifex/spinifex/services/viperblockd" - "github.com/mulgadc/spinifex/spinifex/services/vpcd" + "github.com/mulgadc/spinifex/spinifex/vpcd" ) type Service interface { diff --git a/spinifex/service/service_test.go b/spinifex/service/service_test.go index fb496e2f3..3696fe3a8 100644 --- a/spinifex/service/service_test.go +++ b/spinifex/service/service_test.go @@ -8,7 +8,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/services/predastore" "github.com/mulgadc/spinifex/spinifex/services/spinifexui" "github.com/mulgadc/spinifex/spinifex/services/viperblockd" - "github.com/mulgadc/spinifex/spinifex/services/vpcd" + "github.com/mulgadc/spinifex/spinifex/vpcd" "github.com/stretchr/testify/assert" ) diff --git a/spinifex/services/vpcd/mock_ovn.go b/spinifex/services/vpcd/mock_ovn.go deleted file mode 100644 index 9dbebed95..000000000 --- a/spinifex/services/vpcd/mock_ovn.go +++ /dev/null @@ -1,11 +0,0 @@ -package vpcd - -// Phase 1 compat layer: the OVN client mock lives in -// spinifex/network/ovn/mock. These aliases keep existing vpcd tests -// compiling while Phase 2 migrates them to import the mock package directly. - -import "github.com/mulgadc/spinifex/spinifex/network/ovn/mock" - -type MockOVNClient = mock.Client - -var NewMockOVNClient = mock.New diff --git a/spinifex/services/vpcd/ovn.go b/spinifex/services/vpcd/ovn.go deleted file mode 100644 index cef9d2d8d..000000000 --- a/spinifex/services/vpcd/ovn.go +++ /dev/null @@ -1,18 +0,0 @@ -package vpcd - -// Phase 1 compat layer: OVN client lives in spinifex/network/ovn. These -// aliases keep existing vpcd code compiling while Phase 2 migrates callers -// to import the new package directly. - -import "github.com/mulgadc/spinifex/spinifex/network/ovn" - -type ( - OVNClient = ovn.Client - LiveOVNClient = ovn.LiveClient - ACLSpec = ovn.ACLSpec -) - -var ( - ErrNATNotFound = ovn.ErrNATNotFound - NewLiveOVNClient = ovn.NewLiveClient -) diff --git a/spinifex/services/vpcd/vpcd.go b/spinifex/vpcd/vpcd.go similarity index 99% rename from spinifex/services/vpcd/vpcd.go rename to spinifex/vpcd/vpcd.go index 6f38fa0ae..78ac4ca82 100644 --- a/spinifex/services/vpcd/vpcd.go +++ b/spinifex/vpcd/vpcd.go @@ -17,6 +17,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/admin" "github.com/mulgadc/spinifex/spinifex/network/external" "github.com/mulgadc/spinifex/spinifex/network/host" + "github.com/mulgadc/spinifex/spinifex/network/ovn" "github.com/mulgadc/spinifex/spinifex/network/policy" "github.com/mulgadc/spinifex/spinifex/network/reconcile" "github.com/mulgadc/spinifex/spinifex/network/subscribers" @@ -442,7 +443,7 @@ func launchService(cfg *Config) error { return fmt.Errorf("OVN NB DB address not configured (ovn_nb_addr is empty)") } - liveClient := NewLiveOVNClient(cfg.OVNNBAddr) + liveClient := ovn.NewLiveClient(cfg.OVNNBAddr) ctx := context.Background() if err := liveClient.Connect(ctx); err != nil { slog.Error("Failed to connect to OVN NB DB", "endpoint", cfg.OVNNBAddr, "err", err) @@ -576,9 +577,9 @@ func launchService(cfg *Config) error { // Startup reconcile: leader-gated read of NATS KV intent state, applied // once against OVN NB DB. The drift loop below handles the recovery case // (KV not yet populated when this fires — daemon's EnsureDefaultVPC may - // race with vpcd's startup). Per-event NATS subscribers (TopologyHandler) - // already route through the same network/* managers, so the startup pass - // and the runtime fast path share one implementation. + // race with vpcd's startup). Per-event NATS subscribers route through + // the same network/* managers, so the startup pass and the runtime fast + // path share one implementation. if isLeader { intent, intentErr := reconcile.LoadIntentFromKV(ctx, js, cfg.AZ) if intentErr != nil { diff --git a/spinifex/services/vpcd/vpcd_test.go b/spinifex/vpcd/vpcd_test.go similarity index 100% rename from spinifex/services/vpcd/vpcd_test.go rename to spinifex/vpcd/vpcd_test.go