4.3 KiB
Plan: Verbose (V-level) logging in the GCP provider
Created: 2026-08-11 17:42
Context
Debugging GCP provisioning is currently blind: the operator has zero .V(n) calls
anywhere, so --zap-log-level=debug (or any numeric level) reveals nothing about
what the GCP provider is doing — which API calls it makes, with what parameters,
and what came back. The goal: with debug/V-level logging enabled, see the details
of every GCP Compute API call (Insert/Get/Delete/AggregatedList) including a
summary of the response; with default info level, the provider stays as quiet
as today.
Approach (the "how")
Logger source — context-carried, not injected. Provider methods all take
ctx, and the reconciler already builds a per-request logger
(logf.FromContext(ctx) in internal/controller/proxy_controller.go:117) that
carries the proxy's name/namespace. The GCP provider will do
log := logf.FromContext(ctx).WithName("gcp").WithValues("provider", p.name) at
the top of each public method. Zero wiring changes (no registry/constructor/struct
changes), and every provider log line automatically inherits the reconcile
context (which Proxy triggered it). Calls from the GC sweeper inherit its
orphan-gc logger name the same way.
Verbosity scheme (logr convention: .Info() = V(0), debug flag = V(1)):
- V(1) — one line per GCP API call, after it returns: operation, identifying
params, outcome. Examples:
Create:"GCP insert instance"withzone,name,machineType,image,opName(currently discarded at gcp.go:117 — capture it, it's the only handle for correlating with GCP's operation log), plus a line for the 409-already-exists path.Get:"GCP get instance"withzone,name,status, mappedstate,ip.Delete:"GCP delete instance"withzone,name,opName, and the 404-treated-as-success path.ListByTag:"GCP aggregated list"withfilter,count.- Error paths at V(1) too: log the raw classification (HTTP status / reason
from
googleapi.Error) before it's wrapped, since the wrapped error the reconciler sees is coarser.
- V(2) — request/response detail: full curated insert-request summary
(network, networkTag, diskSizeGB, port, labels,
cloudInitBytes=len), per-instance lines inListByTag(id, state, uid, age).
Curated fields, never raw proto dumps. CreateRequest.CloudInit is resolved
user-data possibly sourced from a Secret, and it lands in the insert request's
metadata — so logging the request proto wholesale would leak it. Log named safe
fields only; for cloud-init, log only its byte length. This is a hard rule, and
a test asserts it.
Where the calls live: the Provider methods in
internal/provider/gcp/gcp.go (Create/Get/Delete/ListByTag), not in
realInstances (deliberately untested by design, gcp.go:86) and not an HTTP
round-tripper (would log auth headers/user-data, unredactable). The
instancesAPI fake seam (newWithAPI, gcp.go:96) keeps everything testable.
To surface opName, change Provider.Create/Delete to capture the string
their instancesAPI calls already return instead of discarding it.
Files to change
internal/provider/gcp/gcp.go— addlogfimport; V(1)/V(2) logging inCreate,Get,Delete,ListByTag; capture opNames. Only file with production changes.internal/provider/gcp/gcp_test.go— new table-driven testTestLogging_verbosity(name TBD per houseTest<Function>_<scenario>style): inject a capturing logger vialogf.IntoContext(ctx, funcr.New(...))(github.com/go-logr/logr/funcr, logr already a direct dep), assert:- at V(1): expected message + keys per operation (incl. opName),
- at V(0): nothing logged,
- cloud-init content never appears in any log output (grep the captured
lines for a sentinel string placed in
CloudInit).
- No changes to
provider.Providerinterface, registry,cmd/main.go, manifests, or the kubernetes provider (it can copy this pattern later).
Verification
go test -race ./internal/provider/gcp/...
go build ./...
Optional live check: run the manager with --zap-log-level=2 against the GCP
project and confirm insert/get lines appear during a Proxy reconcile, and that
--zap-log-level=info stays quiet.
Also append a CHANGELOG.md entry per house convention once confirmed working.