Skip to content

Commit 94f2e67

Browse files
authored
🌱 OPRUN-4122 Drop hash computation of ClusterExtensionRevision phases (#2245)
* Drop hash computation of `ClusterExtensionRevision` phases Applier can decide if a new `ClusterExtensionRevision` needs to be created without computing the digest all objects in phases: * try to patch the current revision * if the operation fails due to invalid payload, it is a signal that we tried to update an immutable field (phases included) * in that case, create a new revision Benefits: * No need to keep the computed digest attached to `ClusterExtensionRevision` as annotation * Revisions are created using SSA, passing the right field owner * Simpler applier logic Changes: * Unit tests updated, rephrasing their names to better reflect the use case scenario under test * Added test-operator 1.2.0 bundle to be able to assert creation of new revision in added e2e `TestClusterExtensionForceInstallNonSuccessorVersion` test * Helper function previously living in `test/e2e/cluster_extension_install_test.go` extracted into `test/helpers/helpers.go` so that it could be used in `test/experimental-e2e/experimental_e2e_test.go` as well * Address reviewer comments
1 parent 687401c commit 94f2e67

File tree

13 files changed

+781
-427
lines changed

13 files changed

+781
-427
lines changed

cmd/operator-controller/main.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,10 @@ type config struct {
107107
globalPullSecret string
108108
}
109109

110-
const authFilePrefix = "operator-controller-global-pull-secrets"
110+
const (
111+
authFilePrefix = "operator-controller-global-pull-secrets"
112+
fieldOwnerPrefix = "olm.operatorframework.io"
113+
)
111114

112115
// podNamespace checks whether the controller is running in a Pod vs.
113116
// being run locally by inspecting the namespace file that gets mounted
@@ -560,6 +563,7 @@ func setupBoxcutter(
560563
Scheme: mgr.GetScheme(),
561564
RevisionGenerator: rg,
562565
Preflights: preflights,
566+
FieldOwner: fmt.Sprintf("%s/clusterextension-controller", fieldOwnerPrefix),
563567
}
564568
ceReconciler.RevisionStatesGetter = &controllers.BoxcutterRevisionStatesGetter{Reader: mgr.GetClient()}
565569
ceReconciler.StorageMigrator = &applier.BoxcutterStorageMigrator{
@@ -568,11 +572,6 @@ func setupBoxcutter(
568572
RevisionGenerator: rg,
569573
}
570574

571-
// Boxcutter
572-
const (
573-
boxcutterSystemPrefixFieldOwner = "olm.operatorframework.io"
574-
)
575-
576575
discoveryClient, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig())
577576
if err != nil {
578577
return fmt.Errorf("unable to create discovery client: %w", err)
@@ -599,8 +598,8 @@ func setupBoxcutter(
599598
machinery.NewObjectEngine(
600599
mgr.GetScheme(), trackingCache, mgr.GetClient(),
601600
ownerhandling.NewNative(mgr.GetScheme()),
602-
machinery.NewComparator(ownerhandling.NewNative(mgr.GetScheme()), discoveryClient, mgr.GetScheme(), boxcutterSystemPrefixFieldOwner),
603-
boxcutterSystemPrefixFieldOwner, boxcutterSystemPrefixFieldOwner,
601+
machinery.NewComparator(ownerhandling.NewNative(mgr.GetScheme()), discoveryClient, mgr.GetScheme(), fieldOwnerPrefix),
602+
fieldOwnerPrefix, fieldOwnerPrefix,
604603
),
605604
validation.NewClusterPhaseValidator(mgr.GetRESTMapper(), mgr.GetClient()),
606605
),

internal/operator-controller/applier/boxcutter.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ 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

3332
const (
34-
RevisionHashAnnotation = "olm.operatorframework.io/hash"
3533
ClusterExtensionRevisionPreviousLimit = 5
3634
)
3735

@@ -200,6 +198,7 @@ type Boxcutter struct {
200198
Scheme *runtime.Scheme
201199
RevisionGenerator ClusterExtensionRevisionGenerator
202200
Preflights []Preflight
201+
FieldOwner string
203202
}
204203

205204
func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
@@ -216,34 +215,56 @@ func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Obj
216215
return objs
217216
}
218217

218+
func (bc *Boxcutter) createOrUpdate(ctx context.Context, obj client.Object) error {
219+
if obj.GetObjectKind().GroupVersionKind().Empty() {
220+
gvk, err := apiutil.GVKForObject(obj, bc.Scheme)
221+
if err != nil {
222+
return err
223+
}
224+
obj.GetObjectKind().SetGroupVersionKind(gvk)
225+
}
226+
return bc.Client.Patch(ctx, obj, client.Apply, client.FieldOwner(bc.FieldOwner), client.ForceOwnership)
227+
}
228+
219229
func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
220230
// Generate desired revision
221231
desiredRevision, err := bc.RevisionGenerator.GenerateRevision(contentFS, ext, objectLabels, revisionAnnotations)
222232
if err != nil {
223233
return false, "", err
224234
}
225235

236+
if err := controllerutil.SetControllerReference(ext, desiredRevision, bc.Scheme); err != nil {
237+
return false, "", fmt.Errorf("set ownerref: %w", err)
238+
}
239+
226240
// List all existing revisions
227241
existingRevisions, err := bc.getExistingRevisions(ctx, ext.GetName())
228242
if err != nil {
229243
return false, "", err
230244
}
231-
desiredHash := hashutil.DeepHashObject(desiredRevision.Spec.Phases)
232245

233-
// Sort into current and previous revisions.
234-
var (
235-
currentRevision *ocv1.ClusterExtensionRevision
236-
)
246+
currentRevision := &ocv1.ClusterExtensionRevision{}
237247
state := StateNeedsInstall
248+
// check if we can update the current revision.
238249
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
244-
}
250+
// try first to update the current revision.
251+
currentRevision = &existingRevisions[len(existingRevisions)-1]
252+
desiredRevision.Spec.Previous = currentRevision.Spec.Previous
253+
desiredRevision.Spec.Revision = currentRevision.Spec.Revision
254+
desiredRevision.Name = currentRevision.Name
255+
256+
err := bc.createOrUpdate(ctx, desiredRevision)
257+
switch {
258+
case apierrors.IsInvalid(err):
259+
// We could not update the current revision due to trying to update an immutable field.
260+
// Therefore, we need to create a new revision.
261+
state = StateNeedsUpgrade
262+
case err == nil:
263+
// inplace patch was successful, no changes in phases
264+
state = StateUnchanged
265+
default:
266+
return false, "", fmt.Errorf("patching %s Revision: %w", desiredRevision.Name, err)
245267
}
246-
state = StateNeedsUpgrade
247268
}
248269

