generator: make generated Go types implement runtime.Object and provide AddToScheme - #162
Conversation
📝 WalkthroughWalkthroughGenerated Go models can now optionally implement Kubernetes ChangesGo runtime object generation
Estimated code review effort: 5 (Critical) | ~90+ minutes Sequence Diagram(s)sequenceDiagram
participant CLI as Crossplane CLI
participant Config as Config.Features
participant Generator as Schema generator
participant GoModels as Generated Go models
participant Scheme as Generated scheme helpers
CLI->>Config: set generateGoRuntimeObjects
Config->>Generator: pass runtime-object option
Generator->>GoModels: add DeepCopy and runtime.Object methods
Generator->>Scheme: write GroupVersion and AddToScheme
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cmd/crossplane/function/generate_test.go (1)
291-328: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winPlease add a flag-enabled regression case here.
Thanks for updating the signature. This still passes only
&config.Config{}, so it won't catchRunforgetting to threadGenerateGoRuntimeObjectsinto schema generation. Could you add aruncase withFeatures.GenerateGoRuntimeObjects: trueand assert the generated Go models include the runtime-object artifacts?🤖 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 `@cmd/crossplane/function/generate_test.go` around lines 291 - 328, Add a new Run regression case in TestRunErrors that exercises generateCmd.Run with config.Config.Features.GenerateGoRuntimeObjects enabled, so the test verifies schema generation still threads that flag through. Update the existing run-path setup in generate_test.go to use a config with Features.GenerateGoRuntimeObjects: true and assert the generated Go model artifacts include the runtime-object outputs, using generateCmd.Run and the related schema generation helpers as the main reference points.cmd/crossplane/function/generate.go (1)
148-161: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winHonor
features.generateGoRuntimeObjectsin schema generation.Thanks for threading
cfgintoRun. Did you mean to mirrorcmd/crossplane/project/build.goandcmd/crossplane/project/run.gohere? Line 160 still builds the schema manager withgenerator.AllLanguages()only, socrossplane function generateignores the new config key and keeps generating the default Go models even when users opt in.Suggested fix
schemaMgr := manager.New( c.schemasFS, - generator.Filter(generator.AllLanguages(), c.proj.Spec.Schemas.GetLanguages()), + generator.Filter( + generator.AllLanguages(generator.WithGoRuntimeObjects(cfg.Features.GenerateGoRuntimeObjects)), + c.proj.Spec.Schemas.GetLanguages(), + ), runner.NewRealSchemaRunner(runner.WithImageConfig(c.proj.Spec.ImageConfigs)), )🤖 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 `@cmd/crossplane/function/generate.go` around lines 148 - 161, The schema generation path in generateCmd.Run is still hardcoded to generator.AllLanguages(), so it ignores features.generateGoRuntimeObjects from cfg and keeps producing the default Go models. Update the schema manager setup in Run to mirror the config-aware filtering used in project build/run, using cfg to decide whether Go runtime objects should be included or excluded. Keep the change localized around generateCmd.Run, generator.Filter, and manager.New so the selected schema languages honor the new feature flag.
🧹 Nitpick comments (1)
internal/schemas/generator/runtimeobject_test.go (1)
53-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThanks for adding focused coverage — would you mind table-driving these cases?
They already follow the PascalCase/std-lib parts of the repo test conventions, but this path expects args/want-style tables with named cases. Folding the DeepCopy and root-type scenarios into one table would make the next generator edge case much easier to add without duplicating setup. As per path instructions,
**/*_test.go: "Enforce table-driven test structure: PascalCase test names (no underscores), args/want pattern, use cmp.Diff with cmpopts.EquateErrors() for error testing. Check for proper test case naming and reason fields."🤖 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 `@internal/schemas/generator/runtimeobject_test.go` around lines 53 - 143, The two separate tests in addRuntimeObjects should be folded into a single table-driven test using named cases, args/want-style inputs, and shared setup so future generator edge cases are easier to add. Update TestAddRuntimeObjectsDeepCopy and TestAddRuntimeObjectsRootType to use a table with clear case names and expected method sets, while keeping the existing assertions around addRuntimeObjects and roMethods; preserve the current coverage for DeepCopy-only vs root-type method generation and make the structure match the *_test.go table-driven convention.Source: Path instructions
🤖 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.
Inline comments:
In `@internal/schemas/generator/runtimeobject_integration_test.go`:
- Around line 85-92: The integration test checks for k8s.io/apimachinery in
generated models/go.mod, but it currently skips when dependency resolution
fails, which can hide broken or drifting generated deps. Update the
runtimeobject integration test(s) to fail the test instead of calling t.Skipf
when go mod download or similar dependency resolution breaks, and use an
explicit opt-out for offline/local runs only if needed. Keep the assertion
around the generated go.mod content in the same test flow so compile-gate
regressions in the runtimeobject generator are caught by CI.
In `@internal/schemas/generator/runtimeobject.go`:
- Around line 149-172: Named map/slice aliases are being treated as scalars, so
DeepCopy emits shallow pointer copies for fields like *Labels and shares backing
storage. Update the type classification path in collectScalarTypes/classifyElem
to resolve local named aliases before falling back to scalar handling, so named
collection types are recognized as composite types and copied deeply. Then
adjust writeFieldCopy to use the deep-copy path for those aliases instead of
generating **out = **in.
---
Outside diff comments:
In `@cmd/crossplane/function/generate_test.go`:
- Around line 291-328: Add a new Run regression case in TestRunErrors that
exercises generateCmd.Run with config.Config.Features.GenerateGoRuntimeObjects
enabled, so the test verifies schema generation still threads that flag through.
Update the existing run-path setup in generate_test.go to use a config with
Features.GenerateGoRuntimeObjects: true and assert the generated Go model
artifacts include the runtime-object outputs, using generateCmd.Run and the
related schema generation helpers as the main reference points.
In `@cmd/crossplane/function/generate.go`:
- Around line 148-161: The schema generation path in generateCmd.Run is still
hardcoded to generator.AllLanguages(), so it ignores
features.generateGoRuntimeObjects from cfg and keeps producing the default Go
models. Update the schema manager setup in Run to mirror the config-aware
filtering used in project build/run, using cfg to decide whether Go runtime
objects should be included or excluded. Keep the change localized around
generateCmd.Run, generator.Filter, and manager.New so the selected schema
languages honor the new feature flag.
---
Nitpick comments:
In `@internal/schemas/generator/runtimeobject_test.go`:
- Around line 53-143: The two separate tests in addRuntimeObjects should be
folded into a single table-driven test using named cases, args/want-style
inputs, and shared setup so future generator edge cases are easier to add.
Update TestAddRuntimeObjectsDeepCopy and TestAddRuntimeObjectsRootType to use a
table with clear case names and expected method sets, while keeping the existing
assertions around addRuntimeObjects and roMethods; preserve the current coverage
for DeepCopy-only vs root-type method generation and make the structure match
the *_test.go table-driven convention.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 799a4495-015a-4313-a14e-5e2a177d5c30
📒 Files selected for processing (13)
cmd/crossplane/config/help/config.mdcmd/crossplane/config/set.gocmd/crossplane/function/generate.gocmd/crossplane/function/generate_test.gocmd/crossplane/main.gocmd/crossplane/project/build.gocmd/crossplane/project/run.gointernal/config/config.gointernal/schemas/generator/go.gointernal/schemas/generator/interface.gointernal/schemas/generator/runtimeobject.gointernal/schemas/generator/runtimeobject_integration_test.gointernal/schemas/generator/runtimeobject_test.go
75d816f to
56a98a2
Compare
Apply accessor generation after Go post-processing at the call sites via a new applyAccessors helper, rather than folding it into generateGo. This mirrors the runtime.Object generation approach in crossplane#162 so both features use the same "post-process the generated code after the fact" pattern, and makes generateGo's signature identical across both, keeping the two changes easy to reconcile. Generating accessors after fixK8sTypeNames/removeSelfImports also means they reference the final type names. Skip unexported struct fields when emitting accessors: generated models don't currently have any, but an accessor for one would be useless to external consumers and could produce oddly-cased method names. Reuse receiverTypeName from accessors.go in the tests instead of redefining an equivalent renderRecv helper. Signed-off-by: Erik Miller <erik.miller@gusto.com>
adamwg
left a comment
There was a problem hiding this comment.
This looks good to me overall, and I like that #160 and this PR now work similarly.
One bug I noticed in testing: the core/v1 API group gets the wrong group name in groupversion_info.go:
var GroupVersion = schema.GroupVersion{Group: "core.k8s.io", Version: "v1"}I think this one is a special case, since the Group should actually be empty. Other built-in groups I spot checked (apps/v1, networking.k8s.io/v1, batch/v1) look correct.
| // generateGoRuntimeObjects feature is enabled. It additionally requires | ||
| // k8s.io/apimachinery (used by the generated runtime.Object and AddToScheme | ||
| // code) and its transitive dependencies. | ||
| const goModContentsRuntimeObjects = `module dev.crossplane.io/models |
There was a problem hiding this comment.
I think there would be no harm in using this go.mod and go.sum unconditionally. There will be a couple of superfluous entries when runtime.Object generation is disabled, but go build is fine with that and go mod tidy will clean it up if anyone cares. One thing fewer to maintain and test if we just have one version.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
internal/schemas/generator/go.go (1)
636-675: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUser-facing messages in
writeGroupVersionInfo.Thanks for adding the scheme setup path. The wrapper import is already
crossplane-runtime/pkg/errors, but the surrounding messages still describe generator internals; could the messages instead say what package/schema setup failed, such as “failed to write generated scheme registration for package...”?🤖 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 `@internal/schemas/generator/go.go` around lines 636 - 675, Update the error messages in writeGroupVersionInfo to describe the user-facing failure in generated scheme registration, including the relevant package or schema context where available, instead of internal operations like stat, format, create, or write. Preserve error wrapping and the existing behavior for each failure path.Source: Path instructions
🤖 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.
Inline comments:
In `@internal/schemas/generator/go.go`:
- Around line 333-336: Update the k8sPkgMetaV1 handling in the package/group
generation flow to reuse getK8sPackageInfo(pkg) instead of assigning
meta.k8s.io, meta, and v1 directly. Preserve the canonical meta.core.k8s.io
mapping and apiGroup synthetic-group behavior used by the OpenAPI path.
---
Nitpick comments:
In `@internal/schemas/generator/go.go`:
- Around line 636-675: Update the error messages in writeGroupVersionInfo to
describe the user-facing failure in generated scheme registration, including the
relevant package or schema context where available, instead of internal
operations like stat, format, create, or write. Preserve error wrapping and the
existing behavior for each failure path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 507815b2-aef6-47c1-b768-2c7195b9ee23
📒 Files selected for processing (16)
.golangci.ymlcmd/crossplane/config/help/config.mdcmd/crossplane/config/set.gocmd/crossplane/function/generate.gocmd/crossplane/function/generate_test.gocmd/crossplane/main.gocmd/crossplane/project/build.gocmd/crossplane/project/run.gointernal/config/config.gointernal/schemas/generator/go.gointernal/schemas/generator/interface.gointernal/schemas/generator/interface_test.gointernal/schemas/generator/runtimeobject.gointernal/schemas/generator/runtimeobject_compilegate_test.gointernal/schemas/generator/runtimeobject_integration_test.gointernal/schemas/generator/runtimeobject_test.go
🚧 Files skipped from review as they are similar to previous changes (9)
- cmd/crossplane/project/build.go
- cmd/crossplane/config/help/config.md
- cmd/crossplane/project/run.go
- internal/config/config.go
- cmd/crossplane/function/generate_test.go
- cmd/crossplane/config/set.go
- cmd/crossplane/main.go
- internal/schemas/generator/interface.go
- internal/schemas/generator/runtimeobject.go
adamwg
left a comment
There was a problem hiding this comment.
I gave the latest version of this a test and it is working 👍 I'm able to use the generated schemas with SDK helpers like resource.AsObject and comopsed.From, which lets me eliminate the manual JSON round-trip conversions in my example.
The only thing missing, as in #160, is threading the config through to the other locations where we generate schemas: crossplane dependency ... and the render commands.
|
@adamwg threading of the config should now be done in both PRs. Would it be worth collapsing them together into a single PR so they don't end up with merge conflicts? |
@erikmiller-gusto I've merged #160. This one will need a rebase, but I expect the merge conflicts will be relatively easy to resolve. |
Fixes crossplane#143. Behind a new opt-in feature flag, generated Go models gain controller-gen-style DeepCopyInto/DeepCopy on every struct, and on root types (structs with APIVersion + Kind + Metadata, i.e. the resource and its List) DeepCopyObject, GetObjectKind/GroupVersionKind/SetGroupVersionKind reading and writing the typed APIVersion/Kind fields, and an init registering the type with the package SchemeBuilder. Each package containing root types gets a groupversion_info.go defining GroupVersion, SchemeBuilder and AddToScheme, apimachinery-only with no controller-runtime dependency. Users no longer have to set apiVersion/kind by hand on composed resources. Off by default: crossplane config set features.generateGoRuntimeObjects true The flag is threaded from config through every command that generates schemas: project build, project run, function generate, dependency add, dependency update-cache, composition generate, composition render / render, and operation render. The render commands hand the same generators to the dependency manager, so dependency schemas match the project's own. dependency clean-cache is the exception: it only removes generated schemas, so it keeps the flag-off default in dependency.NewManager. The generated models module requires k8s.io/apimachinery, pinned to the version the Go function template uses so a function consuming the models via a replace statement still resolves everything from the template's go.sum. There is one go.mod and go.sum regardless of the flag: an unused requirement is harmless to go build, and one set of module files is one thing fewer to maintain and test. Built-in Kubernetes types register under their real API group. The group labels the generator uses for those packages are synthetic and only drive the directory layout, so a goPackage now carries the layout group and the API group separately. Without that, core/v1 and meta/v1 registered under "core.k8s.io" and "meta.core.k8s.io", GVKs that disagree with what each type's own GroupVersionKind() reports. Tests cover DeepCopy generation (root vs non-root detection, scalar, struct and named-collection-alias fields), the API group written into groupversion_info.go, the feature-flag plumbing, and a default-off case. Compile gates materialize the generated module and build it, including a behavioral test for deep-copy independence and AddToScheme GVK round-tripping. They shell out to the Go toolchain, so they need network access and a writable module cache that the Nix sandbox running our unit tests does not have; they sit behind a compilegate build tag rather than a runtime skip, which would report as a test that ran: go test -tags compilegate ./internal/schemas/generator/... .golangci.yml lints the tagged file so it cannot rot. Signed-off-by: Erik Miller <erik.miller@gusto.com> Rebased onto main after crossplane#160, which landed the Go model accessors and the generator Option plumbing this change was written against. Four resolutions worth calling out: - generateK8sSharedSchemas, generateK8sPackageCode, generateModelsWithGVK and generateGVKGroupCode took an `accessors bool`. Rather than grow a second positional bool they now take the goGenerator, which already holds both flags. - Both features emit methods onto the same structs, so the order matters. runtime.Object runs first because addAccessors skips method names that already exist and applyRuntimeObjects does not: a field named objectKind yields a GetObjectKind accessor that collides with the one root types get from schema.ObjectKind. Reversed, the struct gets two and the generated module stops compiling. TestRuntimeObjectsThenAccessorsDeduplicates pins it. - Two compile gates build the generated module with both features on, which neither feature's own gate covered. - TestAllLanguagesWithGoRuntimeObjects became TestAllLanguagesGoOptions and now asserts both flags reach the Go generator independently and together. The eight commits this branch carried were squashed into one: they were review iterations that introduced and then removed code, and replaying them over the new base meant resolving conflicts against states that no longer exist. Signed-off-by: Erik Miller <erik.miller@gusto.com>
6bdb60c to
0990a75
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/schemas/generator/runtimeobject_compilegate_test.go (1)
239-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a
reasonfield to this table-driven test.Thanks for the clear built-in GVK checks! Every other table-driven test in this cohort (
TestAllLanguagesGoOptions,TestAddRuntimeObjects) includes areasonfield explaining what each case verifies. Thiscasesmap is missing one. Add areasonfield for consistency and to help future readers understand whyCoreV1,MetaV1, andAutoscalingare the representative cases chosen out of the 5 registered schemes.As per path instructions,
**/*_test.go: "Check for proper test case naming and reason fields."♻️ Proposed fix
cases := map[string]struct { + reason string obj runtime.Object want schema.GroupVersionKind }{ - "CoreV1": {obj: &corev1.Pod{}, want: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}}, - "MetaV1": {obj: &metav1.Status{}, want: schema.GroupVersionKind{Version: "v1", Kind: "Status"}}, - "Autoscaling": {obj: &autoscalingv1.TokenRequest{}, want: schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "TokenRequest"}}, + "CoreV1": {reason: "core group types report an empty group in their GVK", obj: &corev1.Pod{}, want: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}}, + "MetaV1": {reason: "meta group types report an empty group in their GVK", obj: &metav1.Status{}, want: schema.GroupVersionKind{Version: "v1", Kind: "Status"}}, + "Autoscaling": {reason: "non-core built-in groups report their real group name", obj: &autoscalingv1.TokenRequest{}, want: schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "TokenRequest"}}, },🤖 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 `@internal/schemas/generator/runtimeobject_compilegate_test.go` around lines 239 - 246, Add a reason string field to the table-driven cases in the GVK test, and populate it with concise explanations for the CoreV1, MetaV1, and Autoscaling representative cases. Update the test assertions or failure reporting to use each case’s reason consistently with TestAllLanguagesGoOptions and TestAddRuntimeObjects.
🤖 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 `@internal/schemas/generator/runtimeobject_compilegate_test.go`:
- Around line 239-246: Add a reason string field to the table-driven cases in
the GVK test, and populate it with concise explanations for the CoreV1, MetaV1,
and Autoscaling representative cases. Update the test assertions or failure
reporting to use each case’s reason consistently with TestAllLanguagesGoOptions
and TestAddRuntimeObjects.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: c1903e34-daa0-400d-b032-afea82833873
📒 Files selected for processing (20)
.golangci.ymlcmd/crossplane/composition/generate.gocmd/crossplane/config/help/config.mdcmd/crossplane/config/set.gocmd/crossplane/dependency/add.gocmd/crossplane/dependency/cache.gocmd/crossplane/function/generate.gocmd/crossplane/function/generate_test.gocmd/crossplane/project/build.gocmd/crossplane/project/run.gocmd/crossplane/render/op/cmd.gocmd/crossplane/render/xr/cmd.gointernal/config/config.gointernal/schemas/generator/go.gointernal/schemas/generator/interface.gointernal/schemas/generator/interface_test.gointernal/schemas/generator/runtimeobject.gointernal/schemas/generator/runtimeobject_compilegate_test.gointernal/schemas/generator/runtimeobject_integration_test.gointernal/schemas/generator/runtimeobject_test.go
🚧 Files skipped from review as they are similar to previous changes (10)
- cmd/crossplane/config/set.go
- .golangci.yml
- internal/config/config.go
- internal/schemas/generator/interface.go
- cmd/crossplane/config/help/config.md
- cmd/crossplane/function/generate_test.go
- internal/schemas/generator/runtimeobject_integration_test.go
- cmd/crossplane/function/generate.go
- internal/schemas/generator/runtimeobject.go
- internal/schemas/generator/go.go
Description of your changes
Fixes #143.
What
Generates
runtime.Objectsupport for the Go schema models, behind a new opt-infeature flag. When enabled, generated Go models can be registered in a
runtime.Schemeand used with k8s ecosystem libraries, so users no longer haveto set
apiVersion/kindby hand on composed resources.For every generated struct:
DeepCopyInto/DeepCopy.For root types (structs with
APIVersion+Kind+Metadata, i.e. theresource and its
List):DeepCopyObject() runtime.ObjectGetObjectKind/GroupVersionKind/SetGroupVersionKind(the type implementsschema.ObjectKind, reading/writing the typedAPIVersion/Kindfields)init()registering the type with the packageSchemeBuilder.Per package containing root types, a
groupversion_info.godefiningGroupVersion,SchemeBuilderandAddToScheme(apimachinery-only; nocontroller-runtime dependency).
Feature gate
Off by default; enable with:
The flag is threaded from config through every command that generates schemas:
project build,project run,function generate,dependency add,dependency update-cache,composition generate,composition render/render, andoperation render. The render commands also hand the samegenerators to the dependency manager, so dependency schemas match the project's
own.
dependency clean-cacheis the one exception — it only removes generatedschemas, so it keeps the flag-off default in
dependency.NewManager.When the flag is off, no
runtime.Object,DeepCopyorgroupversion_info.gocode is emitted.
Dependencies
The generated models module requires
k8s.io/apimachinery, pinned to v0.33.0to match the function Go template — so a generated function that consumes the
models via
replacestill resolves everything from the template's existinggo.sum(verified locally by building a function against the models using theshipped template
go.mod/go.sum).There is one
go.mod/go.sumregardless of the flag. With the flag off nothingimports apimachinery, but the requirement stays: an unused requirement is
harmless to
go build,go mod tidyremoves it for anyone who cares, and oneset of module files is one thing fewer to maintain and test.
Testing
Unit tests for the DeepCopy / runtime.Object generation (root vs non-root
detection, scalar/struct/alias field handling), for the API group written into
groupversion_info.gofor built-in Kubernetes packages, and for the featureflag plumbing.
A default-off test confirming no
runtime.Objectartifacts are emitted.Compile gates that materialize the generated module (the CRD path, the
OpenAPI path, the flag-off path, and both features on) and
go build/go testit — thesecatch
DeepCopyIntocodegen bugs that parse cleanly but don't type-check. Oneof them is a behavioral test asserting deep-copy independence for scalar,
slice-of-struct and map fields,
AddToSchemeGVK round-tripping, and thatSetGroupVersionKindwrites the typed fields.The compile gates shell out to the Go toolchain to resolve the generated
module's dependencies, so they need network access and a writable module
cache. The Nix sandbox our unit tests run in has neither, and the generated
module pins the apimachinery version the function template uses rather than
the one this repo depends on, so it can't resolve from gomod2nix either. They
are therefore behind a
compilegatebuild tag rather than a runtime skip,which would report as a test that ran:
go test -tags compilegate ./internal/schemas/generator/....golangci.ymllints the tagged file so it can't rot. Happy to add anetwork-enabled CI job that runs them as a follow-up if you'd like the gate
enforced on every PR — didn't want to add a non-hermetic job unilaterally.
Worth flagging:
accessors_test.gofrom feat: add GetX/SetX accessors to generated Go models #160 solves the same problem with aruntime skip, so the package now has two conventions. Happy to converge on
either — I left it alone rather than change merged code in a rebase.
Rebased onto main after #160
#160 landed the accessors feature and the generator
Optionplumbing thischange was written against, so the diff here is a good deal smaller than it was.
Four things the rebase decided, beyond the mechanical "both flags, not either":
generateK8sSharedSchemas,generateK8sPackageCode,generateModelsWithGVKand
generateGVKGroupCodetook anaccessors bool. Rather than grow a secondpositional bool they now take the
goGenerator, which already holds bothflags.
onto the same structs.
runtime.Objectruns first becauseaddAccessorsskips method names that already exist and
applyRuntimeObjectsdoesn't — afield named
objectKindyields aGetObjectKindaccessor that collides withthe one root types get from
schema.ObjectKind. Reversed, the struct gets twoand the module stops compiling.
feature's own gate covered the combination.
iterations that introduced and then removed code, so replaying them meant
resolving conflicts against states that no longer exist.
Known limitation
DeepCopyIntodecides struct-vs-scalar for cross-package types via ASTheuristics, with a small denylist of k8s alias types that have no
DeepCopyInto(
Time,MicroTime,FieldsV1,RawExtension). That denylist was validatedagainst the test fixtures (incl. the large
azure_linux_function_appCRD); areal CRD referencing a k8s alias type not covered by the fixtures could emit a
DeepCopyIntocall on a type that lacks it. The compile gate catches this foranything in the fixtures; broadening detection (e.g. deriving the alias set
programmatically) is a possible follow-up.
I have:
./nix.sh flake checkto ensure this PR is ready for review.Linked a PR or a docs tracking issue to document this change.Addedbackport release-x.ylabels to auto-backport this PR.On the two struck items: the
crossplane confighelp text this PR extends isrendered into the generated command reference, so
crossplane/docspicks the newkey up without a separate change. And this is a new opt-in feature rather than a
fix, so it isn't a backport candidate.
Need help with this checklist? See the cheat sheet.