@@ -29,7 +29,6 @@ import (
2929 "github.com/operator-framework/operator-controller/internal/operator-controller/labels"
3030 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
3131 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
32- hashutil "github.com/operator-framework/operator-controller/internal/shared/util/hash"
3332)
3433
3534const (
@@ -202,6 +201,7 @@ type Boxcutter struct {
202201 Scheme * runtime.Scheme
203202 RevisionGenerator ClusterExtensionRevisionGenerator
204203 Preflights []Preflight
204+ FieldOwner string
205205}
206206
207207func (bc * Boxcutter ) Apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (bool , string , error ) {
@@ -218,84 +218,107 @@ func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Obj
218218 return objs
219219}
220220
221+ func (bc * Boxcutter ) ensureGVKIsSet (obj client.Object ) error {
222+ if ! obj .GetObjectKind ().GroupVersionKind ().Empty () {
223+ return nil
224+ }
225+
226+ gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
227+ if err != nil {
228+ return err
229+ }
230+
231+ obj .GetObjectKind ().SetGroupVersionKind (gvk )
232+
233+ return nil
234+ }
235+
221236func (bc * Boxcutter ) apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (bool , string , error ) {
222237 // Generate desired revision
223238 desiredRevision , err := bc .RevisionGenerator .GenerateRevision (contentFS , ext , objectLabels , revisionAnnotations )
224239 if err != nil {
225240 return false , "" , err
226241 }
227242
243+ if err := controllerutil .SetControllerReference (ext , desiredRevision , bc .Scheme ); err != nil {
244+ return false , "" , fmt .Errorf ("set ownerref: %w" , err )
245+ }
246+
228247 // List all existing revisions
229248 existingRevisions , err := bc .getExistingRevisions (ctx , ext .GetName ())
230249 if err != nil {
231250 return false , "" , err
232251 }
233- desiredHash := hashutil .DeepHashObject (desiredRevision .Spec .Phases )
234252
235- // Sort into current and previous revisions.
236- var (
237- currentRevision * ocv1.ClusterExtensionRevision
238- )
253+ currentRevision := & ocv1.ClusterExtensionRevision {}
239254 state := StateNeedsInstall
240- if len (existingRevisions ) > 0 {
241- maybeCurrentRevision := existingRevisions [len (existingRevisions )- 1 ]
242- annotations := maybeCurrentRevision .GetAnnotations ()
243- if annotations != nil {
244- if revisionHash , ok := annotations [RevisionHashAnnotation ]; ok && revisionHash == desiredHash {
245- currentRevision = & maybeCurrentRevision
255+ // check if we can update the current revision.
256+ if len (existingRevisions ) > 0 { // nolint:nestif
257+ // try first to update the current revision.
258+ currentRevision = & existingRevisions [len (existingRevisions )- 1 ]
259+ desiredRevision .Spec .Previous = currentRevision .Spec .Previous
260+ desiredRevision .Spec .Revision = currentRevision .Spec .Revision
261+ desiredRevision .Name = currentRevision .Name
262+
263+ if err := bc .ensureGVKIsSet (desiredRevision ); err != nil {
264+ return false , "" , fmt .Errorf ("setting gvk failed: %w" , err )
265+ }
266+ if err = bc .Client .Patch (ctx , desiredRevision , client .Apply , client .FieldOwner (bc .FieldOwner ), client .ForceOwnership ); err != nil {
267+ if ! apierrors .IsInvalid (err ) {
268+ return false , "" , fmt .Errorf ("patching %s Revision: %w" , desiredRevision .Name , err )
246269 }
270+ // We could not update the current revision due to trying to update an immutable field.
271+ // Therefore, we need to create a new revision.
272+ state = StateNeedsUpgrade
273+ } else {
274+ // inplace patch was successful, no changes in phases
275+ state = StateUnchanged
247276 }
248- state = StateNeedsUpgrade
249277 }
250278
251- // Preflights
252- plainObjs := bc .getObjects (desiredRevision )
253- for _ , preflight := range bc .Preflights {
254- if shouldSkipPreflight (ctx , preflight , ext , state ) {
255- continue
256- }
257- switch state {
258- case StateNeedsInstall :
259- err := preflight .Install (ctx , plainObjs )
260- if err != nil {
261- return false , "" , err
279+ if state != StateUnchanged { // nolint:nestif
280+ // Preflights
281+ plainObjs := bc .getObjects (desiredRevision )
282+ for _ , preflight := range bc .Preflights {
283+ if shouldSkipPreflight (ctx , preflight , ext , state ) {
284+ continue
262285 }
263- // TODO: jlanford's IDE says that "StateNeedsUpgrade" condition is always true, but
264- // it isn't immediately obvious why that is. Perhaps len(existingRevisions) is
265- // always greater than 0 (seems unlikely), or shouldSkipPreflight always returns
266- // true (and we continue) when state == StateNeedsInstall?
267- case StateNeedsUpgrade :
268- err := preflight .Upgrade (ctx , plainObjs )
269- if err != nil {
270- return false , "" , err
286+ switch state {
287+ case StateNeedsInstall :
288+ err := preflight .Install (ctx , plainObjs )
289+ if err != nil {
290+ return false , "" , err
291+ }
292+ // TODO: jlanford's IDE says that "StateNeedsUpgrade" condition is always true, but
293+ // it isn't immediately obvious why that is. Perhaps len(existingRevisions) is
294+ // always greater than 0 (seems unlikely), or shouldSkipPreflight always returns
295+ // true (and we continue) when state == StateNeedsInstall?
296+ case StateNeedsUpgrade :
297+ err := preflight .Upgrade (ctx , plainObjs )
298+ if err != nil {
299+ return false , "" , err
300+ }
271301 }
272302 }
273- }
274303
275- if currentRevision == nil {
276- // all Revisions are outdated => create a new one.
304+ // need to create new revision
277305 prevRevisions := existingRevisions
278306 revisionNumber := latestRevisionNumber (prevRevisions ) + 1
279307
280- newRevision := desiredRevision
281- newRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
282- if newRevision .GetAnnotations () == nil {
283- newRevision .Annotations = map [string ]string {}
284- }
285- newRevision .Annotations [RevisionHashAnnotation ] = desiredHash
286- newRevision .Spec .Revision = revisionNumber
308+ desiredRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
309+ desiredRevision .Spec .Revision = revisionNumber
287310
288- if err = bc .setPreviousRevisions (ctx , newRevision , prevRevisions ); err != nil {
311+ if err = bc .setPreviousRevisions (ctx , desiredRevision , prevRevisions ); err != nil {
289312 return false , "" , fmt .Errorf ("garbage collecting old Revisions: %w" , err )
290313 }
291314
292- if err := controllerutil . SetControllerReference ( ext , newRevision , bc .Scheme ); err != nil {
293- return false , "" , fmt .Errorf ("set ownerref : %w" , err )
315+ if err := bc .ensureGVKIsSet ( desiredRevision ); err != nil {
316+ return false , "" , fmt .Errorf ("setting gvk failed : %w" , err )
294317 }
295- if err := bc .Client .Create (ctx , newRevision ); err != nil {
318+ if err := bc .Client .Patch (ctx , desiredRevision , client . Apply , client . FieldOwner ( bc . FieldOwner ), client . ForceOwnership ); err != nil {
296319 return false , "" , fmt .Errorf ("creating new Revision: %w" , err )
297320 }
298- currentRevision = newRevision
321+ currentRevision = desiredRevision
299322 }
300323
301324 progressingCondition := meta .FindStatusCondition (currentRevision .Status .Conditions , ocv1 .TypeProgressing )
0 commit comments