diff --git a/commitchecker.yaml b/commitchecker.yaml index cc6807e73..d670eee15 100644 --- a/commitchecker.yaml +++ b/commitchecker.yaml @@ -1,4 +1,4 @@ -expectedMergeBase: 6f23a793aff89728a110de7f9eec9d8948c8fbff +expectedMergeBase: 6692d1bb59b2f5b00a6fe2f21e8ffb9cfd4fa018 upstreamBranch: main upstreamOrg: operator-framework upstreamRepo: operator-controller diff --git a/go.mod b/go.mod index da8af5284..c27a39311 100644 --- a/go.mod +++ b/go.mod @@ -14,13 +14,13 @@ require ( github.com/go-logr/logr v1.4.3 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/go-cmp v0.7.0 - github.com/google/go-containerregistry v0.21.0 + github.com/google/go-containerregistry v0.21.1 github.com/google/renameio/v2 v2.0.2 github.com/gorilla/handlers v1.5.2 github.com/klauspost/compress v1.18.4 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 - github.com/operator-framework/api v0.39.0 + github.com/operator-framework/api v0.41.0 github.com/operator-framework/helm-operator-plugins v0.8.0 github.com/operator-framework/operator-registry v1.63.0 github.com/prometheus/client_golang v1.23.2 @@ -32,20 +32,20 @@ require ( go.podman.io/image/v5 v5.39.1 golang.org/x/exp v0.0.0-20260209203927-2842357ff358 golang.org/x/mod v0.33.0 - golang.org/x/sync v0.19.0 + golang.org/x/sync v0.20.0 golang.org/x/tools v0.42.0 helm.sh/helm/v3 v3.20.0 - k8s.io/api v0.35.0 - k8s.io/apiextensions-apiserver v0.35.0 - k8s.io/apimachinery v0.35.0 - k8s.io/apiserver v0.35.0 + k8s.io/api v0.35.1 + k8s.io/apiextensions-apiserver v0.35.1 + k8s.io/apimachinery v0.35.1 + k8s.io/apiserver v0.35.1 k8s.io/cli-runtime v0.35.0 - k8s.io/client-go v0.35.0 - k8s.io/component-base v0.35.0 + k8s.io/client-go v0.35.1 + k8s.io/component-base v0.35.1 k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v1.35.0 k8s.io/utils v0.0.0-20260108192941-914a6e750570 - pkg.package-operator.run/boxcutter v0.10.0 + pkg.package-operator.run/boxcutter v0.11.0 sigs.k8s.io/controller-runtime v0.23.1 sigs.k8s.io/controller-tools v0.20.1 sigs.k8s.io/crdify v0.5.0 diff --git a/go.sum b/go.sum index 805a08cfd..ed931730f 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.21.0 h1:ocqxUOczFwAZQBMNE7kuzfqvDe0VWoZxQMOesXreCDI= -github.com/google/go-containerregistry v0.21.0/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= +github.com/google/go-containerregistry v0.21.1 h1:sOt/o9BS2b87FnR7wxXPvRKU1XVJn2QCwOS5g8zQXlc= +github.com/google/go-containerregistry v0.21.1/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -407,8 +407,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/operator-framework/api v0.39.0 h1:9h7aVufeQ+l2ACXJE51hkMFcqrQwJOLM6/vwgGu6tgI= -github.com/operator-framework/api v0.39.0/go.mod h1:tcYIwuznZzfo4HKUTu0dbquIHqxiewnKW/ZmhHKzMH8= +github.com/operator-framework/api v0.41.0 h1:B0nutndl95elbLXJGRlkFNTI8OuZIjSqvTEeORPhTKo= +github.com/operator-framework/api v0.41.0/go.mod h1:Ouud+eqruzll9X3iv8wuAOTNAyyEncYXp4IVgbIlIdg= github.com/operator-framework/helm-operator-plugins v0.8.0 h1:0f6HOQC5likkf0b/OvGvw7nhDb6h8Cj5twdCNjwNzMc= github.com/operator-framework/helm-operator-plugins v0.8.0/go.mod h1:Sc+8bE38xTCgCChBUvtq/PxatEg9fAypr7S5iAw8nlA= github.com/operator-framework/operator-lib v0.19.0 h1:az6ogYj21rtU0SF9uYctRLyKp2dtlqTsmpfehFy6Ce8= @@ -644,8 +644,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -796,8 +796,8 @@ k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKW k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= -pkg.package-operator.run/boxcutter v0.10.0 h1:ToohW6jmcSHKqJJgN6cbIQ5UFG/BkYIp0mXxzfH3ZFU= -pkg.package-operator.run/boxcutter v0.10.0/go.mod h1:uBJIZiMgUt1SaZJyRp06L6s2kqL+JLNVOts470+k8Mw= +pkg.package-operator.run/boxcutter v0.11.0 h1:VhIQ7baW7ad2mFDYQ2ybk/NqZo6BX0LfO/vI0usHj9U= +pkg.package-operator.run/boxcutter v0.11.0/go.mod h1:t/51lJTnGkpGKfNDSkycAjAeZTP214VyNIcyJOB5Nhw= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE= diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller.go b/internal/operator-controller/controllers/clusterextensionrevision_controller.go index aa757b7f9..4b18c40c0 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller.go @@ -24,6 +24,7 @@ import ( "pkg.package-operator.run/boxcutter" "pkg.package-operator.run/boxcutter/machinery" machinerytypes "pkg.package-operator.run/boxcutter/machinery/types" + "pkg.package-operator.run/boxcutter/ownerhandling" "pkg.package-operator.run/boxcutter/probing" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -141,7 +142,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer return c.delete(ctx, cer) } - revision, opts, err := c.toBoxcutterRevision(ctx, cer) + phases, opts, err := c.buildBoxcutterPhases(ctx, cer) if err != nil { setRetryingConditions(cer, err.Error()) return ctrl.Result{}, fmt.Errorf("converting to boxcutter revision: %v", err) @@ -153,6 +154,14 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer return ctrl.Result{}, fmt.Errorf("failed to create revision engine: %v", err) } + revision := boxcutter.NewRevisionWithOwner( + cer.Name, + cer.Spec.Revision, + phases, + cer, + ownerhandling.NewNative(c.Client.Scheme()), + ) + if cer.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived { if err := c.TrackingCache.Free(ctx, cer); err != nil { markAsAvailableUnknown(cer, ocv1.ClusterExtensionRevisionReasonReconciling, err.Error()) @@ -171,7 +180,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer return ctrl.Result{}, werr } - rres, err := revisionEngine.Reconcile(ctx, *revision, opts...) + rres, err := revisionEngine.Reconcile(ctx, revision, opts...) if err != nil { if rres != nil { // Log detailed reconcile reports only in debug mode (V(1)) to reduce verbosity. @@ -296,8 +305,8 @@ func (c *ClusterExtensionRevisionReconciler) delete(ctx context.Context, cer *oc return ctrl.Result{}, nil } -func (c *ClusterExtensionRevisionReconciler) archive(ctx context.Context, revisionEngine RevisionEngine, cer *ocv1.ClusterExtensionRevision, revision *boxcutter.Revision) (ctrl.Result, error) { - tdres, err := revisionEngine.Teardown(ctx, *revision) +func (c *ClusterExtensionRevisionReconciler) archive(ctx context.Context, revisionEngine RevisionEngine, cer *ocv1.ClusterExtensionRevision, revision boxcutter.RevisionBuilder) (ctrl.Result, error) { + tdres, err := revisionEngine.Teardown(ctx, revision) if err != nil { err = fmt.Errorf("error archiving revision: %v", err) setRetryingConditions(cer, err.Error()) @@ -356,11 +365,11 @@ func (c *ClusterExtensionRevisionReconciler) SetupWithManager(mgr ctrl.Manager) Complete(c) } -func (c *ClusterExtensionRevisionReconciler) establishWatch(ctx context.Context, cer *ocv1.ClusterExtensionRevision, revision *boxcutter.Revision) error { +func (c *ClusterExtensionRevisionReconciler) establishWatch(ctx context.Context, cer *ocv1.ClusterExtensionRevision, revision boxcutter.RevisionBuilder) error { gvks := sets.New[schema.GroupVersionKind]() - for _, phase := range revision.Phases { - for _, obj := range phase.Objects { - gvks.Insert(obj.GroupVersionKind()) + for _, phase := range revision.GetPhases() { + for _, obj := range phase.GetObjects() { + gvks.Insert(obj.GetObjectKind().GroupVersionKind()) } } @@ -451,7 +460,7 @@ func (c *ClusterExtensionRevisionReconciler) listPreviousRevisions(ctx context.C return previous, nil } -func (c *ClusterExtensionRevisionReconciler) toBoxcutterRevision(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (*boxcutter.Revision, []boxcutter.RevisionReconcileOption, error) { +func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Context, cer *ocv1.ClusterExtensionRevision) ([]boxcutter.Phase, []boxcutter.RevisionReconcileOption, error) { previous, err := c.listPreviousRevisions(ctx, cer) if err != nil { return nil, nil, fmt.Errorf("listing previous revisions: %w", err) @@ -473,13 +482,9 @@ func (c *ClusterExtensionRevisionReconciler) toBoxcutterRevision(ctx context.Con }), } - r := &boxcutter.Revision{ - Name: cer.Name, - Owner: cer, - Revision: cer.Spec.Revision, - } + phases := make([]boxcutter.Phase, 0) for _, specPhase := range cer.Spec.Phases { - phase := boxcutter.Phase{Name: specPhase.Name} + objs := make([]client.Object, 0) for _, specObj := range specPhase.Objects { obj := specObj.Object.DeepCopy() @@ -496,11 +501,11 @@ func (c *ClusterExtensionRevisionReconciler) toBoxcutterRevision(ctx context.Con obj, boxcutter.WithCollisionProtection(cp))) } - phase.Objects = append(phase.Objects, *obj) + objs = append(objs, obj) } - r.Phases = append(r.Phases, phase) + phases = append(phases, boxcutter.NewPhase(specPhase.Name, objs)) } - return r, opts, nil + return phases, opts, nil } // EffectiveCollisionProtection resolves the collision protection value using diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go b/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go index 36224232a..682c10174 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go @@ -1405,6 +1405,7 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T rev := &ocv1.ClusterExtensionRevision{ ObjectMeta: metav1.ObjectMeta{ Name: "test-rev-1", + UID: types.UID("test-rev-1"), Labels: map[string]string{}, Annotations: map[string]string{ labels.ServiceAccountNameKey: "test-sa", diff --git a/internal/operator-controller/controllers/revision_engine_factory.go b/internal/operator-controller/controllers/revision_engine_factory.go index aa8a9b9ff..f87d930b3 100644 --- a/internal/operator-controller/controllers/revision_engine_factory.go +++ b/internal/operator-controller/controllers/revision_engine_factory.go @@ -21,7 +21,6 @@ import ( "pkg.package-operator.run/boxcutter/machinery" machinerytypes "pkg.package-operator.run/boxcutter/machinery/types" "pkg.package-operator.run/boxcutter/managedcache" - "pkg.package-operator.run/boxcutter/ownerhandling" "pkg.package-operator.run/boxcutter/validation" "sigs.k8s.io/controller-runtime/pkg/client" @@ -69,8 +68,7 @@ func (f *defaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, r machinery.NewPhaseEngine( machinery.NewObjectEngine( f.Scheme, f.TrackingCache, scopedClient, - ownerhandling.NewNative(f.Scheme), - machinery.NewComparator(ownerhandling.NewNative(f.Scheme), f.DiscoveryClient, f.Scheme, f.FieldOwnerPrefix), + machinery.NewComparator(f.DiscoveryClient, f.Scheme, f.FieldOwnerPrefix), f.FieldOwnerPrefix, f.FieldOwnerPrefix, ), validation.NewClusterPhaseValidator(f.RESTMapper, scopedClient), diff --git a/openshift/tests-extension/go.mod b/openshift/tests-extension/go.mod index 1ea5a36a5..bfd2dc727 100644 --- a/openshift/tests-extension/go.mod +++ b/openshift/tests-extension/go.mod @@ -17,11 +17,11 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/pretty v1.2.1 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.35.0 - k8s.io/apiextensions-apiserver v0.35.0 - k8s.io/apimachinery v0.35.0 - k8s.io/apiserver v0.35.0 - k8s.io/client-go v0.35.0 + k8s.io/api v0.35.1 + k8s.io/apiextensions-apiserver v0.35.1 + k8s.io/apimachinery v0.35.1 + k8s.io/apiserver v0.35.1 + k8s.io/client-go v0.35.1 k8s.io/kubernetes v1.35.0 k8s.io/utils v0.0.0-20260108192941-914a6e750570 sigs.k8s.io/controller-runtime v0.23.1 @@ -97,7 +97,7 @@ require ( golang.org/x/exp v0.0.0-20260209203927-2842357ff358 // indirect golang.org/x/net v0.50.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sync v0.19.0 // indirect + golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect @@ -110,7 +110,7 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/component-base v0.35.0 // indirect + k8s.io/component-base v0.35.1 // indirect k8s.io/component-helpers v0.35.0 // indirect k8s.io/controller-manager v0.33.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/openshift/tests-extension/go.sum b/openshift/tests-extension/go.sum index b50ae7cfe..77fc71aef 100644 --- a/openshift/tests-extension/go.sum +++ b/openshift/tests-extension/go.sum @@ -246,8 +246,8 @@ golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwE golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/openshift/tests-extension/vendor/golang.org/x/sync/singleflight/singleflight.go b/openshift/tests-extension/vendor/golang.org/x/sync/singleflight/singleflight.go index 405183098..90ca138af 100644 --- a/openshift/tests-extension/vendor/golang.org/x/sync/singleflight/singleflight.go +++ b/openshift/tests-extension/vendor/golang.org/x/sync/singleflight/singleflight.go @@ -22,7 +22,7 @@ var errGoexit = errors.New("runtime.Goexit was called") // A panicError is an arbitrary value recovered from a panic // with the stack trace during the execution of given function. type panicError struct { - value interface{} + value any stack []byte } @@ -40,7 +40,7 @@ func (p *panicError) Unwrap() error { return err } -func newPanicError(v interface{}) error { +func newPanicError(v any) error { stack := debug.Stack() // The first line of the stack trace is of the form "goroutine N [status]:" @@ -58,7 +58,7 @@ type call struct { // These fields are written once before the WaitGroup is done // and are only read after the WaitGroup is done. - val interface{} + val any err error // These fields are read and written with the singleflight @@ -78,7 +78,7 @@ type Group struct { // Result holds the results of Do, so they can be passed // on a channel. type Result struct { - Val interface{} + Val any Err error Shared bool } @@ -88,7 +88,7 @@ type Result struct { // time. If a duplicate comes in, the duplicate caller waits for the // original to complete and receives the same results. // The return value shared indicates whether v was given to multiple callers. -func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { +func (g *Group) Do(key string, fn func() (any, error)) (v any, err error, shared bool) { g.mu.Lock() if g.m == nil { g.m = make(map[string]*call) @@ -118,7 +118,7 @@ func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, e // results when they are ready. // // The returned channel will not be closed. -func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result { +func (g *Group) DoChan(key string, fn func() (any, error)) <-chan Result { ch := make(chan Result, 1) g.mu.Lock() if g.m == nil { @@ -141,7 +141,7 @@ func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result } // doCall handles the single call for a key. -func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { +func (g *Group) doCall(c *call, key string, fn func() (any, error)) { normalReturn := false recovered := false diff --git a/openshift/tests-extension/vendor/modules.txt b/openshift/tests-extension/vendor/modules.txt index 7c331a3b8..6b4ce96f7 100644 --- a/openshift/tests-extension/vendor/modules.txt +++ b/openshift/tests-extension/vendor/modules.txt @@ -439,8 +439,8 @@ golang.org/x/net/websocket ## explicit; go 1.24.0 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.19.0 -## explicit; go 1.24.0 +# golang.org/x/sync v0.20.0 +## explicit; go 1.25.0 golang.org/x/sync/singleflight # golang.org/x/sys v0.41.0 ## explicit; go 1.24.0 @@ -619,7 +619,7 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# k8s.io/api v0.35.0 => github.com/openshift/kubernetes/staging/src/k8s.io/api v0.0.0-20251108023427-891f5bb03061 +# k8s.io/api v0.35.1 => github.com/openshift/kubernetes/staging/src/k8s.io/api v0.0.0-20251108023427-891f5bb03061 ## explicit; go 1.24.0 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -680,7 +680,7 @@ k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 k8s.io/api/storagemigration/v1alpha1 -# k8s.io/apiextensions-apiserver v0.35.0 => github.com/openshift/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251108023427-891f5bb03061 +# k8s.io/apiextensions-apiserver v0.35.1 => github.com/openshift/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251108023427-891f5bb03061 ## explicit; go 1.24.0 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 @@ -692,7 +692,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1 k8s.io/apiextensions-apiserver/pkg/features -# k8s.io/apimachinery v0.35.0 => github.com/openshift/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20251108023427-891f5bb03061 +# k8s.io/apimachinery v0.35.1 => github.com/openshift/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20251108023427-891f5bb03061 ## explicit; go 1.24.0 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -762,7 +762,7 @@ k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/netutil k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.35.0 => github.com/openshift/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251108023427-891f5bb03061 +# k8s.io/apiserver v0.35.1 => github.com/openshift/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251108023427-891f5bb03061 ## explicit; go 1.24.0 k8s.io/apiserver/pkg/admission k8s.io/apiserver/pkg/admission/configuration @@ -814,7 +814,7 @@ k8s.io/apiserver/pkg/util/feature k8s.io/apiserver/pkg/util/webhook k8s.io/apiserver/pkg/util/x509metrics k8s.io/apiserver/pkg/warning -# k8s.io/client-go v0.35.0 => github.com/openshift/kubernetes/staging/src/k8s.io/client-go v0.0.0-20251108023427-891f5bb03061 +# k8s.io/client-go v0.35.1 => github.com/openshift/kubernetes/staging/src/k8s.io/client-go v0.0.0-20251108023427-891f5bb03061 ## explicit; go 1.24.0 k8s.io/client-go/applyconfigurations/admissionregistration/v1 k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1 @@ -1104,7 +1104,7 @@ k8s.io/client-go/util/homedir k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/workqueue -# k8s.io/component-base v0.35.0 => github.com/openshift/kubernetes/staging/src/k8s.io/component-base v0.0.0-20251108023427-891f5bb03061 +# k8s.io/component-base v0.35.1 => github.com/openshift/kubernetes/staging/src/k8s.io/component-base v0.0.0-20251108023427-891f5bb03061 ## explicit; go 1.24.0 k8s.io/component-base/cli/flag k8s.io/component-base/compatibility diff --git a/requirements.txt b/requirements.txt index e39c112f0..71b41fcce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ Babel==2.18.0 beautifulsoup4==4.14.3 -certifi==2026.1.4 +certifi==2026.2.25 charset-normalizer==3.4.4 click==8.3.1 colorama==0.4.6 @@ -14,7 +14,7 @@ markdown2==2.5.4 MarkupSafe==3.0.3 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.7.2 +mkdocs-material==9.7.3 mkdocs-material-extensions==1.3.1 packaging==26.0 paginate==0.5.7 diff --git a/vendor/github.com/operator-framework/api/pkg/lib/version/doc.go b/vendor/github.com/operator-framework/api/pkg/lib/version/doc.go new file mode 100644 index 000000000..7318ee6d1 --- /dev/null +++ b/vendor/github.com/operator-framework/api/pkg/lib/version/doc.go @@ -0,0 +1,5 @@ +// +k8s:openapi-gen=true +// +k8s:openapi-model-package=com.github.operator-framework.api.pkg.operators.lib.version + +// Package version contains the OperatorVersion type. +package version diff --git a/vendor/github.com/operator-framework/api/pkg/lib/version/zz_generated.model_name.go b/vendor/github.com/operator-framework/api/pkg/lib/version/zz_generated.model_name.go new file mode 100644 index 000000000..1fd31307b --- /dev/null +++ b/vendor/github.com/operator-framework/api/pkg/lib/version/zz_generated.model_name.go @@ -0,0 +1,26 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by openapi-gen-v0.0. DO NOT EDIT. + +package version + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in OperatorVersion) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.lib.version.OperatorVersion" +} diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go index 74bc9b819..fd9d1d849 100644 --- a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go @@ -1,6 +1,8 @@ // +groupName=operators.coreos.com // +k8s:deepcopy-gen=package // +k8s:conversion-gen=github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators +// +k8s:openapi-gen=true +// +k8s:openapi-model-package=com.github.operator-framework.api.pkg.operators.v1alpha1 // Package v1alpha1 contains resources types for version v1alpha1 of the operators.coreos.com API group. package v1alpha1 diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go index 7aa854f59..292fedf9b 100644 --- a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go @@ -232,8 +232,6 @@ type SubscriptionStatus struct { // CatalogHealth contains the Subscription's view of its relevant CatalogSources' status. // It is used to determine SubscriptionStatusConditions related to CatalogSources. // +optional - // +patchMergeKey= - // +patchStrategy=merge CatalogHealth []SubscriptionCatalogHealth `json:"catalogHealth,omitempty"` // Conditions is a list of the latest available observations about a Subscription's current state. diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.model_name.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.model_name.go new file mode 100644 index 000000000..555d6d7f0 --- /dev/null +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.model_name.go @@ -0,0 +1,306 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by openapi-gen-v0.0. DO NOT EDIT. + +package v1alpha1 + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in APIResourceReference) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.APIResourceReference" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in APIServiceDefinitions) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.APIServiceDefinitions" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in APIServiceDescription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.APIServiceDescription" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ActionDescriptor) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ActionDescriptor" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in AppLink) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.AppLink" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in BundleLookup) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.BundleLookup" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in BundleLookupCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.BundleLookupCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CRDDescription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CRDDescription" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSource) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSource" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSourceList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSourceList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSourceSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSourceSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSourceStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSourceStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CleanupSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CleanupSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CleanupStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CleanupStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersion) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersion" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ConfigMapResourceReference) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ConfigMapResourceReference" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CustomResourceDefinitions) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CustomResourceDefinitions" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in DependentStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.DependentStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ExtractContentConfig) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ExtractContentConfig" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in GRPCConnectionState) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.GRPCConnectionState" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in GrpcPodConfig) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.GrpcPodConfig" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Icon) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Icon" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallMode) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallMode" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlan) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlan" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanReference) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanReference" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Maintainer) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Maintainer" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in NamedInstallStrategy) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.NamedInstallStrategy" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RegistryPoll) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RegistryPoll" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RegistryServiceStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RegistryServiceStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RelatedImage) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RelatedImage" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RequirementStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RequirementStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ResourceInstance) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ResourceInstance" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ResourceList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ResourceList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SpecDescriptor) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SpecDescriptor" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StatusDescriptor) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StatusDescriptor" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Step) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Step" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StepResource) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StepResource" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StrategyDeploymentPermissions) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StrategyDeploymentPermissions" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StrategyDeploymentSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StrategyDeploymentSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StrategyDetailsDeployment) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StrategyDetailsDeployment" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Subscription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Subscription" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionCatalogHealth) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionCatalogHealth" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionConfig) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionConfig" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in UpdateStrategy) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.UpdateStrategy" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in WebhookDescription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.WebhookDescription" +} diff --git a/vendor/github.com/operator-framework/api/pkg/validation/internal/bundle.go b/vendor/github.com/operator-framework/api/pkg/validation/internal/bundle.go index c5f7ba18d..8e4e22019 100644 --- a/vendor/github.com/operator-framework/api/pkg/validation/internal/bundle.go +++ b/vendor/github.com/operator-framework/api/pkg/validation/internal/bundle.go @@ -8,6 +8,7 @@ import ( operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/api/pkg/validation/errors" interfaces "github.com/operator-framework/api/pkg/validation/interfaces" + "go.podman.io/image/v5/docker/reference" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -46,6 +47,10 @@ func validateBundle(bundle *manifests.Bundle) (result errors.ManifestResult) { if nameErrors != nil { result.Add(nameErrors...) } + relatedImagesErrors := validateRelatedImages(bundle) + if relatedImagesErrors != nil { + result.Add(relatedImagesErrors...) + } return result } @@ -62,6 +67,30 @@ func validateBundleName(bundle *manifests.Bundle) []errors.Error { return errs } +// validateRelatedImages checks that all relatedImages[].image pullspecs are valid +// using github.com/distribution/reference.ParseNormalizedNamed +func validateRelatedImages(bundle *manifests.Bundle) []errors.Error { + var errs []errors.Error + + for i, relatedImage := range bundle.CSV.Spec.RelatedImages { + if relatedImage.Image == "" { + errs = append(errs, errors.ErrInvalidBundle( + fmt.Sprintf("relatedImages[%d] has an empty image field", i), + fmt.Sprintf("spec.relatedImages[%d].image", i))) + continue + } + + // Parse and validate the image reference + if _, err := reference.ParseNormalizedNamed(relatedImage.Image); err != nil { + errs = append(errs, errors.ErrInvalidBundle( + fmt.Sprintf("relatedImages[%d] has an invalid image pullspec %q: %v", i, relatedImage.Image, err), + fmt.Sprintf("spec.relatedImages[%d].image", i))) + } + } + + return errs +} + func validateServiceAccounts(bundle *manifests.Bundle) []errors.Error { // get service account names defined in the csv saNamesFromCSV := make(map[string]struct{}, 0) diff --git a/vendor/golang.org/x/sync/singleflight/singleflight.go b/vendor/golang.org/x/sync/singleflight/singleflight.go index 405183098..90ca138af 100644 --- a/vendor/golang.org/x/sync/singleflight/singleflight.go +++ b/vendor/golang.org/x/sync/singleflight/singleflight.go @@ -22,7 +22,7 @@ var errGoexit = errors.New("runtime.Goexit was called") // A panicError is an arbitrary value recovered from a panic // with the stack trace during the execution of given function. type panicError struct { - value interface{} + value any stack []byte } @@ -40,7 +40,7 @@ func (p *panicError) Unwrap() error { return err } -func newPanicError(v interface{}) error { +func newPanicError(v any) error { stack := debug.Stack() // The first line of the stack trace is of the form "goroutine N [status]:" @@ -58,7 +58,7 @@ type call struct { // These fields are written once before the WaitGroup is done // and are only read after the WaitGroup is done. - val interface{} + val any err error // These fields are read and written with the singleflight @@ -78,7 +78,7 @@ type Group struct { // Result holds the results of Do, so they can be passed // on a channel. type Result struct { - Val interface{} + Val any Err error Shared bool } @@ -88,7 +88,7 @@ type Result struct { // time. If a duplicate comes in, the duplicate caller waits for the // original to complete and receives the same results. // The return value shared indicates whether v was given to multiple callers. -func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { +func (g *Group) Do(key string, fn func() (any, error)) (v any, err error, shared bool) { g.mu.Lock() if g.m == nil { g.m = make(map[string]*call) @@ -118,7 +118,7 @@ func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, e // results when they are ready. // // The returned channel will not be closed. -func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result { +func (g *Group) DoChan(key string, fn func() (any, error)) <-chan Result { ch := make(chan Result, 1) g.mu.Lock() if g.m == nil { @@ -141,7 +141,7 @@ func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result } // doCall handles the single call for a key. -func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { +func (g *Group) doCall(c *call, key string, fn func() (any, error)) { normalReturn := false recovered := false diff --git a/vendor/modules.txt b/vendor/modules.txt index 97d3252da..4100800f7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -439,7 +439,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/go-containerregistry v0.21.0 +# github.com/google/go-containerregistry v0.21.1 ## explicit; go 1.25.6 github.com/google/go-containerregistry/internal/and github.com/google/go-containerregistry/internal/compression @@ -653,8 +653,8 @@ github.com/opencontainers/image-spec/specs-go/v1 # github.com/opencontainers/runtime-spec v1.3.0 ## explicit github.com/opencontainers/runtime-spec/specs-go -# github.com/operator-framework/api v0.39.0 -## explicit; go 1.25.3 +# github.com/operator-framework/api v0.41.0 +## explicit; go 1.25.7 github.com/operator-framework/api/pkg/constraints github.com/operator-framework/api/pkg/encoding github.com/operator-framework/api/pkg/lib/release @@ -1030,8 +1030,8 @@ golang.org/x/net/websocket ## explicit; go 1.24.0 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.19.0 -## explicit; go 1.24.0 +# golang.org/x/sync v0.20.0 +## explicit; go 1.25.0 golang.org/x/sync/errgroup golang.org/x/sync/semaphore golang.org/x/sync/singleflight @@ -1287,7 +1287,7 @@ helm.sh/helm/v3/pkg/storage/driver helm.sh/helm/v3/pkg/time helm.sh/helm/v3/pkg/time/ctime helm.sh/helm/v3/pkg/uploader -# k8s.io/api v0.35.0 => k8s.io/api v0.35.0 +# k8s.io/api v0.35.1 => k8s.io/api v0.35.0 ## explicit; go 1.25.0 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -1349,7 +1349,7 @@ k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 k8s.io/api/storagemigration/v1beta1 -# k8s.io/apiextensions-apiserver v0.35.0 => k8s.io/apiextensions-apiserver v0.35.0 +# k8s.io/apiextensions-apiserver v0.35.1 => k8s.io/apiextensions-apiserver v0.35.0 ## explicit; go 1.25.0 k8s.io/apiextensions-apiserver/pkg/apihelpers k8s.io/apiextensions-apiserver/pkg/apis/apiextensions @@ -1371,7 +1371,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1 k8s.io/apiextensions-apiserver/pkg/features -# k8s.io/apimachinery v0.35.0 => k8s.io/apimachinery v0.35.0 +# k8s.io/apimachinery v0.35.1 => k8s.io/apimachinery v0.35.0 ## explicit; go 1.25.0 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -1443,7 +1443,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.35.0 => k8s.io/apiserver v0.35.0 +# k8s.io/apiserver v0.35.1 => k8s.io/apiserver v0.35.0 ## explicit; go 1.25.0 k8s.io/apiserver/pkg/apis/apiserver k8s.io/apiserver/pkg/apis/apiserver/install @@ -1498,7 +1498,7 @@ k8s.io/cli-runtime/pkg/genericclioptions k8s.io/cli-runtime/pkg/genericiooptions k8s.io/cli-runtime/pkg/printers k8s.io/cli-runtime/pkg/resource -# k8s.io/client-go v0.35.0 => k8s.io/client-go v0.35.0 +# k8s.io/client-go v0.35.1 => k8s.io/client-go v0.35.0 ## explicit; go 1.25.0 k8s.io/client-go/applyconfigurations k8s.io/client-go/applyconfigurations/admissionregistration/v1 @@ -1859,7 +1859,7 @@ k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/watchlist k8s.io/client-go/util/workqueue -# k8s.io/component-base v0.35.0 => k8s.io/component-base v0.35.0 +# k8s.io/component-base v0.35.1 => k8s.io/component-base v0.35.0 ## explicit; go 1.25.0 k8s.io/component-base/cli/flag k8s.io/component-base/compatibility @@ -1971,7 +1971,7 @@ oras.land/oras-go/v2/registry/remote/credentials/trace oras.land/oras-go/v2/registry/remote/errcode oras.land/oras-go/v2/registry/remote/internal/errutil oras.land/oras-go/v2/registry/remote/retry -# pkg.package-operator.run/boxcutter v0.10.0 +# pkg.package-operator.run/boxcutter v0.11.0 ## explicit; go 1.25.3 pkg.package-operator.run/boxcutter pkg.package-operator.run/boxcutter/machinery diff --git a/vendor/pkg.package-operator.run/boxcutter/.golangci.yaml b/vendor/pkg.package-operator.run/boxcutter/.golangci.yaml index 194a2951b..811fde861 100644 --- a/vendor/pkg.package-operator.run/boxcutter/.golangci.yaml +++ b/vendor/pkg.package-operator.run/boxcutter/.golangci.yaml @@ -91,7 +91,6 @@ linters: - protogetter - reassign - recvcheck - - revive - rowserrcheck - sloglint - spancheck @@ -110,9 +109,13 @@ linters: - usetesting - wastedassign - whitespace - - wsl + - wsl_v5 - zerologlint settings: + wsl_v5: + allow-first-in-block: true + allow-whole-block: false + branch-max-lines: 2 gosec: excludes: - G301 diff --git a/vendor/pkg.package-operator.run/boxcutter/boxcutter.go b/vendor/pkg.package-operator.run/boxcutter/boxcutter.go index a2133178a..2810b30ae 100644 --- a/vendor/pkg.package-operator.run/boxcutter/boxcutter.go +++ b/vendor/pkg.package-operator.run/boxcutter/boxcutter.go @@ -3,23 +3,40 @@ package boxcutter import ( "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/discovery" "sigs.k8s.io/controller-runtime/pkg/client" "pkg.package-operator.run/boxcutter/machinery" "pkg.package-operator.run/boxcutter/machinery/types" - "pkg.package-operator.run/boxcutter/ownerhandling" "pkg.package-operator.run/boxcutter/validation" ) // Revision represents multiple phases at a given point in time. type Revision = types.Revision +// RevisionBuilder is a Revision with methods to attach options. +type RevisionBuilder = types.RevisionBuilder + +// NewRevision creates a new RevisionBuilder with the given name, rev and phases. +var NewRevision = types.NewRevision + +// NewRevisionWithOwner creates a new RevisionBuilder +// with the given name, rev, phases and owner. +var NewRevisionWithOwner = types.NewRevisionWithOwner + // Phase represents a collection of objects lifecycled together. type Phase = types.Phase +// PhaseBuilder is a Phase with methods to attach options. +type PhaseBuilder = types.PhaseBuilder + +// NewPhase creates a new PhaseBuilder with the given name and objects. +var NewPhase = types.NewPhase + +// NewPhaseWithOwner creates a new PhaseBuilder with the given name, objects and owner. +var NewPhaseWithOwner = types.NewPhaseWithOwner + // ObjectReconcileOption is the common interface for object reconciliation options. type ObjectReconcileOption = types.ObjectReconcileOption @@ -86,6 +103,11 @@ var WithPhaseReconcileOptions = types.WithPhaseReconcileOptions // WithPhaseTeardownOptions applies the given options only to the given Phase. var WithPhaseTeardownOptions = types.WithPhaseTeardownOptions +// WithOwner sets an owning object and the strategy to use with it. +// Ensures controller-refs are set to track the owner and +// enables handover between owners. +var WithOwner = types.WithOwner + // ProgressProbeType is a well-known probe type used to guard phase progression. const ProgressProbeType = types.ProgressProbeType @@ -93,14 +115,7 @@ const ProgressProbeType = types.ProgressProbeType type RevisionEngine = machinery.RevisionEngine // OwnerStrategy interface needed for RevisionEngine. -type OwnerStrategy interface { - SetControllerReference(owner, obj metav1.Object) error - GetController(obj metav1.Object) (metav1.OwnerReference, bool) - IsController(owner, obj metav1.Object) bool - CopyOwnerReferences(objA, objB metav1.Object) - ReleaseController(obj metav1.Object) - RemoveOwner(owner, obj metav1.Object) -} +type OwnerStrategy = types.OwnerStrategy // RevisionEngineOptions holds all configuration options for the RevisionEngine. type RevisionEngineOptions struct { @@ -114,7 +129,6 @@ type RevisionEngineOptions struct { // Optional - OwnerStrategy OwnerStrategy PhaseValidator *validation.PhaseValidator } @@ -124,20 +138,16 @@ func NewPhaseEngine(opts RevisionEngineOptions) (*machinery.PhaseEngine, error) return nil, err } - if opts.OwnerStrategy == nil { - opts.OwnerStrategy = ownerhandling.NewNative(opts.Scheme) - } - if opts.PhaseValidator == nil { opts.PhaseValidator = validation.NewNamespacedPhaseValidator(opts.RestMapper, opts.Writer) } comp := machinery.NewComparator( - opts.OwnerStrategy, opts.DiscoveryClient, opts.Scheme, opts.FieldOwner) + opts.DiscoveryClient, opts.Scheme, opts.FieldOwner) oe := machinery.NewObjectEngine( opts.Scheme, opts.Reader, opts.Writer, - opts.OwnerStrategy, comp, opts.FieldOwner, opts.SystemPrefix, + comp, opts.FieldOwner, opts.SystemPrefix, ) return machinery.NewPhaseEngine(oe, opts.PhaseValidator), nil @@ -149,19 +159,15 @@ func NewRevisionEngine(opts RevisionEngineOptions) (*RevisionEngine, error) { return nil, err } - if opts.OwnerStrategy == nil { - opts.OwnerStrategy = ownerhandling.NewNative(opts.Scheme) - } - pval := validation.NewNamespacedPhaseValidator(opts.RestMapper, opts.Writer) rval := validation.NewRevisionValidator() comp := machinery.NewComparator( - opts.OwnerStrategy, opts.DiscoveryClient, opts.Scheme, opts.FieldOwner) + opts.DiscoveryClient, opts.Scheme, opts.FieldOwner) oe := machinery.NewObjectEngine( opts.Scheme, opts.Reader, opts.Writer, - opts.OwnerStrategy, comp, opts.FieldOwner, opts.SystemPrefix, + comp, opts.FieldOwner, opts.SystemPrefix, ) pe := machinery.NewPhaseEngine(oe, pval) diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/comparator.go b/vendor/pkg.package-operator.run/boxcutter/machinery/comparator.go index 130699321..7027f43b7 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/comparator.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/comparator.go @@ -21,6 +21,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/structured-merge-diff/v6/fieldpath" "sigs.k8s.io/structured-merge-diff/v6/typed" + + "pkg.package-operator.run/boxcutter/machinery/types" ) // Comparator detects divergent state between desired and actual @@ -28,7 +30,6 @@ import ( // If not all fields from desired are owned by the same field owner in actual, // we know that the object has been updated by another actor. type Comparator struct { - ownerStrategy divergeDetectorOwnerStrategy openAPIAccessor openAPIAccessor scheme *runtime.Scheme fieldOwner string @@ -38,23 +39,17 @@ type discoveryClient interface { OpenAPIV3() openapi.Client } -type divergeDetectorOwnerStrategy interface { - SetControllerReference(owner, obj metav1.Object) error -} - type openAPIAccessor interface { Get(gv schema.GroupVersion) (*spec3.OpenAPI, error) } // NewComparator returns a new Comparator instance. func NewComparator( - ownerStrategy divergeDetectorOwnerStrategy, discoveryClient discoveryClient, scheme *runtime.Scheme, fieldOwner string, ) *Comparator { return &Comparator{ - ownerStrategy: ownerStrategy, openAPIAccessor: &defaultOpenAPIAccessor{ c: discoveryClient.OpenAPIV3(), }, @@ -178,9 +173,14 @@ func (d CompareResult) Modified() []string { // Compare checks if a resource has been changed from desired. func (d *Comparator) Compare( - owner client.Object, desiredObject, actualObject Object, + opts ...types.ComparatorOption, ) (res CompareResult, err error) { + var options types.ComparatorOptions + for _, opt := range opts { + opt.ApplyToComparatorOptions(&options) + } + if err := ensureGVKIsSet(desiredObject, d.scheme); err != nil { return res, err } @@ -227,8 +227,12 @@ func (d *Comparator) Compare( // Extrapolate a field set from desired. desiredObject = desiredObject.DeepCopyObject().(Object) - if err := d.ownerStrategy.SetControllerReference(owner, desiredObject); err != nil { - return res, err + if options.Owner != nil { + if err := options.OwnerStrategy.SetControllerReference( + options.Owner, desiredObject, + ); err != nil { + return res, err + } } tName, err := openAPICanonicalName(desiredObject) @@ -435,7 +439,7 @@ func ensureGVKIsSet(obj client.Object, scheme *runtime.Scheme) error { const statusSubresourceSuffix = "{name}/status" // Determines if the schema has a Status subresource defined. -// If so the comperator has to ignore .status, because the API server will also ignore these fields. +// If so the Comparator has to ignore .status, because the API server will also ignore these fields. func hasStatusSubresource(openAPISchema *spec3.OpenAPI) bool { if openAPISchema.Paths == nil { return false diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/objects.go b/vendor/pkg.package-operator.run/boxcutter/machinery/objects.go index 85727208f..53f7ce784 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/objects.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/objects.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "strconv" - "strings" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -23,11 +22,10 @@ import ( // ObjectEngine reconciles individual objects. type ObjectEngine struct { - scheme *runtime.Scheme - cache client.Reader - writer client.Writer - ownerStrategy objectEngineOwnerStrategy - comparator comparator + scheme *runtime.Scheme + cache client.Reader + writer client.Writer + comparator comparator fieldOwner string systemPrefix string @@ -38,18 +36,16 @@ func NewObjectEngine( scheme *runtime.Scheme, cache client.Reader, writer client.Writer, - ownerStrategy objectEngineOwnerStrategy, comparator comparator, fieldOwner string, systemPrefix string, ) *ObjectEngine { return &ObjectEngine{ - scheme: scheme, - cache: cache, - writer: writer, - ownerStrategy: ownerStrategy, - comparator: comparator, + scheme: scheme, + cache: cache, + writer: writer, + comparator: comparator, fieldOwner: fieldOwner, systemPrefix: systemPrefix, @@ -73,19 +69,20 @@ type objectEngineOwnerStrategy interface { type comparator interface { Compare( - owner client.Object, desiredObject, actualObject Object, + opts ...types.ComparatorOption, ) (res CompareResult, err error) } const ( + managedByLabel string = "app.kubernetes.io/managed-by" + managedByLabelValue string = "boxcutter" boxcutterManagedLabel string = "boxcutter-managed" ) // Teardown ensures the given object is safely removed from the cluster. func (e *ObjectEngine) Teardown( ctx context.Context, - owner client.Object, // Owner of the object. revision int64, // Revision number, must start at 1. desiredObject Object, opts ...types.ObjectTeardownOption, @@ -102,15 +99,12 @@ func (e *ObjectEngine) Teardown( panic("owner revision must be set and start at 1") } - if len(owner.GetUID()) == 0 { - panic("owner must be persisted to cluster, empty UID") - } - - // Shortcut when Owner is orphaning its dependents. - // If we don't check this, we might be too quick and start deleting - // dependents that should be kept on the cluster! - if controllerutil.ContainsFinalizer(owner, "orphan") || options.Orphan { - err := removeBoxcutterManagedLabel(ctx, e.writer, desiredObject.(*unstructured.Unstructured)) + // The "orphan" finalizer on the owner object indicates that the Owner + // is being deleted and orphaning its dependents. This finalizer is + // managed by KCM's gc controller. If we observe it, we are racing with + // the gc controller, and should not delete dependent objects. + if options.Orphan || (options.Owner != nil && controllerutil.ContainsFinalizer(options.Owner, "orphan")) { + err := e.removeBoxcutterManagedLabelsAndAnnotations(ctx, e.writer, desiredObject) if err != nil { return false, err } @@ -145,13 +139,21 @@ func (e *ObjectEngine) Teardown( return false, fmt.Errorf("getting object revision: %w", err) } - ctrlSit, _ := e.detectOwner(owner, actualObject, nil) - if actualRevision != revision || ctrlSit != ctrlSituationIsController { - // Remove us from owners list: - patch := actualObject.DeepCopyObject().(Object) - e.ownerStrategy.RemoveOwner(owner, patch) + // Object is not owned by this revision + if actualRevision != revision { + if options.Owner == nil { + // No Owner to remove from the object, return. + return true, nil + } + + ctrlSit, _ := e.detectOwner(options.Owner, options.OwnerStrategy, actualObject, nil) + if ctrlSit != ctrlSituationIsController { + // Remove us from owners list: + patch := actualObject.DeepCopyObject().(Object) + options.OwnerStrategy.RemoveOwner(options.Owner, patch) - return true, e.writer.Patch(ctx, patch, client.MergeFrom(actualObject)) + return true, e.writer.Patch(ctx, patch, client.MergeFrom(actualObject)) + } } // Actually delete the object. @@ -178,7 +180,6 @@ func (e *ObjectEngine) Teardown( // Reconcile runs actions to bring actual state closer to desired. func (e *ObjectEngine) Reconcile( ctx context.Context, - owner client.Object, // Owner of the object. revision int64, // Revision number, must start at 1. desiredObject Object, opts ...types.ObjectReconcileOption, @@ -193,7 +194,7 @@ func (e *ObjectEngine) Reconcile( labels = map[string]string{} } - labels[boxcutterManagedLabel] = "True" + labels[managedByLabel] = managedByLabelValue desiredObject.SetLabels(labels) options.Default() @@ -203,10 +204,6 @@ func (e *ObjectEngine) Reconcile( panic("owner revision must be set and start at 1") } - if len(owner.GetUID()) == 0 { - panic("owner must be persistet to cluster, empty UID") - } - if err := ensureGVKIsSet(desiredObject, e.scheme); err != nil { return nil, err } @@ -215,10 +212,12 @@ func (e *ObjectEngine) Reconcile( desiredObject = desiredObject.DeepCopyObject().(Object) e.setObjectRevision(desiredObject, revision) - if err := e.ownerStrategy.SetControllerReference( - owner, desiredObject, - ); err != nil { - return nil, fmt.Errorf("set controller reference: %w", err) + if options.Owner != nil { + if err := options.OwnerStrategy.SetControllerReference( + options.Owner, desiredObject, + ); err != nil { + return nil, fmt.Errorf("set controller reference: %w", err) + } } // Lookup actual object state on cluster. @@ -261,27 +260,66 @@ func (e *ObjectEngine) Reconcile( } return e.objectUpdateHandling( - ctx, owner, revision, - desiredObject, actualObject, options, + ctx, revision, desiredObject, + actualObject, options, ) } -func (e *ObjectEngine) objectUpdateHandling( - ctx context.Context, - owner client.Object, - revision int64, +func (e *ObjectEngine) checkSituation( desiredObject Object, actualObject Object, options types.ObjectReconcileOptions, -) (ObjectResult, error) { +) ( + ctrlSit ctrlSituation, + compareRes CompareResult, + actualOwner *metav1.OwnerReference, + err error, +) { + var compareOpts []types.ComparatorOption + + if options.Owner != nil { + ctrlSit, actualOwner = e.detectOwner( + options.Owner, options.OwnerStrategy, actualObject, options.PreviousOwners) + + compareOpts = append(compareOpts, types.WithOwner(options.Owner, options.OwnerStrategy)) + } else { + if e.isBoxcutterManaged(actualObject) { + ctrlSit = ctrlSituationIsController + } else { + ctrlSit = ctrlSituationNoController + } + } + // An object already exists on the cluster. // Before doing anything else, we have to figure out // who owns and controls the object. - ctrlSit, actualOwner := e.detectOwner(owner, actualObject, options.PreviousOwners) + compareRes, err = e.comparator.Compare(desiredObject, actualObject, compareOpts...) + if err != nil { + err = fmt.Errorf("diverge check: %w", err) + + return ctrlSit, + compareRes, + actualOwner, + err + } - compareRes, err := e.comparator.Compare(owner, desiredObject, actualObject) + return ctrlSit, + compareRes, + actualOwner, + err +} + +func (e *ObjectEngine) objectUpdateHandling( + ctx context.Context, + revision int64, + desiredObject Object, + actualObject Object, + options types.ObjectReconcileOptions, +) (ObjectResult, error) { + ctrlSit, compareRes, actualOwner, err := e.checkSituation( + desiredObject, actualObject, options) if err != nil { - return nil, fmt.Errorf("diverge check: %w", err) + return nil, err } // Ensure revision linearity. @@ -412,13 +450,16 @@ func (e *ObjectEngine) objectUpdateHandling( // ObjectResult ModifiedFields does not contain ownerReference changes // introduced here, this may lead to Updated Actions without modifications. e.setObjectRevision(desiredObject, revision) - e.ownerStrategy.CopyOwnerReferences(actualObject, desiredObject) - e.ownerStrategy.ReleaseController(desiredObject) - if err := e.ownerStrategy.SetControllerReference( - owner, desiredObject, - ); err != nil { - return nil, fmt.Errorf("set controller reference: %w", err) + if options.Owner != nil { + options.OwnerStrategy.CopyOwnerReferences(actualObject, desiredObject) + options.OwnerStrategy.ReleaseController(desiredObject) + + if err := options.OwnerStrategy.SetControllerReference( + options.Owner, desiredObject, + ); err != nil { + return nil, fmt.Errorf("set controller reference: %w", err) + } } // Write changes. @@ -443,11 +484,16 @@ func (e *ObjectEngine) objectUpdateHandling( ), nil } +// isBoxcutterManaged is used to detect if we have managed this object at some point. +// It's only purpose is to prevent boxcutter immediately re-adopting objects when +// resources get orphaned by the GC. func (e *ObjectEngine) isBoxcutterManaged(obj client.Object) bool { - for k := range obj.GetLabels() { - if strings.HasPrefix(k, boxcutterManagedLabel) { - return true - } + labels := obj.GetLabels() + annotations := obj.GetAnnotations() + + _, hasRevisionAnnotation := annotations[e.revisionAnnotation()] + if labels[managedByLabel] == managedByLabelValue && hasRevisionAnnotation { + return true } return false @@ -478,12 +524,12 @@ func (e *ObjectEngine) apply( return err } - o := []client.ApplyOption{ - client.FieldOwner(e.fieldOwner), - } + o := make([]client.ApplyOption, 0, len(opts)+1) + o = append(o, client.FieldOwner(e.fieldOwner)) o = append(o, opts...) var ac runtime.ApplyConfiguration + switch v := obj.(type) { case runtime.ApplyConfiguration: ac = v @@ -514,24 +560,25 @@ const ( func (e *ObjectEngine) detectOwner( owner client.Object, + ownerStrategy objectEngineOwnerStrategy, actualObject Object, previousOwners []client.Object, ) (ctrlSituation, *metav1.OwnerReference) { // e.ownerStrategy may either work on .metadata.ownerReferences or // on an annotation to allow cross-namespace and cross-cluster refs. - ownerRef, ok := e.ownerStrategy.GetController(actualObject) + ownerRef, ok := ownerStrategy.GetController(actualObject) if !ok { return ctrlSituationNoController, nil } // Are we already controller? - if e.ownerStrategy.IsController(owner, actualObject) { + if ownerStrategy.IsController(owner, actualObject) { return ctrlSituationIsController, &ownerRef } // Check if previous owner is controller. for _, previousOwner := range previousOwners { - if e.ownerStrategy.IsController(previousOwner, actualObject) { + if ownerStrategy.IsController(previousOwner, actualObject) { return ctrlSituationPreviousIsController, &ownerRef } } @@ -596,14 +643,22 @@ func (e *ObjectEngine) revisionAnnotation() string { return e.systemPrefix + "/revision" } -func removeBoxcutterManagedLabel( - ctx context.Context, w client.Writer, obj *unstructured.Unstructured, +func (e *ObjectEngine) removeBoxcutterManagedLabelsAndAnnotations( + ctx context.Context, w client.Writer, obj Object, ) error { - updated := obj.DeepCopy() + updated := obj.DeepCopyObject().(Object) + + annotations := obj.GetAnnotations() + delete(annotations, e.revisionAnnotation()) + obj.SetAnnotations(annotations) labels := updated.GetLabels() + if l, ok := labels[managedByLabel]; ok && l == managedByLabelValue { + delete(labels, managedByLabel) + } delete(labels, boxcutterManagedLabel) + updated.SetLabels(labels) if err := w.Patch(ctx, updated, client.MergeFrom(obj)); err != nil { diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/phases.go b/vendor/pkg.package-operator.run/boxcutter/machinery/phases.go index 18eb2b1ab..9d4d68d46 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/phases.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/phases.go @@ -7,7 +7,6 @@ import ( "strings" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/controller-runtime/pkg/client" "pkg.package-operator.run/boxcutter/machinery/types" "pkg.package-operator.run/boxcutter/validation" @@ -35,22 +34,20 @@ func NewPhaseEngine( type phaseValidator interface { Validate( ctx context.Context, - owner client.Object, phase types.Phase, + opts ...types.PhaseReconcileOption, ) error } type objectEngine interface { Reconcile( ctx context.Context, - owner client.Object, // Owner of the object. revision int64, // Revision number, must start at 1. desiredObject Object, opts ...types.ObjectReconcileOption, ) (ObjectResult, error) Teardown( ctx context.Context, - owner client.Object, // Owner of the object. revision int64, // Revision number, must start at 1. desiredObject Object, opts ...types.ObjectTeardownOption, @@ -87,23 +84,26 @@ type phaseTeardownResult struct { } func (r *phaseTeardownResult) String() string { - out := fmt.Sprintf("Phase %q\n", r.name) + var out strings.Builder + fmt.Fprintf(&out, "Phase %q\n", r.name) if len(r.gone) > 0 { - out += "Gone Objects:\n" + fmt.Fprintln(&out, "Gone Objects:") + for _, gone := range r.gone { - out += "- " + gone.String() + "\n" + fmt.Fprintf(&out, "- %s\n", gone.String()) } } if len(r.waiting) > 0 { - out += "Waiting Objects:\n" + fmt.Fprintln(&out, "Waiting Objects:") + for _, waiting := range r.waiting { - out += "- " + waiting.String() + "\n" + fmt.Fprintf(&out, "- %s\n", waiting.String()) } } - return out + return out.String() } func (r *phaseTeardownResult) GetName() string { @@ -132,11 +132,12 @@ func (r *phaseTeardownResult) Waiting() []types.ObjectRef { // Teardown ensures the given phase is safely removed from the cluster. func (e *PhaseEngine) Teardown( ctx context.Context, - owner client.Object, revision int64, phase types.Phase, opts ...types.PhaseTeardownOption, ) (PhaseTeardownResult, error) { + opts = append(opts, phase.GetTeardownOptions()...) + var options types.PhaseTeardownOptions for _, opt := range opts { opt.ApplyToPhaseTeardownOptions(&options) @@ -144,11 +145,9 @@ func (e *PhaseEngine) Teardown( res := &phaseTeardownResult{name: phase.GetName()} - for _, o := range phase.GetObjects() { - obj := &o - + for _, obj := range phase.GetObjects() { gone, err := e.objectEngine.Teardown( - ctx, owner, revision, obj, options.ForObject(obj)...) + ctx, revision, obj, options.ForObject(obj)...) if err != nil { return res, fmt.Errorf("teardown object: %w", err) } @@ -166,11 +165,12 @@ func (e *PhaseEngine) Teardown( // Reconcile runs actions to bring actual state closer to desired. func (e *PhaseEngine) Reconcile( ctx context.Context, - owner client.Object, revision int64, phase types.Phase, opts ...types.PhaseReconcileOption, ) (PhaseResult, error) { + opts = append(opts, phase.GetReconcileOptions()...) + var options types.PhaseReconcileOptions for _, opt := range opts { opt.ApplyToPhaseReconcileOptions(&options) @@ -181,7 +181,7 @@ func (e *PhaseEngine) Reconcile( } // Preflight - err := e.phaseValidator.Validate(ctx, owner, phase) + err := e.phaseValidator.Validate(ctx, phase, opts...) if err != nil { var perr validation.PhaseValidationError if errors.As(err, &perr) { @@ -194,11 +194,9 @@ func (e *PhaseEngine) Reconcile( } // Reconcile - for _, o := range phase.GetObjects() { - obj := &o - + for _, obj := range phase.GetObjects() { ores, err := e.objectEngine.Reconcile( - ctx, owner, revision, obj, options.ForObject(obj)...) + ctx, revision, obj, options.ForObject(obj)...) if err != nil { return pres, fmt.Errorf("reconciling object: %w", err) } @@ -306,22 +304,25 @@ func (r *phaseResult) IsComplete() bool { } func (r *phaseResult) String() string { - out := fmt.Sprintf( + var out strings.Builder + fmt.Fprintf(&out, "Phase %q\nComplete: %t\nIn Transition: %t\n", r.name, r.IsComplete(), r.InTransition(), ) if err := r.GetValidationError(); err != nil { - out += "Validation Errors:\n" + fmt.Fprintln(&out, "Validation Errors:") + for _, err := range err.Unwrap() { - out += "- " + err.Error() + "\n" + fmt.Fprintf(&out, "- %s\n", err.Error()) } } - out += "Objects:\n" + fmt.Fprintln(&out, "Objects:") + for _, ores := range r.objects { - out += "- " + strings.ReplaceAll(strings.TrimSpace(ores.String()), "\n", "\n ") + "\n" + fmt.Fprintf(&out, "- %s\n", strings.ReplaceAll(strings.TrimSpace(ores.String()), "\n", "\n ")) } - return out + return out.String() } diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/results.go b/vendor/pkg.package-operator.run/boxcutter/machinery/results.go index 97395d123..725252a47 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/results.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/results.go @@ -3,6 +3,7 @@ package machinery import ( "fmt" "sort" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" @@ -304,8 +305,10 @@ func reportStart(or ObjectResult) string { actionStr = "Action (PAUSED)" } + var out strings.Builder + gvk := obj.GetObjectKind().GroupVersionKind() - msg := fmt.Sprintf( + fmt.Fprintf(&out, "Object %s.%s %s/%s\n"+ `%s: %q`+"\n", gvk.Kind, gvk.GroupVersion().String(), @@ -323,26 +326,26 @@ func reportStart(or ObjectResult) string { sort.Strings(probeTypes) if len(probeTypes) > 0 { - msg += "Probes:\n" + fmt.Fprintln(&out, "Probes:") } for _, probeType := range probeTypes { probeRes := probes[probeType] switch probeRes.Status { case types.ProbeStatusTrue: - msg += fmt.Sprintf("- %s: Succeeded\n", probeType) + fmt.Fprintf(&out, "- %s: Succeeded\n", probeType) case types.ProbeStatusFalse: - msg += fmt.Sprintf("- %s: Failed\n", probeType) + fmt.Fprintf(&out, "- %s: Failed\n", probeType) case types.ProbeStatusUnknown: - msg += fmt.Sprintf("- %s: Unknown\n", probeType) + fmt.Fprintf(&out, "- %s: Unknown\n", probeType) } for _, m := range probeRes.Messages { - msg += " - " + m + "\n" + fmt.Fprintf(&out, " - %s\n", m) } } - return msg + return out.String() } func runProbes(obj Object, probes map[string]types.Prober) types.ProbeResultContainer { diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/revision.go b/vendor/pkg.package-operator.run/boxcutter/machinery/revision.go index 84dfc854f..4280071e3 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/revision.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/revision.go @@ -40,14 +40,12 @@ type revisionValidator interface { type phaseEngine interface { Reconcile( ctx context.Context, - owner client.Object, revision int64, phase types.Phase, opts ...types.PhaseReconcileOption, ) (PhaseResult, error) Teardown( ctx context.Context, - owner client.Object, revision int64, phase types.Phase, opts ...types.PhaseTeardownOption, @@ -143,24 +141,27 @@ func (r *revisionResult) GetPhases() []PhaseResult { } func (r *revisionResult) String() string { - out := fmt.Sprintf( + var out strings.Builder + fmt.Fprintf(&out, "Revision\nComplete: %t\nIn Transition: %t\n", r.IsComplete(), r.InTransition(), ) if err := r.GetValidationError(); err != nil { - out += "Validation Errors:\n" + fmt.Fprintln(&out, "Validation Errors:") + for _, err := range err.Unwrap() { - out += "- " + err.Error() + "\n" + fmt.Fprintf(&out, "- %s\n", err.Error()) } } phasesWithResults := map[string]struct{}{} - out += "Phases:\n" + + fmt.Fprintln(&out, "Phases:") for _, ores := range r.phasesResults { phasesWithResults[ores.GetName()] = struct{}{} - out += "- " + strings.TrimSpace(strings.ReplaceAll(ores.String(), "\n", "\n ")) + "\n" + fmt.Fprintf(&out, "- %s\n", strings.TrimSpace(strings.ReplaceAll(ores.String(), "\n", "\n "))) } for _, p := range r.phases { @@ -168,10 +169,10 @@ func (r *revisionResult) String() string { continue } - out += fmt.Sprintf("- Phase %q (Pending)\n", p) + fmt.Fprintf(&out, "- Phase %q (Pending)\n", p) } - return out + return out.String() } // Reconcile runs actions to bring actual state closer to desired. @@ -180,7 +181,7 @@ func (re *RevisionEngine) Reconcile( opts ...types.RevisionReconcileOption, ) (RevisionResult, error) { var options types.RevisionReconcileOptions - for _, opt := range opts { + for _, opt := range append(opts, rev.GetReconcileOptions()...) { opt.ApplyToRevisionReconcileOptions(&options) } @@ -204,7 +205,7 @@ func (re *RevisionEngine) Reconcile( // Reconcile for _, phase := range rev.GetPhases() { pres, err := re.phaseEngine.Reconcile( - ctx, rev.GetOwner(), rev.GetRevisionNumber(), + ctx, rev.GetRevisionNumber(), phase, options.ForPhase(phase.GetName())...) if err != nil { return rres, fmt.Errorf("reconciling object: %w", err) @@ -279,34 +280,39 @@ func (r *revisionTeardownResult) GetGonePhaseNames() []string { } func (r *revisionTeardownResult) String() string { - out := fmt.Sprintf( - "Revision Teardown\nActive: %s\n", - r.active, - ) + var out strings.Builder + fmt.Fprintln(&out, "Revision Teardown") + + if len(r.active) > 0 { + fmt.Fprintf(&out, "Active: %s\n", r.active) + } if len(r.waiting) > 0 { - out += "Waiting Phases:\n" + fmt.Fprintln(&out, "Waiting Phases:") + for _, waiting := range r.waiting { - out += "- " + waiting + "\n" + fmt.Fprintf(&out, "- %s\n", waiting) } } if len(r.gone) > 0 { - out += "Gone Phases:\n" + fmt.Fprintln(&out, "Gone Phases:") + for _, gone := range r.gone { - out += "- " + gone + "\n" + fmt.Fprintf(&out, "- %s\n", gone) } } phasesWithResults := map[string]struct{}{} - out += "Phases:\n" + + fmt.Fprintln(&out, "Phases:") for _, ores := range r.phases { phasesWithResults[ores.GetName()] = struct{}{} - out += "- " + strings.TrimSpace(strings.ReplaceAll(ores.String(), "\n", "\n ")) + "\n" + fmt.Fprintf(&out, "- %s\n", strings.TrimSpace(strings.ReplaceAll(ores.String(), "\n", "\n "))) } - return out + return out.String() } // Teardown ensures the given revision is safely removed from the cluster. @@ -315,7 +321,7 @@ func (re *RevisionEngine) Teardown( opts ...types.RevisionTeardownOption, ) (RevisionTeardownResult, error) { var options types.RevisionTeardownOptions - for _, opt := range opts { + for _, opt := range append(opts, rev.GetTeardownOptions()...) { opt.ApplyToRevisionTeardownOptions(&options) } @@ -336,7 +342,7 @@ func (re *RevisionEngine) Teardown( res.active = p.GetName() pres, err := re.phaseEngine.Teardown( - ctx, rev.GetOwner(), rev.GetRevisionNumber(), + ctx, rev.GetRevisionNumber(), p, options.ForPhase(p.GetName())...) if err != nil { return nil, fmt.Errorf("teardown phase: %w", err) diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/types/options.go b/vendor/pkg.package-operator.run/boxcutter/machinery/types/options.go index 8b2647b9b..a80aba9c1 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/types/options.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/types/options.go @@ -1,6 +1,7 @@ package types import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -21,6 +22,20 @@ func (rropts RevisionReconcileOptions) ForPhase(phaseName string) []PhaseReconci return opts } +func (rropts RevisionReconcileOptions) GetOwner() client.Object { + var phaseOptions PhaseReconcileOptions + for _, opt := range rropts.DefaultPhaseOptions { + opt.ApplyToPhaseReconcileOptions(&phaseOptions) + } + + var objectOptions ObjectReconcileOptions + for _, opt := range phaseOptions.DefaultObjectOptions { + opt.ApplyToObjectReconcileOptions(&objectOptions) + } + + return objectOptions.Owner +} + // RevisionReconcileOption is the common interface for revision reconciliation options. type RevisionReconcileOption interface { ApplyToRevisionReconcileOptions(opts *RevisionReconcileOptions) @@ -102,12 +117,18 @@ type PhaseTeardownOption interface { type ObjectReconcileOptions struct { CollisionProtection CollisionProtection PreviousOwners []client.Object + Owner client.Object + OwnerStrategy OwnerStrategy Paused bool Probes map[string]Prober } // Default sets empty Option fields to their default value. func (opts *ObjectReconcileOptions) Default() { + if opts.Owner != nil && opts.OwnerStrategy == nil { + panic("Owner without ownerStrategy set") + } + if len(opts.CollisionProtection) == 0 { opts.CollisionProtection = CollisionProtectionPrevent } @@ -131,10 +152,16 @@ var ( type ObjectTeardownOptions struct { Orphan bool TeardownWriter client.Writer + Owner client.Object + OwnerStrategy OwnerStrategy } // Default sets empty Option fields to their default value. -func (opts *ObjectTeardownOptions) Default() {} +func (opts *ObjectTeardownOptions) Default() { + if opts.Owner != nil && opts.OwnerStrategy == nil { + panic("Owner without ownerStrategy set") + } +} // ObjectTeardownOption is the common interface for object teardown options. type ObjectTeardownOption interface { @@ -225,6 +252,7 @@ func WithProbe(t string, probe Prober) ObjectReconcileOption { if opts.Probes == nil { opts.Probes = map[string]Prober{} } + opts.Probes[t] = probe }, } @@ -348,6 +376,67 @@ func (p *withPhaseTeardownOptions) ApplyToRevisionTeardownOptions(opts *Revision opts.PhaseOptions[p.phaseName] = p.opts } +// OwnerStrategy interface needed for RevisionEngine. +type OwnerStrategy interface { + SetControllerReference(owner, obj metav1.Object) error + GetController(obj metav1.Object) (metav1.OwnerReference, bool) + IsController(owner, obj metav1.Object) bool + CopyOwnerReferences(objA, objB metav1.Object) + ReleaseController(obj metav1.Object) + RemoveOwner(owner, obj metav1.Object) +} + +// WithOwner sets an owning object and the strategy to use with it. +// Ensures controller-refs are set to track the owner and +// enables handover between owners. +func WithOwner(obj client.Object, start OwnerStrategy) interface { + ComparatorOption + ObjectReconcileOption + ObjectTeardownOption +} { + if len(obj.GetUID()) == 0 { + panic("owner must be persisted to cluster, empty UID") + } + + return &combinedOpts{ + fn: func(opts *ComparatorOptions) { + opts.Owner = obj + opts.OwnerStrategy = start + }, + optionFn: optionFn{ + fn: func(opts *ObjectReconcileOptions) { + opts.Owner = obj + opts.OwnerStrategy = start + }, + }, + teardownOptionFn: teardownOptionFn{ + fn: func(opts *ObjectTeardownOptions) { + opts.Owner = obj + opts.OwnerStrategy = start + }, + }, + } +} + +type combinedOpts struct { + optionFn + teardownOptionFn + fn func(opts *ComparatorOptions) +} + +func (copt *combinedOpts) ApplyToComparatorOptions(opts *ComparatorOptions) { + copt.fn(opts) +} + +type ComparatorOptions struct { + Owner client.Object + OwnerStrategy OwnerStrategy +} + +type ComparatorOption interface { + ApplyToComparatorOptions(opts *ComparatorOptions) +} + type optionFn struct { fn func(opts *ObjectReconcileOptions) } diff --git a/vendor/pkg.package-operator.run/boxcutter/machinery/types/types.go b/vendor/pkg.package-operator.run/boxcutter/machinery/types/types.go index 175a4cc9e..b2d545735 100644 --- a/vendor/pkg.package-operator.run/boxcutter/machinery/types/types.go +++ b/vendor/pkg.package-operator.run/boxcutter/machinery/types/types.go @@ -4,7 +4,6 @@ package types import ( "fmt" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -17,6 +16,10 @@ type ObjectRef struct { // ToObjectRef returns an ObjectRef object from unstructured. func ToObjectRef(obj client.Object) ObjectRef { + if obj == nil { + return ObjectRef{} + } + return ObjectRef{ GroupVersionKind: obj.GetObjectKind().GroupVersionKind(), ObjectKey: client.ObjectKeyFromObject(obj), @@ -29,52 +32,196 @@ func (oid ObjectRef) String() string { } // Phase represents a named collection of objects. -type Phase struct { +type Phase interface { + // GetName returns the name of the phase. + GetName() string + // GetObjects returns the objects contained in the phase. + GetObjects() []client.Object + // GetReconcileOptions returns options for reconciling this phase. + GetReconcileOptions() []PhaseReconcileOption + // GetTeardownOptions returns options for tearing down this phase. + GetTeardownOptions() []PhaseTeardownOption +} + +// PhaseBuilder is a Phase with methods to attach options. +type PhaseBuilder interface { + Phase + // WithReconcileOptions sets PhaseReconcileOptions on this phase. + WithReconcileOptions(opts ...PhaseReconcileOption) PhaseBuilder + // WithTeardownOptions sets PhaseTeardownOption on this phase. + WithTeardownOptions(opts ...PhaseTeardownOption) PhaseBuilder +} + +// NewPhase creates a new PhaseBuilder with the given name and objects. +func NewPhase(name string, objects []client.Object) PhaseBuilder { + return &phase{ + Name: name, + Objects: objects, + } +} + +// NewPhaseWithOwner creates a new PhaseBuilder with the given name, objects and owner. +func NewPhaseWithOwner( + name string, objects []client.Object, + owner client.Object, ownerStrat OwnerStrategy, +) PhaseBuilder { + oo := WithOwner(owner, ownerStrat) + p := &phase{ + Name: name, + Objects: objects, + } + + return p.WithReconcileOptions(oo).WithTeardownOptions(oo) +} + +// phase represents a named collection of objects. +type phase struct { // Name of the Phase. Name string // Objects contained in the phase. - Objects []unstructured.Unstructured + Objects []client.Object + + ReconcileOptions []PhaseReconcileOption + TeardownOptions []PhaseTeardownOption } // GetName returns the name of the phase. -func (p *Phase) GetName() string { +func (p *phase) GetName() string { return p.Name } // GetObjects returns the objects contained in the phase. -func (p *Phase) GetObjects() []unstructured.Unstructured { +func (p *phase) GetObjects() []client.Object { return p.Objects } +// GetReconcileOptions returns options for reconciling this phase. +func (p *phase) GetReconcileOptions() []PhaseReconcileOption { + return p.ReconcileOptions +} + +// GetTeardownOptions returns options for tearing down this phase. +func (p *phase) GetTeardownOptions() []PhaseTeardownOption { + return p.TeardownOptions +} + +// WithReconcileOptions sets PhaseReconcileOptions on this phase. +func (p *phase) WithReconcileOptions(opts ...PhaseReconcileOption) PhaseBuilder { + p.ReconcileOptions = append(p.ReconcileOptions, opts...) + + return p +} + +// WithTeardownOptions sets PhaseTeardownOption on this phase. +func (p *phase) WithTeardownOptions(opts ...PhaseTeardownOption) PhaseBuilder { + p.TeardownOptions = append(p.TeardownOptions, opts...) + + return p +} + // Revision represents the version of a content collection consisting of phases. -type Revision struct { +type Revision interface { + // GetName returns the name of the revision. + GetName() string + // GetRevisionNumber returns the current revision number. + GetRevisionNumber() int64 + // GetPhases returns the phases a revision is made up of. + GetPhases() []Phase + // GetReconcileOptions returns options for reconciling this revision. + GetReconcileOptions() []RevisionReconcileOption + // GetTeardownOptions returns options for tearing down this revision. + GetTeardownOptions() []RevisionTeardownOption +} + +// RevisionBuilder is a Revision with methods to attach options. +type RevisionBuilder interface { + Revision + // WithReconcileOptions sets RevisionReconcileOptions on this revision. + WithReconcileOptions(opts ...RevisionReconcileOption) RevisionBuilder + // WithTeardownOptions sets RevisionTeardownOption on this revision. + WithTeardownOptions(opts ...RevisionTeardownOption) RevisionBuilder +} + +// NewRevision creates a new RevisionBuilder with +// the given name, rev and phases. +func NewRevision( + name string, + revNumber int64, + phases []Phase, +) RevisionBuilder { + return &revision{ + Name: name, + Revision: revNumber, + Phases: phases, + } +} + +// NewRevisionWithOwner creates a new RevisionBuilder +// with the given name, rev, phases and owner. +func NewRevisionWithOwner( + name string, + revNumber int64, + phases []Phase, + owner client.Object, ownerStrat OwnerStrategy, +) RevisionBuilder { + oo := WithOwner(owner, ownerStrat) + r := &revision{ + Name: name, + Revision: revNumber, + Phases: phases, + } + + return r.WithReconcileOptions(oo).WithTeardownOptions(oo) +} + +// revision represents the version of a content collection consisting of phases. +type revision struct { // Name of the Revision. Name string - // Owner object will be added as OwnerReference - // to all objects managed by this revision. - Owner client.Object // Revision number. Revision int64 // Ordered list of phases. Phases []Phase + + ReconcileOptions []RevisionReconcileOption + TeardownOptions []RevisionTeardownOption } // GetName returns the name of the revision. -func (r *Revision) GetName() string { +func (r *revision) GetName() string { return r.Name } -// GetOwner returns the owning object. -func (r *Revision) GetOwner() client.Object { - return r.Owner -} - // GetRevisionNumber returns the current revision number. -func (r *Revision) GetRevisionNumber() int64 { +func (r *revision) GetRevisionNumber() int64 { return r.Revision } // GetPhases returns the phases a revision is made up of. -func (r *Revision) GetPhases() []Phase { +func (r *revision) GetPhases() []Phase { return r.Phases } + +// GetReconcileOptions returns options for reconciling this revision. +func (r *revision) GetReconcileOptions() []RevisionReconcileOption { + return r.ReconcileOptions +} + +// GetTeardownOptions returns options for tearing down this revision. +func (r *revision) GetTeardownOptions() []RevisionTeardownOption { + return r.TeardownOptions +} + +// WithReconcileOptions sets RevisionReconcileOptions on this revision. +func (r *revision) WithReconcileOptions(opts ...RevisionReconcileOption) RevisionBuilder { + r.ReconcileOptions = opts + + return r +} + +// WithTeardownOptions sets RevisionTeardownOption on this revision. +func (r *revision) WithTeardownOptions(opts ...RevisionTeardownOption) RevisionBuilder { + r.TeardownOptions = opts + + return r +} diff --git a/vendor/pkg.package-operator.run/boxcutter/managedcache/objectboundaccess.go b/vendor/pkg.package-operator.run/boxcutter/managedcache/objectboundaccess.go index 3b153b731..17ba9fee5 100644 --- a/vendor/pkg.package-operator.run/boxcutter/managedcache/objectboundaccess.go +++ b/vendor/pkg.package-operator.run/boxcutter/managedcache/objectboundaccess.go @@ -338,6 +338,7 @@ func (m *objectBoundAccessManagerImpl[T]) handleAccessorRequest( go func(ctx context.Context, doneCh chan<- cacheDone) { defer wg.Done() + doneCh <- cacheDone{key: key, err: ctrlcache.Start(ctx)} }(ctx, doneCh) @@ -364,6 +365,7 @@ func (m *objectBoundAccessManagerImpl[T]) request( } responseCh := make(chan accessorResponse, 1) + req.responseCh = responseCh select { case accCh <- req: diff --git a/vendor/pkg.package-operator.run/boxcutter/managedcache/trackingcache.go b/vendor/pkg.package-operator.run/boxcutter/managedcache/trackingcache.go index df0c462e6..a37a29b8a 100644 --- a/vendor/pkg.package-operator.run/boxcutter/managedcache/trackingcache.go +++ b/vendor/pkg.package-operator.run/boxcutter/managedcache/trackingcache.go @@ -156,6 +156,7 @@ func (c *trackingCache) Start(ctx context.Context) error { ctx = logr.NewContext(ctx, c.log) cacheErrCh := make(chan error) + go func() { cacheErrCh <- c.Cache.Start(ctx) }() @@ -261,6 +262,7 @@ func (c *trackingCache) ensureCacheSyncForGVK(ctx context.Context, gvk schema.Gr if _, ok := c.waitingForSync[gvk]; ok { // -> don't start another WaitForCacheSync and instead queue up in c.waitingForSync[gvk]. log.V(-1).Info("new call waiting for WaitForCacheSync already in flight") + c.waitingForSync[gvk] = append(c.waitingForSync[gvk], errCh) return @@ -268,6 +270,7 @@ func (c *trackingCache) ensureCacheSyncForGVK(ctx context.Context, gvk schema.Gr obj := &unstructured.Unstructured{} obj.SetGroupVersionKind(gvk) + i, err := c.Cache.GetInformer(ctx, obj, cache.BlockUntilSynced(false)) if err != nil { errCh <- err @@ -279,6 +282,7 @@ func (c *trackingCache) ensureCacheSyncForGVK(ctx context.Context, gvk schema.Gr isNewInformer := !c.knownInformers.Has(gvk) if isNewInformer { c.knownInformers.Insert(gvk) + if err := c.cacheSourcer.handleNewInformer(i); err != nil { errCh <- err @@ -298,15 +302,20 @@ func (c *trackingCache) ensureCacheSyncForGVK(ctx context.Context, gvk schema.Gr stopCh := make(chan struct{}) c.cacheWaitInFlight[gvk] = stopCh + go func() { log.V(-1).Info("waiting for new informer to sync") + if toolscache.WaitForCacheSync(stopCh, i.HasSynced) { log.V(-1).Info("informer synced successfully") + c.informerSyncCh <- informerSyncResponse{gvk: gvk, err: nil} return } + log.V(-1).Info("wait for informer sync canceled") + c.informerSyncCh <- informerSyncResponse{gvk: gvk, err: context.Canceled} }() }, @@ -394,6 +403,7 @@ func (c *trackingCache) RemoveInformer(ctx context.Context, obj client.Object) e c.gvkRequestCh <- trackingCacheRequest{ do: func(ctx context.Context) { defer close(errCh) + err := c.Cache.RemoveInformer(ctx, obj) if err != nil { errCh <- err @@ -463,20 +473,24 @@ func (c *trackingCache) removeOtherInformers(ctx context.Context, gvksToKeep set } var errs []error + for _, gvkToStop := range gvksToStop { obj := &unstructured.Unstructured{} obj.SetGroupVersionKind(gvkToStop) + if err := c.Cache.RemoveInformer(ctx, obj); err != nil { errs = append(errs, err) continue } + if err := c.stopInformer(ctx, gvkToStop, nil); err != nil { errs = append(errs, err) continue } } + errCh <- errors.Join(errs...) }, } @@ -524,6 +538,7 @@ func (c *trackingCache) watch(ctx context.Context, gvks sets.Set[schema.GroupVer func (c *trackingCache) Free(ctx context.Context, user client.Object) error { c.watchesByUserLock.Lock() defer c.watchesByUserLock.Unlock() + delete(c.watchesByUser, toAccessManagerKey(user)) c.accessLock.Lock() diff --git a/vendor/pkg.package-operator.run/boxcutter/validation/metadata.go b/vendor/pkg.package-operator.run/boxcutter/validation/metadata.go index 9fb4367a8..4393c12af 100644 --- a/vendor/pkg.package-operator.run/boxcutter/validation/metadata.go +++ b/vendor/pkg.package-operator.run/boxcutter/validation/metadata.go @@ -1,15 +1,15 @@ package validation import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" ) -func validateObjectMetadata(obj *unstructured.Unstructured) []error { +func validateObjectMetadata(obj client.Object) []error { var errs []error // Type Meta - if len(obj.GetAPIVersion()) == 0 { + if len(obj.GetObjectKind().GroupVersionKind().Version) == 0 { errs = append(errs, field.Required( field.NewPath("apiVersion"), @@ -17,7 +17,7 @@ func validateObjectMetadata(obj *unstructured.Unstructured) []error { )) } - if len(obj.GetKind()) == 0 { + if len(obj.GetObjectKind().GroupVersionKind().Kind) == 0 { errs = append(errs, field.Required( field.NewPath("kind"), diff --git a/vendor/pkg.package-operator.run/boxcutter/validation/object.go b/vendor/pkg.package-operator.run/boxcutter/validation/object.go index fea52f474..2c068a663 100644 --- a/vendor/pkg.package-operator.run/boxcutter/validation/object.go +++ b/vendor/pkg.package-operator.run/boxcutter/validation/object.go @@ -11,7 +11,6 @@ import ( apimachineryerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -63,16 +62,22 @@ func NewNamespacedObjectValidator( // It returns an ObjectValidationError when it was successfully able to validate the Object. // It returns a different error when unable to validate the object. func (d *ObjectValidator) Validate( - ctx context.Context, owner client.Object, - obj *unstructured.Unstructured, + ctx context.Context, + obj client.Object, + opts ...bctypes.ObjectReconcileOption, ) error { + var options bctypes.ObjectReconcileOptions + for _, opt := range opts { + opt.ApplyToObjectReconcileOptions(&options) + } + // Static metadata validation. errs := validateObjectMetadata(obj) - if !d.allowNamespaceEscalation { + if options.Owner != nil && !d.allowNamespaceEscalation { // Ensure we are not leaving the namespace we are operating in. if err := validateNamespace( - d.restMapper, owner.GetNamespace(), obj, + d.restMapper, options.Owner.GetNamespace(), obj, ); err != nil { errs = append(errs, err) // we don't want to do a dry-run when this already fails. @@ -116,7 +121,7 @@ func (e MustBeInNamespaceError) Error() string { func validateNamespace( restMapper restMapper, namespace string, - obj *unstructured.Unstructured, + obj client.Object, ) error { // shortcut if Namespaces are not limited. if len(namespace) == 0 { @@ -167,7 +172,7 @@ func (e DryRunValidationError) Unwrap() error { func validateDryRun( ctx context.Context, w client.Writer, - obj *unstructured.Unstructured, + obj client.Object, ) error { objectPatch, mErr := json.Marshal(obj) if mErr != nil { @@ -175,7 +180,7 @@ func validateDryRun( } patch := client.RawPatch(types.ApplyPatchType, objectPatch) - dst := obj.DeepCopyObject().(*unstructured.Unstructured) + dst := obj.DeepCopyObject().(client.Object) err := w.Patch(ctx, dst, patch, client.FieldOwner("dummy"), client.ForceOwnership, client.DryRunAll) if apimachineryerrors.IsNotFound(err) { diff --git a/vendor/pkg.package-operator.run/boxcutter/validation/phase.go b/vendor/pkg.package-operator.run/boxcutter/validation/phase.go index 8045324c8..06c385a8d 100644 --- a/vendor/pkg.package-operator.run/boxcutter/validation/phase.go +++ b/vendor/pkg.package-operator.run/boxcutter/validation/phase.go @@ -41,8 +41,13 @@ func NewNamespacedPhaseValidator( // Validate runs validation of the phase and its objects. func (v *PhaseValidator) Validate( - ctx context.Context, owner client.Object, phase types.Phase, + ctx context.Context, phase types.Phase, opts ...types.PhaseReconcileOption, ) error { + var options types.PhaseReconcileOptions + for _, opt := range opts { + opt.ApplyToPhaseReconcileOptions(&options) + } + phaseError := validatePhaseName(phase) var ( @@ -50,10 +55,8 @@ func (v *PhaseValidator) Validate( errs []error ) - for _, o := range phase.GetObjects() { - obj := &o - - err := v.ObjectValidator.Validate(ctx, owner, obj) + for _, obj := range phase.GetObjects() { + err := v.ObjectValidator.Validate(ctx, obj, options.ForObject(obj)...) if err == nil { continue } @@ -121,13 +124,12 @@ func checkForObjectDuplicates(phases ...types.Phase) []ObjectValidationError { conflicts := map[types.ObjectRef]map[string]struct{}{} for _, phase := range phases { - for _, o := range phase.GetObjects() { - obj := &o + for _, obj := range phase.GetObjects() { ref := types.ToObjectRef(obj) otherPhase, ok := uniqueObjectsInPhase[ref] if !ok { - uniqueObjectsInPhase[ref] = phase.Name + uniqueObjectsInPhase[ref] = phase.GetName() continue } @@ -146,7 +148,7 @@ func checkForObjectDuplicates(phases ...types.Phase) []ObjectValidationError { ovs := make([]ObjectValidationError, 0, len(conflicts)) for objRef, phasesMap := range conflicts { - var phases []string + phases := make([]string, 0, len(phasesMap)) for p := range phasesMap { phases = append(phases, p) } diff --git a/vendor/pkg.package-operator.run/boxcutter/validation/revision.go b/vendor/pkg.package-operator.run/boxcutter/validation/revision.go index 14f3abf30..9dd180b9e 100644 --- a/vendor/pkg.package-operator.run/boxcutter/validation/revision.go +++ b/vendor/pkg.package-operator.run/boxcutter/validation/revision.go @@ -34,16 +34,15 @@ func (v *RevisionValidator) Validate(_ context.Context, rev types.Revision) erro func staticValidateMultiplePhases(phases ...types.Phase) []PhaseValidationError { dups := checkForObjectDuplicates(phases...) - //nolint:prealloc var phaseErrors []PhaseValidationError for _, phase := range phases { var objectErrors []ObjectValidationError - for _, obj := range phase.Objects { + for _, obj := range phase.GetObjects() { oe := NewObjectValidationError( - types.ToObjectRef(&obj), - validateObjectMetadata(&obj)..., + types.ToObjectRef(obj), + validateObjectMetadata(obj)..., ) if oe == nil { continue