Skip to content

Commit 1cf61b6

Browse files
committed
Use separate state type for exchanging data between reconcile steps
1 parent fcc42a1 commit 1cf61b6

File tree

3 files changed

+53
-90
lines changed

3 files changed

+53
-90
lines changed

internal/operator-controller/controllers/boxcutter_reconcile_steps.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,15 @@ func (d *BoxcutterRevisionStatesGetter) GetRevisionStates(ctx context.Context, e
8181
}
8282

8383
func MigrateStorage(m StorageMigrator) ReconcileStepFunc {
84-
return func(ctx context.Context, ext *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error) {
84+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
8585
objLbls := map[string]string{
8686
labels.OwnerKindKey: ocv1.ClusterExtensionKind,
8787
labels.OwnerNameKey: ext.GetName(),
8888
}
8989

9090
if err := m.Migrate(ctx, ext, objLbls); err != nil {
91-
return ctx, nil, fmt.Errorf("migrating storage: %w", err)
91+
return nil, fmt.Errorf("migrating storage: %w", err)
9292
}
93-
return ctx, nil, nil
93+
return nil, nil
9494
}
9595
}

internal/operator-controller/controllers/clusterextension_controller.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ const (
5858
ClusterExtensionCleanupContentManagerCacheFinalizer = "olm.operatorframework.io/cleanup-contentmanager-cache"
5959
)
6060

61+
type reconcileState struct {
62+
revisionStates *RevisionStates
63+
resolvedRevisionMetadata *RevisionMetadata
64+
imageFS fs.FS
65+
}
66+
6167
// ReconcileStepFunc represents a single step in the ClusterExtension reconciliation process.
62-
// It takes a context and ClusterExtension object as input and returns:
63-
// - A potentially modified context for the next step
68+
// It takes a context, state and ClusterExtension object as input and returns:
6469
// - Any error that occurred during reconciliation, which will be returned to the caller
65-
type ReconcileStepFunc func(context.Context, *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error)
70+
// - A ctrl.Result that indicates whether reconciliation should complete immediately or be retried later
71+
type ReconcileStepFunc func(context.Context, *reconcileState, *ocv1.ClusterExtension) (*ctrl.Result, error)
6672

6773
// ReconcileSteps is an ordered collection of reconciliation steps that are executed sequentially.
6874
// Each step receives the output context from the previous step, allowing data to flow through the pipeline.
@@ -77,18 +83,15 @@ type ReconcileSteps []ReconcileStepFunc
7783
func (steps *ReconcileSteps) Reconcile(ctx context.Context, ext *ocv1.ClusterExtension) (ctrl.Result, error) {
7884
var res *ctrl.Result
7985
var err error
80-
nextStepCtx := ctx
86+
s := &reconcileState{}
8187
for _, step := range *steps {
82-
nextStepCtx, res, err = step(nextStepCtx, ext)
88+
res, err = step(ctx, s, ext)
8389
if err != nil {
8490
return ctrl.Result{}, err
8591
}
8692
if res != nil {
8793
return *res, nil
8894
}
89-
if nextStepCtx == nil {
90-
return ctrl.Result{}, nil
91-
}
9295
}
9396
return ctrl.Result{}, nil
9497
}

internal/operator-controller/controllers/clusterextension_reconcile_steps.go

