Skip to content

Add DeepCopy generation to all API types#4527

Open
mozesl-nokia wants to merge 5 commits into
kptdev:mainfrom
nokia:deepcopy-all
Open

Add DeepCopy generation to all API types#4527
mozesl-nokia wants to merge 5 commits into
kptdev:mainfrom
nokia:deepcopy-all

Conversation

@mozesl-nokia
Copy link
Copy Markdown
Contributor

@mozesl-nokia mozesl-nokia commented May 18, 2026

We have only been generating DeepCopy methods for a few select types, but it would be very useful to do so for all of the API types so it can be easily used by consumers (Porch).

Added the appropriate kubebuilder annotation to be on package level for both the main Kptfile types and fnresult types, the go generate comments, then ran the generation.

Had to add a manual DeepCopyInto method for fnresultv1.Result, since it has a kyaml framework.Result field, which does not have DeepCopyInto.

Deduplicated framework.Result (removed ResultItem).

@netlify
Copy link
Copy Markdown

netlify Bot commented May 18, 2026

Deploy Preview for kptdocs ready!

Name Link
🔨 Latest commit 9471daa
🔍 Latest deploy log https://app.netlify.com/projects/kptdocs/deploys/6a0b3d5e7627d3000723c6a5
😎 Deploy Preview https://deploy-preview-4527--kptdocs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com>
Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com>
Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com>
Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com>
Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com>
Comment on lines +80 to +113
const maxDepth = 1024

func deepCopyInterface(in any, depth uint) any {
if depth > maxDepth {
panic(fmt.Sprintf("reached max deepcopy depth of %d", maxDepth))
}

// return all primitive / non-pointer types
switch t := in.(type) {
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64,
complex64, complex128,
bool,
string,
json.Number:
return t
// inspired by k8s.io/apimachinery/pkg/runtime.DeepCopyJSONValue
case map[string]any:
clone := make(map[string]interface{}, len(t))
for k, v := range t {
clone[k] = deepCopyInterface(v, depth+1)
}
return clone
case []any:
clone := make([]interface{}, len(t))
for i, v := range t {
clone[i] = deepCopyInterface(v, depth+1)
}
return clone
default:
panic(fmt.Sprintf("cannot deepcopy type %T", t))
}
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is very much overkill, so I'm looking for suggestions on what to do instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative would be not to use go structs coming from kyaml at all, but define our own API structs in the kpt repo. That way we can control our own API schemas.

@mozesl-nokia mozesl-nokia marked this pull request as ready for review May 18, 2026 16:44
Copilot AI review requested due to automatic review settings May 18, 2026 16:44
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. go Pull requests that update Go code labels May 18, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands DeepCopy support across the kptfile/v1 and fnresult/v1 API types by enabling package-level controller-gen generation, introducing a shared DeepCopy helper for kyaml/fn/framework result types, and updating render status plumbing to store framework.Results directly (removing the bespoke ResultItem mirror types).

Changes:

  • Enable package-level +kubebuilder:object:generate=true and add/standardize go:generate controller-gen ... object directives for API packages, then check in regenerated zz_generated.deepcopy.go files.
  • Replace kptfilev1.PipelineStepResult.Results/ErrorResults from custom ResultItem slices to framework.Results, and update render executor/test expectations accordingly.
  • Add pkg/api/common DeepCopy utilities (with tests) and manual DeepCopyInto implementations for types containing framework.Results.

Reviewed changes

Copilot reviewed 8 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/api/kptfile/v1/zz_generated.deepcopy.go Regenerated DeepCopy methods for all kptfile/v1 API types.
pkg/api/kptfile/v1/types.go Package-level generation annotation; switch PipelineStepResult to framework.Results; remove ResultItem and related mirror types.
pkg/api/kptfile/v1/deepcopy.go Manual DeepCopyInto for PipelineStepResult to correctly deep copy framework.Results.
pkg/api/fnresult/v1/zz_generated.deepcopy.go New generated DeepCopy stubs for fnresult/v1 (works with manual Result.DeepCopyInto).
pkg/api/fnresult/v1/types.go Package-level generation annotation and go:generate for controller-gen.
pkg/api/fnresult/v1/deepcopy.go Manual DeepCopyInto for Result to deep copy framework.Results.
pkg/api/common/deepcopy.go New shared deepcopy helpers for framework.Result(s) and interface field values.
pkg/api/common/deepcopy_test.go Unit tests covering the new common deepcopy helpers.
internal/util/render/executor.go Store framework.Results directly in PipelineStepResult and filter ErrorResults via framework.Error.
internal/util/render/executor_test.go Update assertions for framework.Severity and remove tests for deleted conversion helper.
go.sum Dependency checksum cleanup (removes unused docker/distribution entries).
Files not reviewed (2)
  • pkg/api/fnresult/v1/zz_generated.deepcopy.go: Language not supported
  • pkg/api/kptfile/v1/zz_generated.deepcopy.go: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +76 to +112
func DeepCopyInterface(in any) any {
return deepCopyInterface(in, 0)
}

const maxDepth = 1024

func deepCopyInterface(in any, depth uint) any {
if depth > maxDepth {
panic(fmt.Sprintf("reached max deepcopy depth of %d", maxDepth))
}

// return all primitive / non-pointer types
switch t := in.(type) {
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64,
complex64, complex128,
bool,
string,
json.Number:
return t
// inspired by k8s.io/apimachinery/pkg/runtime.DeepCopyJSONValue
case map[string]any:
clone := make(map[string]interface{}, len(t))
for k, v := range t {
clone[k] = deepCopyInterface(v, depth+1)
}
return clone
case []any:
clone := make([]interface{}, len(t))
for i, v := range t {
clone[i] = deepCopyInterface(v, depth+1)
}
return clone
default:
panic(fmt.Sprintf("cannot deepcopy type %T", t))
}
Copy link
Copy Markdown
Contributor Author

@mozesl-nokia mozesl-nokia May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I could gather is that these fields should never be unmarshalled as anything other than what DeepCopyJSONValue supports. I did consider (and half-write) a completely generic reflect-based deepcopy, but that seemed even more unnecessary.

Although, the exact type is determined exclusively by what the actual KRM function (or kyaml filter) sets.

If we do want to go the reflect route, there definitely are libraries for it.

if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(framework.Result)
// (*in).DeepCopyInto(*out)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason it was left there was to specifically indicate that we are replacing that method.

step.ExitCode = last.ExitCode
step.Results = frameworkResultsToItems(last.Results)
step.Results = last.Results
for _, ri := range step.Results {
Stderr string `yaml:"stderr,omitempty" json:"stderr,omitempty"`
ExitCode int `yaml:"exitCode" json:"exitCode"`

Results framework.Results `yaml:"results,omitempty" json:"results,omitempty"`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to use the kyaml framework Results type here that is missing DeepCopy? Why don't we use our own structure defined in the kpt repo that we control and can generate a proper DeepCopy method for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functions themselves either insert into a *framework.ResourceList (containing *framework.Results) or the equivalent *fnsdk.ResourceList, which used to be a carbon copy, but now has the string version of CurrentValue and ProposedValue (not yet used by catalog functions).

Thus, the functions themselves still need their output converted, which will have to be done on Porch side as well.

We could make it so that the SDK version (an easily serializable one) is used by all functions, and handled accordingly by kpt (no need for conversion then), but that would break compatibility with kyaml filters I assume (if we even care about that).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update Go code size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants