Skip to content

✨ Generate and use ApplyConfiguration types for Server-Side Apply#2515

Open
joelanford wants to merge 2 commits intooperator-framework:mainfrom
joelanford:generate-applyconfigurations
Open

✨ Generate and use ApplyConfiguration types for Server-Side Apply#2515
joelanford wants to merge 2 commits intooperator-framework:mainfrom
joelanford:generate-applyconfigurations

Conversation

@joelanford
Copy link
Member

Description

Generate typed apply configuration structs for all OLM API types and migrate Boxcutter from the deprecated client.Patch(ctx, rev, client.Apply, ...) to typed client.Apply(ctx, rev, ...).

The project previously lacked generated apply configuration types, which forced Boxcutter to construct full API objects and use the deprecated client.Patch with client.Apply option for Server-Side Apply. This PR generates those types and migrates all ClusterExtensionRevision construction and application to use them natively.

Key changes:

  • Generate AC types for ClusterCatalog, ClusterExtension, and ClusterExtensionRevision using controller-gen's applyconfiguration generator
  • Add +genclient and +genclient:nonNamespaced markers to cluster-scoped API types so generated constructors correctly omit the namespace parameter
  • ClusterExtensionRevisionGenerator interface returns AC types built via the fluent builder pattern
  • Rename createOrUpdate to apply; uses client.Apply directly
  • Owner references set via metav1ac.OwnerReference() builder instead of controllerutil.SetControllerReference
  • BoxcutterStorageMigrator uses client.Apply instead of client.Create
  • PhaseSort and helpers operate on AC types natively

Reviewer Checklist

  • API Go Documentation
  • Tests: Unit Tests (and E2E Tests, if appropriate)
  • Comprehensive Commit Messages
  • Links to related GitHub Issue(s)

Copilot AI review requested due to automatic review settings February 18, 2026 15:48
@openshift-ci openshift-ci bot requested review from dtfranz and tmshort February 18, 2026 15:48
@netlify
Copy link

netlify bot commented Feb 18, 2026

Deploy Preview for olmv1 ready!

Name Link
🔨 Latest commit 6ff4d55
🔍 Latest deploy log https://app.netlify.com/projects/olmv1/deploys/6996864ae7488a000898bdf3
😎 Deploy Preview https://deploy-preview-2515--olmv1.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

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

@openshift-ci
Copy link

openshift-ci bot commented Feb 18, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign pedjak for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Comment on lines 31 to +35
GroupVersion = schema.GroupVersion{Group: "olm.operatorframework.io", Version: "v1"}

// SchemeGroupVersion is an alias for GroupVersion, required by the
// generated apply configuration code.
SchemeGroupVersion = GroupVersion
Copy link
Contributor

Choose a reason for hiding this comment

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

Does GroupVersion change at all after initialization, same with SchemeGroupVersion. Are these treated as constants?

Copy link
Member Author

Choose a reason for hiding this comment

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

Assumed to be constant. If something changes it, I'd consider it a bug.

Copy link
Contributor

Choose a reason for hiding this comment

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

Shame golang won't let you declare them as a constant.

Copy link
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 generates typed ApplyConfiguration structs for all OLM API types (ClusterCatalog, ClusterExtension, and ClusterExtensionRevision) and migrates the Boxcutter component from the deprecated client.Patch(ctx, rev, client.Apply, ...) pattern to the typed client.Apply(ctx, rev, ...) method for Server-Side Apply.

Changes:

  • Generate ApplyConfiguration types using controller-gen's applyconfiguration generator
  • Add +genclient and +genclient:nonNamespaced markers to cluster-scoped API types
  • Migrate ClusterExtensionRevisionGenerator interface and implementations to return AC types
  • Update PhaseSort and helper functions to operate on AC types natively
  • Replace client.Patch with client.Apply throughout Boxcutter and BoxcutterStorageMigrator
  • Set owner references via metav1ac.OwnerReference() builder instead of controllerutil.SetControllerReference

Reviewed changes

Copilot reviewed 38 out of 38 changed files in this pull request and generated no comments.

