Skip to content

Commit 4e0d40a

Browse files
committed
move pending release handling out of getReleaseState
1 parent cd87fe2 commit 4e0d40a

File tree

2 files changed

+35
-28
lines changed

2 files changed

+35
-28
lines changed

Diff for: internal/operator-controller/applier/helm.go

+26-19
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const (
3636
maxHelmReleaseHistory = 10
3737
)
3838

39+
var (
40+
errPendingRelease = errors.New("release is in a pending state")
41+
errBuildHelmChartFuncNil = errors.New("BundleToHelmChartFn is nil")
42+
)
43+
3944
// Preflight is a check that should be run before making any changes to the cluster
4045
type Preflight interface {
4146
// Install runs checks that should be successful prior
@@ -96,6 +101,23 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExte
96101
}
97102

98103
rel, desiredRel, state, err := h.getReleaseState(ctx, ac, ext, chrt, values, post)
104+
// if a release is pending, that means that a helm action
105+
// (installation/upgrade) we were attempting was likely interrupted in-flight.
106+
// Pending release would leave us in reconciliation error loop because helm
107+
// wouldn't be able to progress automatically from it.
108+
//
109+
// one of the workarounds is to try and remove helm metadata relating to
110+
// that pending release which should 'reset' its state communicated to helm
111+
// and the next reconciliation should be able to successfully pick up from here
112+
// for context see: https://github.com/helm/helm/issues/5595 and https://github.com/helm/helm/issues/7476
113+
// and the discussion in https://github.com/operator-framework/operator-controller/pull/1776
114+
if errors.Is(err, errPendingRelease) {
115+
if _, err := ac.Config().Releases.Delete(rel.Name, rel.Version); err != nil {
116+
return nil, "", fmt.Errorf("failed removing pending release %q version %d metadata: %w", rel.Name, rel.Version, err)
117+
}
118+
// return an error to try to detect proper state (installation/upgrade) at next reconciliation
119+
return nil, "", fmt.Errorf("removed pending release %q version %d metadata", rel.Name, rel.Version)
120+
}
99121
if err != nil {
100122
return nil, "", err
101123
}
@@ -155,7 +177,7 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExte
155177