Lines changed: 39 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ package controllers
1919
import (
2020
"context"
2121
"errors"
22-
"fmt"
23-
"io/fs"
2422

2523
apimeta "k8s.io/apimachinery/pkg/api/meta"
2624
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -36,55 +34,35 @@ import (
3634
imageutil "github.com/operator-framework/operator-controller/internal/shared/util/image"
3735
)
3836

39-
type revisionStatesKey struct{}
40-
type resolvedRevisionMetadataKey struct{}
41-
type imageFSKey struct{}
42-
43-
func valueFromContext[T any](ctx context.Context, key any) (*T, error) {
44-
v := ctx.Value(key)
45-
if v == nil {
46-
return nil, fmt.Errorf("value not found in context under key %v", key)
47-
}
48-
ptv, ok := v.(*T)
49-
if !ok {
50-
tv, ok := v.(T)
51-
if !ok {
52-
return nil, fmt.Errorf("value found in context under key %v is not of type %T or pointer to it", key, v)
53-
}
54-
return &tv, nil
55-
}
56-
return ptv, nil
57-
}
58-
5937
func HandleFinalizers(f finalizer.Finalizer) ReconcileStepFunc {
60-
return func(ctx context.Context, ext *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error) {
38+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
6139
l := log.FromContext(ctx)
6240

6341
l.Info("handling finalizers")
6442
finalizeResult, err := f.Finalize(ctx, ext)
6543
if err != nil {
6644
setStatusProgressing(ext, err)
67-
return ctx, nil, err
45+
return nil, err
6846
}
6947
if finalizeResult.Updated || finalizeResult.StatusUpdated {
7048
// On create: make sure the finalizer is applied before we do anything
7149
// On delete: make sure we do nothing after the finalizer is removed
72-
return ctx, &ctrl.Result{}, nil
50+
return &ctrl.Result{}, nil
7351
}
7452

7553
if ext.GetDeletionTimestamp() != nil {
7654
// If we've gotten here, that means the cluster extension is being deleted, we've handled all of
7755
// _our_ finalizers (above), but the cluster extension is still present in the cluster, likely
7856
// because there are _other_ finalizers that other controllers need to handle, (e.g. the orphan
7957
// deletion finalizer).
80-
return nil, nil, nil
58+
return &ctrl.Result{}, nil
8159
}
82-
return ctx, nil, nil
60+
return nil, nil
8361
}
8462
}
8563

8664
func RetrieveRevisionStates(r RevisionStatesGetter) ReconcileStepFunc {
87-
return func(ctx context.Context, ext *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error) {
65+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
8866
l := log.FromContext(ctx)
8967
l.Info("getting installed bundle")
9068
revisionStates, err := r.GetRevisionStates(ctx, ext)
@@ -94,37 +72,34 @@ func RetrieveRevisionStates(r RevisionStatesGetter) ReconcileStepFunc {
9472
if errors.As(err, &saerr) {
9573
setInstalledStatusConditionUnknown(ext, saerr.Error())
9674
setStatusProgressing(ext, errors.New("installation cannot proceed due to missing ServiceAccount"))
97-
return ctx, nil, err
75+
return nil, err
9876
}
9977
setInstalledStatusConditionUnknown(ext, err.Error())
10078
setStatusProgressing(ext, errors.New("retrying to get installed bundle"))
101-
return ctx, nil, err
79+
return nil, err
10280
}
103-
return context.WithValue(ctx, revisionStatesKey{}, revisionStates), nil, nil
81+
state.revisionStates = revisionStates
82+
return nil, nil
10483
}
10584
}
10685

10786
func RetrieveRevisionMetadata(r resolve.Resolver) ReconcileStepFunc {
108-
return func(ctx context.Context, ext *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error) {
87+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
10988
l := log.FromContext(ctx)
110-
revisionStates, err := valueFromContext[RevisionStates](ctx, revisionStatesKey{})
111-
if err != nil {
112-
return ctx, nil, err
113-
}
11489
var resolvedRevisionMetadata *RevisionMetadata
115-
if len(revisionStates.RollingOut) == 0 {
90+
if len(state.revisionStates.RollingOut) == 0 {
11691
l.Info("resolving bundle")
11792
var bm *ocv1.BundleMetadata
118-
if revisionStates.Installed != nil {
119-
bm = &revisionStates.Installed.BundleMetadata
93+
if state.revisionStates.Installed != nil {
94+
bm = &state.revisionStates.Installed.BundleMetadata
12095
}
12196
resolvedBundle, resolvedBundleVersion, resolvedDeprecation, err := r.Resolve(ctx, ext, bm)
12297
if err != nil {
12398
// Note: We don't distinguish between resolution-specific errors and generic errors
12499
setStatusProgressing(ext, err)
125-
setInstalledStatusFromRevisionStates(ext, revisionStates)
100+
setInstalledStatusFromRevisionStates(ext, state.revisionStates)
126101
ensureAllConditionsWithReason(ext, ocv1.ReasonFailed, err.Error())
127-
return ctx, nil, err
102+
return nil, err
128103
}
129104

130105
// set deprecation status after _successful_ resolution
@@ -148,54 +123,39 @@ func RetrieveRevisionMetadata(r resolve.Resolver) ReconcileStepFunc {
148123
BundleMetadata: bundleutil.MetadataFor(resolvedBundle.Name, *resolvedBundleVersion),
149124
}
150125
} else {
151-
resolvedRevisionMetadata = revisionStates.RollingOut[0]
126+
resolvedRevisionMetadata = state.revisionStates.RollingOut[0]
152127
}
153-
return context.WithValue(ctx, resolvedRevisionMetadataKey{}, resolvedRevisionMetadata), nil, nil
128+
state.resolvedRevisionMetadata = resolvedRevisionMetadata
129+
return nil, nil
154130
}
155131
}
156132

157133
func UnpackBundle(i imageutil.Puller, cache imageutil.Cache) ReconcileStepFunc {
158-
return func(ctx context.Context, ext *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error) {
134+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
159135
l := log.FromContext(ctx)
160136
l.Info("unpacking resolved bundle")
161-
resolvedRevisionMetadata, err := valueFromContext[RevisionMetadata](ctx, resolvedRevisionMetadataKey{})
137+
imageFS, _, _, err := i.Pull(ctx, ext.GetName(), state.resolvedRevisionMetadata.Image, cache)
162138
if err != nil {
163-
return ctx, nil, err
164-
}
165-
imageFS, _, _, err := i.Pull(ctx, ext.GetName(), resolvedRevisionMetadata.Image, cache)
166-
if err != nil {
167-
revisionStates := ctx.Value(revisionStatesKey{}).(*RevisionStates)
168139
// Wrap the error passed to this with the resolution information until we have successfully
169140
// installed since we intend for the progressing condition to replace the resolved condition
170141
// and will be removing the .status.resolution field from the ClusterExtension status API
171-
setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedRevisionMetadata.BundleMetadata, err))
172-
setInstalledStatusFromRevisionStates(ext, revisionStates)
173-
return ctx, nil, err
142+
setStatusProgressing(ext, wrapErrorWithResolutionInfo(state.resolvedRevisionMetadata.BundleMetadata, err))
143+
setInstalledStatusFromRevisionStates(ext, state.revisionStates)
144+
return nil, err
174145
}
175-
return context.WithValue(ctx, imageFSKey{}, imageFS), nil, nil
146+
state.imageFS = imageFS
147+
return nil, nil
176148
}
177149
}
178150

179151
func ApplyBundle(a Applier) ReconcileStepFunc {
180-
return func(ctx context.Context, ext *ocv1.ClusterExtension) (context.Context, *ctrl.Result, error) {
152+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
181153
l := log.FromContext(ctx)
182-
resolvedRevisionMetadata, err := valueFromContext[RevisionMetadata](ctx, resolvedRevisionMetadataKey{})
183-
if err != nil {
184-
return ctx, nil, err
185-
}
186-
imageFS, err := valueFromContext[fs.FS](ctx, imageFSKey{})
187-
if err != nil {
188-
return ctx, nil, err
189-
}
190-
revisionStates, err := valueFromContext[RevisionStates](ctx, revisionStatesKey{})
191-
if err != nil {
192-
return ctx, nil, err
193-
}
194154
storeLbls := map[string]string{
195-
labels.BundleNameKey: resolvedRevisionMetadata.Name,
196-
labels.PackageNameKey: resolvedRevisionMetadata.Package,
197-
labels.BundleVersionKey: resolvedRevisionMetadata.Version,
198-
labels.BundleReferenceKey: resolvedRevisionMetadata.Image,
155+
labels.BundleNameKey: state.resolvedRevisionMetadata.Name,
156+
labels.PackageNameKey: state.resolvedRevisionMetadata.Package,
157+
labels.BundleVersionKey: state.resolvedRevisionMetadata.Version,
158+
labels.BundleReferenceKey: state.resolvedRevisionMetadata.Image,
199159
}
200160
objLbls := map[string]string{
201161
labels.OwnerKindKey: ocv1.ClusterExtensionKind,
@@ -212,21 +172,21 @@ func ApplyBundle(a Applier) ReconcileStepFunc {
212172
// to ensure exponential backoff can occur:
213173
// - Permission errors (it is not possible to watch changes to permissions.
214174
// The only way to eventually recover from permission errors is to keep retrying).
215-
rolloutSucceeded, rolloutStatus, err := a.Apply(ctx, *imageFS, ext, objLbls, storeLbls)
175+
rolloutSucceeded, rolloutStatus, err := a.Apply(ctx, state.imageFS, ext, objLbls, storeLbls)
216176

217177
// Set installed status
218178
if rolloutSucceeded {
219-
revisionStates = &RevisionStates{Installed: resolvedRevisionMetadata}
220-
} else if err == nil && revisionStates.Installed == nil && len(revisionStates.RollingOut) == 0 {
221-
revisionStates = &RevisionStates{RollingOut: []*RevisionMetadata{resolvedRevisionMetadata}}
179+
state.revisionStates = &RevisionStates{Installed: state.resolvedRevisionMetadata}
180+
} else if err == nil && state.revisionStates.Installed == nil && len(state.revisionStates.RollingOut) == 0 {
181+
state.revisionStates = &RevisionStates{RollingOut: []*RevisionMetadata{state.resolvedRevisionMetadata}}
222182
}
223-
setInstalledStatusFromRevisionStates(ext, revisionStates)
183+
setInstalledStatusFromRevisionStates(ext, state.revisionStates)
224184

225185
// If there was an error applying the resolved bundle,
226186
// report the error via the Progressing condition.
227187
if err != nil {
228-
setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedRevisionMetadata.BundleMetadata, err))
229-
return ctx, nil, err
188+
setStatusProgressing(ext, wrapErrorWithResolutionInfo(state.resolvedRevisionMetadata.BundleMetadata, err))
189+
return nil, err
230190
} else if !rolloutSucceeded {
231191
apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{
232192
Type: ocv1.TypeProgressing,
@@ -238,6 +198,6 @@ func ApplyBundle(a Applier) ReconcileStepFunc {
238198
} else {
239199
setStatusProgressing(ext, nil)
240200
}
241-
return ctx, nil, nil
201+
return nil, nil
242202
}
243203
}

0 commit comments

Comments
 (0)