249270
// Preflights
@@ -270,30 +291,22 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
270291
}
271292
}
272293

273-
if currentRevision == nil {
274-
// all Revisions are outdated => create a new one.
294+
if state != StateUnchanged {
295+
// need to create new revision
275296
prevRevisions := existingRevisions
276297
revisionNumber := latestRevisionNumber(prevRevisions) + 1
277298

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
299+
desiredRevision.Name = fmt.Sprintf("%s-%d", ext.Name, revisionNumber)
300+
desiredRevision.Spec.Revision = revisionNumber
285301

286-
if err = bc.setPreviousRevisions(ctx, newRevision, prevRevisions); err != nil {
302+
if err = bc.setPreviousRevisions(ctx, desiredRevision, prevRevisions); err != nil {
287303
return false, "", fmt.Errorf("garbage collecting old Revisions: %w", err)
288304
}
289305

290-
if err := controllerutil.SetControllerReference(ext, newRevision, bc.Scheme); err != nil {
291-
return false, "", fmt.Errorf("set ownerref: %w", err)
292-
}
293-
if err := bc.Client.Create(ctx, newRevision); err != nil {
306+
if err := bc.createOrUpdate(ctx, desiredRevision); err != nil {
294307
return false, "", fmt.Errorf("creating new Revision: %w", err)
295308
}
296-
currentRevision = newRevision
309+
currentRevision = desiredRevision
297310
}
298311

299312
progressingCondition := meta.FindStatusCondition(currentRevision.Status.Conditions, ocv1.TypeProgressing)

0 commit comments

Comments
 (0)