@@ -27,7 +27,6 @@ import (
2727 ocv1 "github.com/operator-framework/operator-controller/api/v1"
2828 "github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
2929 "github.com/operator-framework/operator-controller/internal/operator-controller/labels"
30- hashutil "github.com/operator-framework/operator-controller/internal/shared/util/hash"
3130)
3231
3332const (
@@ -200,6 +199,7 @@ type Boxcutter struct {
200199 Scheme * runtime.Scheme
201200 RevisionGenerator ClusterExtensionRevisionGenerator
202201 Preflights []Preflight
202+ FieldOwner string
203203}
204204
205205func (bc * Boxcutter ) Apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (bool , string , error ) {
@@ -216,84 +216,107 @@ func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Obj
216216 return objs
217217}
218218
219+ func (bc * Boxcutter ) ensureGVKIsSet (obj client.Object ) error {
220+ if ! obj .GetObjectKind ().GroupVersionKind ().Empty () {
221+ return nil
222+ }
223+
224+ gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
225+ if err != nil {
226+ return err
227+ }
228+
229+ obj .GetObjectKind ().SetGroupVersionKind (gvk )
230+
231+ return nil
232+ }
233+
219234func (bc * Boxcutter ) apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (bool , string , error ) {
220235 // Generate desired revision
221236 desiredRevision , err := bc .RevisionGenerator .GenerateRevision (contentFS , ext , objectLabels , revisionAnnotations )
222237 if err != nil {
223238 return false , "" , err
224239 }
225240
241+ if err := controllerutil .SetControllerReference (ext , desiredRevision , bc .Scheme ); err != nil {
242+ return false , "" , fmt .Errorf ("set ownerref: %w" , err )
243+ }
244+
226245 // List all existing revisions
227246 existingRevisions , err := bc .getExistingRevisions (ctx , ext .GetName ())
228247 if err != nil {
229248 return false , "" , err
230249 }
231- desiredHash := hashutil .DeepHashObject (desiredRevision .Spec .Phases )
232250
233- // Sort into current and previous revisions.
234- var (
235- currentRevision * ocv1.ClusterExtensionRevision
236- )
251+ currentRevision := & ocv1.ClusterExtensionRevision {}
237252 state := StateNeedsInstall
238- if len (existingRevisions ) > 0 {
239- maybeCurrentRevision := existingRevisions [len (existingRevisions )- 1 ]
240- annotations := maybeCurrentRevision .GetAnnotations ()
241- if annotations != nil {
242- if revisionHash , ok := annotations [RevisionHashAnnotation ]; ok && revisionHash == desiredHash {
243- currentRevision = & maybeCurrentRevision
253+ // check if we can update the current revision.
254+ if len (existingRevisions ) > 0 { // nolint:nestif
255+ // try first to update the current revision.
256+ currentRevision = & existingRevisions [len (existingRevisions )- 1 ]
257+ desiredRevision .Spec .Previous = currentRevision .Spec .Previous
258+ desiredRevision .Spec .Revision = currentRevision .Spec .Revision
259+ desiredRevision .Name = currentRevision .Name
260+
261+ if err := bc .ensureGVKIsSet (desiredRevision ); err != nil {
262+ return false , "" , fmt .Errorf ("setting gvk failed: %w" , err )
263+ }
264+ if err = bc .Client .Patch (ctx , desiredRevision , client .Apply , client .FieldOwner (bc .FieldOwner ), client .ForceOwnership ); err != nil {
265+ if ! apierrors .IsInvalid (err ) {
266+ return false , "" , fmt .Errorf ("patching %s Revision: %w" , desiredRevision .Name , err )
244267 }
268+ // We could not update the current revision due to trying to update an immutable field.
269+ // Therefore, we need to create a new revision.
270+ state = StateNeedsUpgrade
271+ } else {
272+ // inplace patch was successful, no changes in phases
273+ state = StateUnchanged
245274 }
246- state = StateNeedsUpgrade
247275 }
248276
249- // Preflights
250- plainObjs := bc .getObjects (desiredRevision )
251- for _ , preflight := range bc .Preflights {
252- if shouldSkipPreflight (ctx , preflight , ext , state ) {
253- continue
254- }
255- switch state {
256- case StateNeedsInstall :
257- err := preflight .Install (ctx , plainObjs )
258- if err != nil {
259- return false , "" , err
277+ if state != StateUnchanged { // nolint:nestif
278+ // Preflights
279+ plainObjs := bc .getObjects (desiredRevision )
280+ for _ , preflight := range bc .Preflights {
281+ if shouldSkipPreflight (ctx , preflight , ext , state ) {
282+ continue
260283 }
261- // TODO: jlanford's IDE says that "StateNeedsUpgrade" condition is always true, but
262- // it isn't immediately obvious why that is. Perhaps len(existingRevisions) is
263- // always greater than 0 (seems unlikely), or shouldSkipPreflight always returns
264- // true (and we continue) when state == StateNeedsInstall?
265- case StateNeedsUpgrade :
266- err := preflight .Upgrade (ctx , plainObjs )
267- if err != nil {
268- return false , "" , err
284+ switch state {
285+ case StateNeedsInstall :
286+ err := preflight .Install (ctx , plainObjs )
287+ if err != nil {
288+ return false , "" , err
289+ }
290+ // TODO: jlanford's IDE says that "StateNeedsUpgrade" condition is always true, but
291+ // it isn't immediately obvious why that is. Perhaps len(existingRevisions) is
292+ // always greater than 0 (seems unlikely), or shouldSkipPreflight always returns
293+ // true (and we continue) when state == StateNeedsInstall?
294+ case StateNeedsUpgrade :
295+ err := preflight .Upgrade (ctx , plainObjs )
296+ if err != nil {
297+ return false , "" , err
298+ }
269299 }
270300 }
271- }
272301
273- if currentRevision == nil {
274- // all Revisions are outdated => create a new one.
302+ // need to create new revision
275303 prevRevisions := existingRevisions
276304 revisionNumber := latestRevisionNumber (prevRevisions ) + 1
277305
278- newRevision := desiredRevision
279- newRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
280- if newRevision .GetAnnotations () == nil {
281- newRevision .Annotations = map [string ]string {}
282- }
283- newRevision .Annotations [RevisionHashAnnotation ] = desiredHash
284- newRevision .Spec .Revision = revisionNumber
306+ desiredRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
307+ desiredRevision .Spec .Revision = revisionNumber
285308
286- if err = bc .setPreviousRevisions (ctx , newRevision , prevRevisions ); err != nil {
309+ if err = bc .setPreviousRevisions (ctx , desiredRevision , prevRevisions ); err != nil {
287310 return false , "" , fmt .Errorf ("garbage collecting old Revisions: %w" , err )
288311 }
289312
290- if err := controllerutil . SetControllerReference ( ext , newRevision , bc .Scheme ); err != nil {
291- return false , "" , fmt .Errorf ("set ownerref : %w" , err )
313+ if err := bc .ensureGVKIsSet ( desiredRevision ); err != nil {
314+ return false , "" , fmt .Errorf ("setting gvk failed : %w" , err )
292315 }
293- if err := bc .Client .Create (ctx , newRevision ); err != nil {
316+ if err := bc .Client .Patch (ctx , desiredRevision , client . Apply , client . FieldOwner ( bc . FieldOwner ), client . ForceOwnership ); err != nil {
294317 return false , "" , fmt .Errorf ("creating new Revision: %w" , err )
295318 }
296- currentRevision = newRevision
319+ currentRevision = desiredRevision
297320 }
298321
299322 progressingCondition := meta .FindStatusCondition (currentRevision .Status .Conditions , ocv1 .TypeProgressing )
0 commit comments