name: kcc-direct-controller-implementer description: Implement the controller, mappers, and fuzzer for a direct KCC resource, ensuring package isolation and CI compliance. Use this when implementing the main reconciliation logic for a "direct" resource.
KCC Direct Controller Implementer
This skill guides the implementation of the controller, mappers, and fuzzer for direct KCC resources, with a focus on package isolation to prevent symbol collisions and rigorous CI validation.
Inputs
resource_kind: The KCC Kind.package_path: The isolated package directory (e.g.,pkg/controller/direct/vertexai/examplestore/).proto_package: The GCP proto package (e.g.,google.cloud.aiplatform.v1).
Workflow
Package Isolation: You MUST implement all controller-related logic in the provided
package_path. This prevents symbol collisions inmapper.generated.go.Brownfield Migrations: For brownfield resources (with existing terraform/DCL controllers), we do NOT immediately change the default controller to
directin Go type labels or static config map defaults.- Keep the default controller as legacy (TF/DCL), so users can opt-in gradually.
- Always test both the direct and legacy controllers. To do this, add the resource Kind to the
forceDirect = trueswitch cases insidetests/e2e/unified_test.go.
Controller Structure & Patterns:
- Proto Format Desired State: Convert the KRM Spec to its Proto representation once in
AdapterForObjectand store it as a proto struct pointer (e.g.,*pb.MyResourceordesired) in the adapter, rather than duplicating conversion logic in bothCreateandUpdate. - NormalizeReferences: Always call
common.NormalizeReferencesinAdapterForObjectto resolve any resource references:if err := common.NormalizeReferences(ctx, reader, obj, nil); err != nil { return nil, fmt.Errorf("normalizing references: %w", err) } - Client Creation Options: Do NOT build the authenticated HTTP client manually. Instead, retrieve configuration options using
RESTClientOptions()and construct the REST client:var opts []option.ClientOption opts, err := m.config.RESTClientOptions() if err != nil { return nil, err } gcpClient, err := gcp.NewConfigRESTClient(ctx, opts...) - Diff Comparison (
compare<Resource>pattern): Implement a dedicatedcompare<Resource>helper function to compare the spec fields of actual/desired proto structures by round-tripping actual via its KRM Spec and callingtags.DiffForTopLevelFields:func compareResource(ctx context.Context, actual, desired *pb.MyResource) (*structuredreporting.Diff, *fieldmaskpb.FieldMask, error) { var maskedActual *pb.MyResource { mapCtx := &direct.MapContext{} spec := MyResourceSpec_FromProto(mapCtx, actual) if mapCtx.Err() != nil { return nil, nil, mapCtx.Err() } maskedActual = MyResourceSpec_ToProto(mapCtx, spec) if mapCtx.Err() != nil { return nil, nil, mapCtx.Err() } } diffs, updateMask, err := tags.DiffForTopLevelFields(ctx, desired.ProtoReflect(), maskedActual.ProtoReflect()) if err != nil { return nil, nil, err } return diffs, updateMask, nil } - Structured Reporting & updateStatus: In the
Updatemethod, usediffs.HasDiff()to report exact diffs back to the user viastructuredreporting.ReportDiff. Always call a helperupdateStatusfunction to update the Kube status at the end of bothCreateandUpdatereconciliation paths:func (a *MyResourceAdapter) updateStatus(ctx context.Context, op directbase.Operation, latest *pb.MyResource) error { mapCtx := &direct.MapContext{} status := MyResourceStatus_FromProto(mapCtx, latest) if mapCtx.Err() != nil { return mapCtx.Err() } return op.UpdateStatus(ctx, status, nil) }
- Proto Format Desired State: Convert the KRM Spec to its Proto representation once in
Mappers: Verify
mapper.generated.goand manual mappers. Ensure all references use the standardRefpattern.Fuzzer: Implement
<resource_lower>_fuzzer.goand register it withfuzztesting.RegisterKRMFuzzer.- Fuzzer Implementation Guidelines:
- ALWAYS prefer and encourage using the fluent
f.SpecField(".foo")andf.StatusField(".bar")methods rather than inserting directly intoSpecFields/StatusFieldssets (e.g. avoidf.SpecFields.Insert...). - For unhandled or unimplemented fields, prefer highly descriptive helper methods (such as
f.Unimplemented_NotYetTriaged(".baz"),f.Unimplemented_Identity(".id"), or other specific variants) rather than standard inserts intoUnimplementedFieldssets. This categorizes why we are not handling specific fields.
- ALWAYS prefer and encourage using the fluent
- Fuzzer Implementation Guidelines:
Final Generation & Reporting: Run
make ready-prfrom the repository root. This is a critical step that:- Regenerates all Go clients in
pkg/clients/generated. - Updates global CRD reports (
docs/reports/crd_report.mdand.csv). - Runs
make fmtandmake vet. YOU MUST COMMIT ALL RESULTING CHANGES.
- Regenerates all Go clients in
Validation & Last-Mile Tests: Run the following tests to ensure CI compliance:
- Fuzzing:
dev/ci/presubmits/fuzz-roundtrippers - E2E Scaffolding:
dev/ci/presubmits/tests-e2e-fixtures-direct - Schema Integrity:
go test ./pkg/crd/template/... - API Field Coverage:
go test ./tests/apichecks/....- If
TestCRDFieldPresenceInTestsForAlphafails, you may need to regenerate the exceptions file by running it withWRITE_GOLDEN_OUTPUT=1.
- If
- Fuzzing:
Journaling
Append any controller/reconciliation quirks or API check hurdles to .gemini/journals/<service>.md using the format described in the kcc-agentic-journaler skill.