Show a summary per file
File Description
api/v1/groupversion_info.go Add AC generation markers and SchemeGroupVersion alias
api/v1/clustercatalog_types.go Add +genclient markers for AC generation
api/v1/clusterextension_types.go Add +genclient markers for AC generation
api/v1/clusterextensionrevision_types.go Add +genclient markers for AC generation
api/v1/ac/*.go Generated ApplyConfiguration types for all API types
internal/operator-controller/applier/phase.go Update PhaseSort to use AC types with pointer returns
internal/operator-controller/applier/phase_test.go Update test expectations to use AC types
internal/operator-controller/applier/boxcutter.go Migrate to typed Apply, update generator interface
internal/operator-controller/applier/boxcutter_test.go Update mocks and expectations for AC types
go.mod Move structured-merge-diff from indirect to direct dependency
Makefile Add AC generation step to generate target
Comments suppressed due to low confidence (3)

internal/operator-controller/applier/phase.go:137

  • Potential nil pointer dereference: The function compareClusterExtensionRevisionObjects accesses a.Object and b.Object without checking if they are nil. If an ApplyConfiguration object is created without calling WithObject(), this will cause a panic. Consider adding nil checks at the beginning of the function to handle this edge case gracefully.
    internal/operator-controller/applier/phase.go:149
  • Potential nil pointer dereference: The PhaseSort function accesses obj.Object.GroupVersionKind() on line 147 without checking if obj.Object is nil. This could panic if an ApplyConfiguration object is created without setting the Object field. Consider adding validation to skip or handle objects with nil Object fields.
    internal/operator-controller/applier/boxcutter.go:670
  • Potential nil pointer dereference: The function revisionManagementPerms dereferences rev.GetName() on line 662 without checking if it returns nil. While GetName() ensures the ObjectMeta exists, it can still return a nil pointer if the Name was never set. Consider adding a nil check or ensuring the name is always set before calling this function.

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

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 11.05991% with 579 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.16%. Comparing base (130c987) to head (6ff4d55).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
applyconfigurations/api/v1/clustercatalog.go 0.00% 94 Missing ⚠️
applyconfigurations/api/v1/clusterextension.go 0.00% 94 Missing ⚠️
...yconfigurations/api/v1/clusterextensionrevision.go 0.00% 94 Missing ⚠️
applyconfigurations/utils.go 0.00% 57 Missing ⚠️
applyconfigurations/api/v1/clusterextensionspec.go 0.00% 20 Missing ⚠️
applyconfigurations/api/v1/catalogfilter.go 0.00% 18 Missing ⚠️
applyconfigurations/api/v1/clustercatalogstatus.go 0.00% 17 Missing ⚠️
...figurations/api/v1/clusterextensionrevisionspec.go 0.00% 17 Missing ⚠️
...plyconfigurations/api/v1/clusterextensionstatus.go 0.00% 17 Missing ⚠️
applyconfigurations/api/v1/clustercatalogspec.go 0.00% 11 Missing ⚠️
... and 20 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2515      +/-   ##
==========================================
- Coverage   73.21%   65.16%   -8.06%     
==========================================
  Files         102      130      +28     
  Lines        8505     9085     +580     
==========================================
- Hits         6227     5920     -307     
- Misses       1802     2697     +895     
+ Partials      476      468       -8     
Flag Coverage Δ
e2e 42.87% <0.00%> (-3.05%) ⬇️
experimental-e2e 11.89% <0.00%> (-41.50%) ⬇️
unit 54.34% <11.05%> (-3.53%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tmshort
Copy link
Contributor

tmshort commented Feb 18, 2026

I'm noting that there's maybe some redundant path components?

api/v1/ac/api/v1 ?

@joelanford joelanford force-pushed the generate-applyconfigurations branch from a04fd03 to 998a7f8 Compare February 19, 2026 02:24
@joelanford
Copy link
Member Author

joelanford commented Feb 19, 2026

I'm noting that there's maybe some redundant path components?

api/v1/ac/api/v1 ?

Yeah good call. I moved <root>/api/v1/ac to <root>/applyconfigurations. Now the subtrees match and there's no repetition in the package path.

joelanford and others added 2 commits February 18, 2026 22:38
Use controller-gen's applyconfiguration generator to produce typed
apply configuration structs for Server-Side Apply. This adds generated
types for ClusterCatalog, ClusterExtension, ClusterExtensionRevision,
and all their sub-types in applyconfigurations/.

Changes:
- Add applyconfiguration generator to the Makefile generate target
- Add +kubebuilder:ac:generate=true and +kubebuilder:ac:output:package
  markers to the API package
- Add SchemeGroupVersion alias required by the generated code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate Boxcutter from deprecated client.Patch(ctx, rev, client.Apply)
to typed client.Apply(ctx, rev) using the generated
ClusterExtensionRevisionApplyConfiguration types.

- ClusterExtensionRevisionGenerator interface now returns AC types
- GenerateRevision/GenerateRevisionFromHelmRelease build AC objects
  directly via builder pattern instead of API struct literals
- Rename createOrUpdate to apply; uses client.Apply instead of
  deprecated client.Patch with client.Apply option
- Owner references set via metav1ac.OwnerReference() builder instead
  of controllerutil.SetControllerReference
- BoxcutterStorageMigrator.Migrate uses client.Apply; interface
  replaces Create with Apply and adds FieldOwner field
- PhaseSort and helpers operate on AC types natively
- All tests updated to use AC builder pattern and mock Apply

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 19, 2026 03:40
@joelanford joelanford force-pushed the generate-applyconfigurations branch from 998a7f8 to 6ff4d55 Compare February 19, 2026 03:40
Copy link
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

Copilot reviewed 39 out of 39 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

internal/operator-controller/applier/phase.go:137

  • Potential nil pointer dereference: The function accesses a.Object.GroupVersionKind() and b.Object.GetNamespace() without checking if a.Object or b.Object is nil. While the ApplyConfiguration type uses pointer fields, the Object field might be nil in certain scenarios, which would cause a panic.

Consider adding nil checks:

if a.Object == nil || b.Object == nil {
    // Handle nil objects appropriately
}

internal/operator-controller/applier/phase.go:149

  • Potential nil pointer dereference: Line 147 accesses obj.Object.GroupVersionKind() without checking if obj.Object is nil. This could cause a panic if the Object field is not initialized in the ApplyConfiguration.

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

{
User: user,
Name: rev.Name,
Name: *rev.GetName(),
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Potential nil pointer dereference: Line 662 dereferences rev.GetName() without checking if the returned pointer is nil. If the name hasn't been set in the ApplyConfiguration, this will cause a panic.

Consider checking for nil:

name := rev.GetName()
if name == nil {
    // Handle nil name appropriately
}

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +160
// Convert to pointers for WithObjects
objPtrs := make([]*ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration, len(objs))
for i := range objs {
objPtrs[i] = &objs[i]
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Aliasing issue with pointer conversion: The code creates pointers to slice elements (&objs[i]) which point to the slice's backing array. After the loop completes, these pointers become invalid if the slice is reallocated. While WithObjects dereferences these immediately (line 64 in the generated code), this pattern is fragile and could lead to bugs if the generated code changes or if the slice is modified between pointer creation and dereferencing.

A safer approach would be to pass values directly if WithObjects accepts pointers but dereferences them, or to store the objects differently. However, since WithObjects immediately dereferences the pointers, this is likely safe in practice but represents a code smell.

Copilot uses AI. Check for mistakes.
generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyInto, DeepCopyObject, and ApplyConfiguration type implementations.
# Need to delete the files for them to be generated properly
@find api cmd hack internal -name "zz_generated.deepcopy.go" -not -path "*/vendor/*" -delete && rm -rf applyconfigurations
$(CONTROLLER_GEN) --load-build-tags=$(GO_BUILD_TAGS) applyconfiguration:headerFile="hack/boilerplate.go.txt" paths="./api/..."
Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @joelanford should all APIs that we have use applyconfigurations now?
OR just the specific one?

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 19, 2026
@openshift-merge-robot
Copy link

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

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

Labels

needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

Comments