Skip to content

Commit dc20dfb

Browse files
pedjakclaude
andauthored
Simplify Boxcutter applier interface (#2446)
- Change Apply() to return only error instead of (bool, string, error), removing status interpretation logic from the applier. ClusterExtensionRevision conditions are already mirrored to ClusterExtension. - Change ApplyBundleWithBoxcutter to accept a function instead of an interface, making unit tests simpler by allowing inline function mocks Co-authored-by: Claude <noreply@anthropic.com>
1 parent 347be32 commit dc20dfb

File tree

5 files changed

+18
-52
lines changed

5 files changed

+18
-52
lines changed

cmd/operator-controller/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl
625625
controllers.RetrieveRevisionStates(revisionStatesGetter),
626626
controllers.ResolveBundle(c.resolver),
627627
controllers.UnpackBundle(c.imagePuller, c.imageCache),
628-
controllers.ApplyBundleWithBoxcutter(appl),
628+
controllers.ApplyBundleWithBoxcutter(appl.Apply),
629629
}
630630

631631
baseDiscoveryClient, err := discovery.NewDiscoveryClientForConfig(c.mgr.GetConfig())

internal/operator-controller/applier/boxcutter.go

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"helm.sh/helm/v3/pkg/release"
1414
"helm.sh/helm/v3/pkg/storage/driver"
1515
apierrors "k8s.io/apimachinery/pkg/api/errors"
16-
"k8s.io/apimachinery/pkg/api/meta"
1716
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1817
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1918
"k8s.io/apimachinery/pkg/runtime"
@@ -283,10 +282,6 @@ type Boxcutter struct {
283282
FieldOwner string
284283
}
285284

286-
func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
287-
return bc.apply(ctx, contentFS, ext, objectLabels, revisionAnnotations)
288-
}
289-
290285
func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Object {
291286
var objs []client.Object
292287
for _, phase := range rev.Spec.Phases {
@@ -308,21 +303,21 @@ func (bc *Boxcutter) createOrUpdate(ctx context.Context, obj client.Object) erro
308303
return bc.Client.Patch(ctx, obj, client.Apply, client.FieldOwner(bc.FieldOwner), client.ForceOwnership)
309304
}
310305

311-
func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
306+
func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) error {
312307
// Generate desired revision
313308
desiredRevision, err := bc.RevisionGenerator.GenerateRevision(ctx, contentFS, ext, objectLabels, revisionAnnotations)
314309
if err != nil {
315-
return false, "", err
310+
return err
316311
}
317312

318313
if err := controllerutil.SetControllerReference(ext, desiredRevision, bc.Scheme); err != nil {
319-
return false, "", fmt.Errorf("set ownerref: %w", err)
314+
return fmt.Errorf("set ownerref: %w", err)
320315
}
321316

322317
// List all existing revisions
323318
existingRevisions, err := bc.getExistingRevisions(ctx, ext.GetName())
324319
if err != nil {
325-
return false, "", err
320+
return err
326321
}
327322

328323
currentRevision := &ocv1.ClusterExtensionRevision{}
@@ -344,7 +339,7 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
344339
// inplace patch was successful, no changes in phases
345340
state = StateUnchanged
346341
default:
347-
return false, "", fmt.Errorf("patching %s Revision: %w", desiredRevision.Name, err)
342+
return fmt.Errorf("patching %s Revision: %w", desiredRevision.Name, err)
348343
}
349344
}
350345

@@ -358,7 +353,7 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
358353
case StateNeedsInstall:
359354
err := preflight.Install(ctx, plainObjs)
360355
if err != nil {
361-
return false, "", err
356+
return err
362357
}
363358
// TODO: jlanford's IDE says that "StateNeedsUpgrade" condition is always true, but
364359
// it isn't immediately obvious why that is. Perhaps len(existingRevisions) is
@@ -367,7 +362,7 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
367362
case StateNeedsUpgrade:
368363
err := preflight.Upgrade(ctx, plainObjs)
369364
if err != nil {
370-
return false, "", err
365+
return err
371366
}
372367
}
373368
}
@@ -381,34 +376,15 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
381376
desiredRevision.Spec.Revision = revisionNumber
382377

383378
if err = bc.garbageCollectOldRevisions(ctx, prevRevisions); err != nil {
384-
return false, "", fmt.Errorf("garbage collecting old revisions: %w", err)
379+
return fmt.Errorf("garbage collecting old revisions: %w", err)
385380
}
386381

387382
if err := bc.createOrUpdate(ctx, desiredRevision); err != nil {
388-
return false, "", fmt.Errorf("creating new Revision: %w", err)
383+
return fmt.Errorf("creating new Revision: %w", err)
389384
}
390-
currentRevision = desiredRevision
391385
}
392386