156178
func (h *Helm) buildHelmChart(bundleFS fs.FS, ext *ocv1.ClusterExtension) (*chart.Chart, error) {
157179
if h.BundleToHelmChartFn == nil {
158-
return nil, errors.New("BundleToHelmChartFn is nil")
180+
return nil, errBuildHelmChartFuncNil
159181
}
160182
watchNamespace, err := GetWatchNamespace(ext)
161183
if err != nil {
@@ -168,24 +190,6 @@ func (h *Helm) getReleaseState(ctx context.Context, cl helmclient.ActionInterfac
168190
logger := log.FromContext(ctx)
169191
currentRelease, err := cl.Get(ext.GetName())
170192

171-
// if a release is pending at this point, that means that a helm action
172-
// (installation/upgrade) we were attempting was likely interrupted in-flight.
173-
// Pending release would leave us in reconciliation error loop because helm
174-
// wouldn't be able to progress automatically from it.
175-
//
176-
// one of the workarounds is to try and remove helm metadata relating to
177-
// that pending release which should 'reset' its state communicated to helm
178-
// and the next reconciliation should be able to successfully pick up from here
179-
// for context see: https://github.com/helm/helm/issues/5595 and https://github.com/helm/helm/issues/7476
180-
// and the discussion in https://github.com/operator-framework/operator-controller/pull/1776
181-
if err == nil && currentRelease.Info.Status.IsPending() {
182-
if _, err = cl.Config().Releases.Delete(currentRelease.Name, currentRelease.Version); err != nil {
183-
return nil, nil, StateError, fmt.Errorf("failed removing interrupted release %q version %d metadata: %w", currentRelease.Name, currentRelease.Version, err)
184-
}
185-
// return error to try to detect proper state (installation/upgrade) at next reconciliation
186-
return nil, nil, StateError, fmt.Errorf("removed interrupted release %q version %d metadata", currentRelease.Name, currentRelease.Version)
187-
}
188-
189193
if errors.Is(err, driver.ErrReleaseNotFound) {
190194
logger.V(4).Info("ClusterExtension dry-run install", "extension", ext.GetName())
191195
desiredRelease, err := cl.Install(ext.GetName(), ext.Spec.Namespace, chrt, values, func(i *action.Install) error {
@@ -205,6 +209,9 @@ func (h *Helm) getReleaseState(ctx context.Context, cl helmclient.ActionInterfac
205209
if err != nil {
206210
return nil, nil, StateError, err
207211
}
212+
if currentRelease.Info.Status.IsPending() {
213+
return currentRelease, nil, StateError, errPendingRelease
214+
}
208215

209216
desiredRelease, err := cl.Upgrade(ext.GetName(), ext.Spec.Namespace, chrt, values, func(upgrade *action.Upgrade) error {
210217
logger.V(4).Info("ClusterExtension dry-run upgrade", "extension", ext.GetName())

Diff for: internal/operator-controller/applier/helm_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ func TestApply_Base(t *testing.T) {
185185
})
186186
}
187187

188-
func TestApply_InterruptedRelease(t *testing.T) {
189-
t.Run("fails removing an interrupted install release", func(t *testing.T) {
188+
func TestApply_PendingRelease(t *testing.T) {
189+
t.Run("fails removing a pending install release", func(t *testing.T) {
190190
testRel := &release.Release{Name: "testrel", Version: 0, Info: &release.Info{Status: release.StatusPendingInstall}}
191191
testStorage := storage.Init(driver.NewMemory())
192192

@@ -198,12 +198,12 @@ func TestApply_InterruptedRelease(t *testing.T) {
198198

199199
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
200200
require.Error(t, err)
201-
require.ErrorContains(t, err, "removing interrupted release")
201+
require.ErrorContains(t, err, "removing pending release")
202202
require.Nil(t, objs)
203203
require.Empty(t, state)
204204
})
205205

206-
t.Run("fails removing an interrupted upgrade release", func(t *testing.T) {
206+
t.Run("fails removing a pending upgrade release", func(t *testing.T) {
207207
testRel := &release.Release{Name: "testrel", Version: 0, Info: &release.Info{Status: release.StatusPendingUpgrade}}
208208
testStorage := storage.Init(driver.NewMemory())
209209

@@ -215,12 +215,12 @@ func TestApply_InterruptedRelease(t *testing.T) {
215215

216216
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
217217
require.Error(t, err)
218-
require.ErrorContains(t, err, "removing interrupted release")
218+
require.ErrorContains(t, err, "removing pending release")
219219
require.Nil(t, objs)
220220
require.Empty(t, state)
221221
})
222222

223-
t.Run("successfully removes an interrupted install release", func(t *testing.T) {
223+
t.Run("successfully removes a pending install release", func(t *testing.T) {
224224
testRel := &release.Release{Name: "testrel", Version: 0, Info: &release.Info{Status: release.StatusPendingInstall}}
225225
testStorage := storage.Init(driver.NewMemory())
226226
err := testStorage.Create(testRel)
@@ -234,12 +234,12 @@ func TestApply_InterruptedRelease(t *testing.T) {
234234

235235
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
236236
require.Error(t, err)
237-
require.ErrorContains(t, err, "removed interrupted release")
237+
require.ErrorContains(t, err, "removed pending release")
238238
require.Nil(t, objs)
239239
require.Empty(t, state)
240240
})
241241

242-
t.Run("successfully removes an interrupted upgrade release", func(t *testing.T) {
242+
t.Run("successfully removes an pending upgrade release", func(t *testing.T) {
243243
testRel := &release.Release{Name: "testrel", Version: 0, Info: &release.Info{Status: release.StatusPendingUpgrade}}
244244
testStorage := storage.Init(driver.NewMemory())
245245
err := testStorage.Create(testRel)
@@ -253,7 +253,7 @@ func TestApply_InterruptedRelease(t *testing.T) {
253253

254254
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
255255
require.Error(t, err)
256-
require.ErrorContains(t, err, "removed interrupted release")
256+
require.ErrorContains(t, err, "removed pending release")
257257
require.Nil(t, objs)
258258
require.Empty(t, state)
259259
})

0 commit comments

Comments
 (0)