Skip to content

feat: read locations from the locations service - #261

Merged
scotwells merged 5 commits into
mainfrom
feat/locations-api-source
Sep 1, 2026
Merged

feat: read locations from the locations service#261
scotwells merged 5 commits into
mainfrom
feat/locations-api-source

Conversation

@scotwells

Copy link
Copy Markdown
Contributor

Compute reads the platform's locations to decide where a workload can run, and to validate the location a user names. It reads them from the network-services operator, which no longer owns that data. The locations service does.

This moves compute onto the locations service, behind a config flag so a control plane can switch when its data is ready rather than on deploy.

Compute refuses to start if the group it is configured to read is not served, naming the missing resource. A silent fallback would let a control plane run with no locations and fail every placement at admission instead.

Related: milo-os/locations, datum-cloud/infra#4299.

Draft: LocationBinding still backs workload placement here, and dropping it needs network-services-operator#413 to land first.

Compute reads two location facts: the cities a project may place workloads
in, and the location a cell serves. Both are served today by
network-services-operator and are moving to the locations service, which
projects a Location into a project's control plane alongside the legacy
LocationBinding.

A new locationSource config field selects which group is read. It defaults
to NetworkServices, so a deployment that does not set it reads exactly what
it reads today. It governs reads only; nothing compute writes changes with
it.

Key changes:
- Add internal/locations, which resolves placement locations and serving
  locations from either group and collapses the three duplicated city-code
  lookups into one call
- Read the locations service through unstructured: its module path does not
  yet resolve, so a typed dependency cannot be pinned. A kind that is not
  installed reads as no locations rather than failing the caller
- Route the workload webhook, the workload reconciler and the deployment
  reconciler through the accessor, including the serving location watch
- Reject an unknown source when the server config loads, rather than on
  every reconcile
The locations module is reachable after all: a replace directive bypasses
the vanity path lookup that a bare `go get` fails on, which is the pattern
network-services-operator already uses for the ipam module. Pin the same
commit NSO pins, the one deployed to staging.

Reading the locations service through its generated types replaces the
unstructured client and its hand-rolled topology extraction, and lets the
shared topology keys be taken from the module rather than restated.

The NetworkServices path is untouched and still reads the same typed
network services kinds it always has.

Key changes:
- Depend on go.miloapis.com/locations by pseudo-version through a replace,
  and register its scheme on the manager and the CLI client
- Keep the absent-CRD degrade, which a typed client reaches differently: the
  no-match arrives wrapped in an ErrResourceDiscoveryFailed, so only
  errors.Is unwrapping finds it. A type missing from the scheme is a wiring
  mistake and still fails
- Cover both states against a real API server, since compiling against the
  types says nothing about a client resolving the kinds
- Assert the two groups agree on the city-code and serving-location keys,
  which is what makes switching sources safe
Reading a location the control plane does not serve degrades to no
locations, but watching one cannot: registering the watch wedges the
manager during cache sync, and a wedged manager says nothing about why.
Setup now checks discovery first and refuses to start, naming the kind, the
CustomResourceDefinition to install, and the locationSource that requires
it.

The check covers only the source actually selected. A deployment reading
network services gains no dependency on the locations service, and it stays
behind the networking integration gate, so a cell that registers no watch
still needs no location kind at all.

Key changes:
- Add EnsureServingLocationKind, and call it before the watch is registered
- Cover the guard against real discovery in envtest, across a control plane
  serving neither kind, one kind, and both, so each source is shown to gate
  on its own kind alone
The module path go.miloapis.com/locations did not resolve, so the dependency
was pinned through a replace onto the GitHub path. The vanity import is now
registered and the module downloads from the public proxy.

Key changes:
- Require go.miloapis.com/locations at its own path and drop the replace
@scotwells
scotwells requested a review from a team September 1, 2026 23:19
@scotwells
scotwells merged commit 5ea879e into main Sep 1, 2026
10 checks passed
@scotwells
scotwells deleted the feat/locations-api-source branch September 1, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants