[Feature] Add a full-object Get op to the agent protocol#201
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR adds a new Changesget_object Operation
Estimated code review effort: 3 (Moderate) | ~35 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3b5b3a8 to
06d0e6d
Compare
The agent's only read op was get_status, which returns a status-only partial object. The cluster-access seam's Get is contractually a full-object read (the direct client returns the whole object), so AgentBacked.Get was mapping a full read onto a status-only one. The upcoming read-modify-patch write ops (per-VPC NAT and DNS, the cloud-provider service account) read .spec/.data to modify it, so they need a real full-object read through the agent. This adds a get_object op end to end -- Executor.GetObject and an opGetObject handler on the agent; a matching opGetObject wire frame and Session.GetObject on the control-plane side -- and repoints AgentBacked.Get at it, re-hydrating the agent's response into the same full Unstructured the direct client returns. For a rolling deploy where dc-api is newer than a connected agent, Get falls back to get_status when the agent reports the op as unsupported, degrading to the prior status-only read instead of erroring. get_status and watch_status (the async status poll and stream) are unchanged, and no RBAC change is needed -- a full read uses the same get permission the agent already holds. Verified: both modules build, vet, and pass race-enabled tests, including the full-object path, the unsupported-op fallback, and the existing VM read flow. A live read through a connected agent is the remaining check before relying on it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RC3BDHCFmJBdxvGVW69FAr
The List op gained a routability guard from review feedback so a GVK-mapped but non-routable family (the pre-seeded virtualmachineinstances) is refused rather than routed to the agent. Get resolves the same GVR->GVK mapping and had the same gap, so the guard is applied to Get too: it returns ErrOpNotRoutable for a mapped-but-direct-only family before calling the agent, and the caller then falls back to the direct path. Added a regression test mirroring the List one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RC3BDHCFmJBdxvGVW69FAr
06d0e6d to
b735b7c
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
🧹 Nitpick comments (2)
dc-agent/main.go (1)
172-172: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHand-maintained ops list risks drift.
The logged
opsslice duplicates the op set already registered inbuildDispatchers' returned map (this PR itself had to manually addOpGetObjecthere). Since the actual wire advertisement is derived from the dispatcher/streamDispatcher maps elsewhere (perTestBuildDispatchers_RegistersAllOps's comment), consider deriving this log field fromdispatcher's keys afterbuildDispatchersreturns, to avoid a future op being registered but omitted from the log (or vice versa).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dc-agent/main.go` at line 172, The logged ops slice in the main startup logging is manually duplicated and can drift from the actual registered ops. Update the logging around the buildDispatchers result to derive the ops field from the returned dispatcher map keys (and any relevant stream dispatcher keys if needed) instead of hardcoding executor.Op* values, so the log always reflects the registered operations.dc-api/internal/providers/clusteraccess/agent.go (1)
147-163: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAvoid computing
translateErr(err)twice.
a.translateErr(err)is called once insideerrors.Is(...)and again in the fallthrough return. Since it's a pure function, hoist it into a local variable.♻️ Suggested refactor
res, err := a.sess.GetObject(ctx, ref) if err != nil { - // An older agent that doesn't implement get_object replies OP_UNSUPPORTED, - // which translateErr maps to ErrOpNotRoutable. In that one case fall back to - // the status-only read so a rolling deploy never errors. Any other error - // (agent unavailable, RBAC, timeout) propagates. - if errors.Is(a.translateErr(err), agentgw.ErrOpNotRoutable) { + // An older agent that doesn't implement get_object replies OP_UNSUPPORTED, + // which translateErr maps to ErrOpNotRoutable. In that one case fall back to + // the status-only read so a rolling deploy never errors. Any other error + // (agent unavailable, RBAC, timeout) propagates. + terr := a.translateErr(err) + if errors.Is(terr, agentgw.ErrOpNotRoutable) { a.log.Info(). Str("seam", "agent"). Str("region", a.region).Str("zone", a.zone). Str("api_version", ref.APIVersion).Str("kind", ref.Kind). Str("namespace", ref.Namespace).Str("name", ref.Name). Msg("agent does not support get_object; falling back to status-only get_status") return a.getStatusFallback(ctx, gvr, ref, ns, name) } - return nil, a.translateErr(err) + return nil, terr }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dc-api/internal/providers/clusteraccess/agent.go` around lines 147 - 163, Avoid calling translateErr(err) twice in the GetObject error path; compute it once and reuse the result. In agent.go’s GetObject handling, assign a.translateErr(err) to a local variable before the errors.Is check, use that variable for the ErrOpNotRoutable comparison, and return it in the non-fallback error case so the logic stays the same while avoiding duplicate work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@dc-agent/main.go`:
- Line 172: The logged ops slice in the main startup logging is manually
duplicated and can drift from the actual registered ops. Update the logging
around the buildDispatchers result to derive the ops field from the returned
dispatcher map keys (and any relevant stream dispatcher keys if needed) instead
of hardcoding executor.Op* values, so the log always reflects the registered
operations.
In `@dc-api/internal/providers/clusteraccess/agent.go`:
- Around line 147-163: Avoid calling translateErr(err) twice in the GetObject
error path; compute it once and reuse the result. In agent.go’s GetObject
handling, assign a.translateErr(err) to a local variable before the errors.Is
check, use that variable for the ErrOpNotRoutable comparison, and return it in
the non-fallback error case so the logic stays the same while avoiding duplicate
work.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6568f1b5-31f9-41fc-85cf-12abd04acb91
📒 Files selected for processing (16)
dc-agent/internal/executor/executor.godc-agent/internal/executor/kube.godc-agent/internal/executor/kube_test.godc-agent/main.godc-agent/main_test.godc-api/internal/agentgw/agentgw.godc-api/internal/api/handlers/agentchannel.godc-api/internal/api/handlers/agentchannel_getobject_test.godc-api/internal/api/handlers/agentgw_adapter.godc-api/internal/providers/clusteraccess/agent.godc-api/internal/providers/clusteraccess/clusteraccess_test.godc-api/internal/providers/harvester/getvm_seam_test.godc-api/internal/providers/harvester/list_seam_test.godc-api/internal/providers/harvester/vmwrite_seam_test.godc-api/internal/providers/kubeovn/network_seam_test.godc-api/internal/providers/registry_test.go
Two trivial maintainability nitpicks from review: - AgentBacked.Get computed translateErr(err) twice in the error path; hoist it to a local and reuse it. - The dc-agent startup log hardcoded the advertised op list (had to be hand-updated for get_object). Derive it from the registered dispatcher keys (sorted) so it can't drift from buildDispatchers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RC3BDHCFmJBdxvGVW69FAr
What this changes
The agent's only read op was
get_status, which returns a status-only partial. But the cluster-access seam'sGetis contractually a full-object read (the direct client returns the whole object) — soAgentBacked.Getwas quietly mapping a full read onto a status-only one. The upcoming read-modify-patch write ops (per-VPC NAT and DNS, the cloud-provider service account) read.spec/.datato modify it, so they need a real full-object read through the agent. This adds one.End to end
Executor.GetObject(dynamic get of one object, returned verbatim — spec + status + metadata) and anopGetObjectdispatch handler.opGetObjectwire frame,Session.GetObject, andAgentBacked.Getrepointed at it — re-hydrating the response into the same fullUnstructuredthe direct client returns, so provider code is identical across the direct and agent seams.getpermission the agent already holds forget_status; the request reuses the existingResourceRefshape.Rolling-deploy safety
If a newer dc-api talks to an older agent that predates
get_object,Getfalls back toget_status(the prior status-only read) instead of erroring — so a rolling deploy never hard-fails a read.get_statusandwatch_status(the async status poll/stream) are untouched; a status-only caller (the VM read flow) sees the identical shape on the fallback path.Why now
This is the second protocol prerequisite of the remaining-ops work (after the
listop in the parent PR). With full reads in place, the remaining slices — image, cloud-provider SA, NAT, DNS — are capability onboarding plus a raw-Patch → server-side-apply refactor, no new read primitives.Verification
Both modules build, vet, and pass race-enabled unit tests. New tests cover the executor get, the
opGetObjectwire round-trip, the full-object re-hydration, theOP_UNSUPPORTED→get_statusfallback, and the existing VM read flow. The live-cluster suites need a real connected agent and weren't run here — one live full read through a connected agent is the remaining check before relying on it.Stacked on #199
Branches off
feat/agent-list-op(#199, thelistop). Review/merge that first; this PR's base retargets tocontrolplaneonce it lands.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
get_objectprotocol operation to retrieve full Kubernetes objects (metadata, spec, status) as raw JSON.Bug Fixes
Found=false.Tests
get_object, including found/absent, cluster-scoped refs, and error/unsupported-op scenarios.