393-
progressingCondition := meta.FindStatusCondition(currentRevision.Status.Conditions, ocv1.TypeProgressing)
394-
availableCondition := meta.FindStatusCondition(currentRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
395-
succeededCondition := meta.FindStatusCondition(currentRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded)
396-
397-
if progressingCondition == nil && availableCondition == nil && succeededCondition == nil {
398-
return false, "New revision created", nil
399-
} else if progressingCondition != nil && progressingCondition.Status == metav1.ConditionTrue {
400-
switch progressingCondition.Reason {
401-
case ocv1.ReasonSucceeded:
402-
return true, "", nil
403-
case ocv1.ClusterExtensionRevisionReasonRetrying:
404-
return false, "", errors.New(progressingCondition.Message)
405-
default:
406-
return false, progressingCondition.Message, nil
407-
}
408-
} else if succeededCondition != nil && succeededCondition.Status != metav1.ConditionTrue {
409-
return false, succeededCondition.Message, nil
410-
}
411-
return true, "", nil
387+
return nil
412388
}
413389

414390
// garbageCollectOldRevisions deletes archived revisions beyond ClusterExtensionRevisionRetentionLimit.

internal/operator-controller/applier/boxcutter_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -926,18 +926,14 @@ func TestBoxcutter_Apply(t *testing.T) {
926926
labels.PackageNameKey: "test-package",
927927
}
928928
}
929-
installSucceeded, installStatus, err := boxcutter.Apply(t.Context(), testFS, ext, nil, revisionAnnotations)
929+
err := boxcutter.Apply(t.Context(), testFS, ext, nil, revisionAnnotations)
930930

931931
// Assert
932932
if tc.expectedErr != "" {
933933
require.Error(t, err)
934934
assert.Contains(t, err.Error(), tc.expectedErr)
935-
assert.False(t, installSucceeded)
936-
assert.Empty(t, installStatus)
937935
} else {
938936
require.NoError(t, err)
939-
assert.False(t, installSucceeded)
940-
assert.Equal(t, "New revision created", installStatus)
941937
}
942938

943939
if tc.validate != nil {

internal/operator-controller/controllers/boxcutter_reconcile_steps.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"cmp"
2121
"context"
2222
"fmt"
23+
"io/fs"
2324
"slices"
2425

2526
apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -93,7 +94,7 @@ func MigrateStorage(m StorageMigrator) ReconcileStepFunc {
9394
}
9495
}
9596

96-
func ApplyBundleWithBoxcutter(a Applier) ReconcileStepFunc {
97+
func ApplyBundleWithBoxcutter(apply func(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) error) ReconcileStepFunc {
9798
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
9899
l := log.FromContext(ctx)
99100
revisionAnnotations := map[string]string{
@@ -108,7 +109,7 @@ func ApplyBundleWithBoxcutter(a Applier) ReconcileStepFunc {
108109
}
109110

110111
l.Info("applying bundle contents")
111-
if _, _, err := a.Apply(ctx, state.imageFS, ext, objLbls, revisionAnnotations); err != nil {
112+
if err := apply(ctx, state.imageFS, ext, objLbls, revisionAnnotations); err != nil {
112113
// If there was an error applying the resolved bundle,
113114
// report the error via the Progressing condition.
114115
setStatusProgressing(ext, wrapErrorWithResolutionInfo(state.resolvedRevisionMetadata.BundleMetadata, err))

internal/operator-controller/controllers/boxcutter_reconcile_steps_apply_test.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@ import (
2828
ocv1 "github.com/operator-framework/operator-controller/api/v1"
2929
)
3030

31-
type mockApplier struct {
32-
err error
33-
}
34-
35-
func (m *mockApplier) Apply(_ context.Context, _ fs.FS, _ *ocv1.ClusterExtension, _ map[string]string, _ map[string]string) (bool, string, error) {
36-
return true, "", m.err
37-
}
38-
3931
func TestApplyBundleWithBoxcutter(t *testing.T) {
4032
type args struct {
4133
activeRevisions []ocv1.RevisionStatus
@@ -119,7 +111,6 @@ func TestApplyBundleWithBoxcutter(t *testing.T) {
119111
} {
120112
t.Run(tc.name, func(t *testing.T) {
121113
ctx := context.Background()
122-
applier := &mockApplier{}
123114

124115
ext := &ocv1.ClusterExtension{
125116
ObjectMeta: metav1.ObjectMeta{
@@ -142,7 +133,9 @@ func TestApplyBundleWithBoxcutter(t *testing.T) {
142133
imageFS: fstest.MapFS{},
143134
}
144135

145-
stepFunc := ApplyBundleWithBoxcutter(applier)
136+
stepFunc := ApplyBundleWithBoxcutter(func(_ context.Context, _ fs.FS, _ *ocv1.ClusterExtension, _, _ map[string]string) error {
137+
return nil
138+
})
146139
result, err := stepFunc(ctx, state, ext)
147140
require.NoError(t, err)
148141
require.Nil(t, result)

0 commit comments

Comments
 (0)