Skip to content

Commit 6e65206

Browse files
azychPer Goncalves da Silva
authored and
Per Goncalves da Silva
committed
move pending release handling out of getReleaseState
1 parent dd325c1 commit 6e65206

File tree

2 files changed

+35
-28
lines changed

2 files changed

+35
-28
lines changed

internal/operator-controller/applier/helm.go

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ const (
3939
maxHelmReleaseHistory = 10
4040
)
4141

42+
var (
43+
errPendingRelease = errors.New("release is in a pending state")
44+
errBundleToHelmChartConverterNil = errors.New("BundleToHelmChartConverter is nil")
45+
)
46+
4247
// Preflight is a check that should be run before making any changes to the cluster
4348
type Preflight interface {
4449
// Install runs checks that should be successful prior
@@ -144,6 +149,23 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExte
144149
}
145150

146151
rel, desiredRel, state, err := h.getReleaseState(ctx, ac, ext, chrt, values, post)
152+
// if a release is pending, that means that a helm action
153+
// (installation/upgrade) we were attempting was likely interrupted in-flight.
154+
// Pending release would leave us in reconciliation error loop because helm
155+
// wouldn't be able to progress automatically from it.
156+
//
157+
// one of the workarounds is to try and remove helm metadata relating to
158+
// that pending release which should 'reset' its state communicated to helm
159+
// and the next reconciliation should be able to successfully pick up from here
160+
// for context see: https://github.com/helm/helm/issues/5595 and https://github.com/helm/helm/issues/7476
161+
// and the discussion in https://github.com/operator-framework/operator-controller/pull/1776
162+
if errors.Is(err, errPendingRelease) {
163+
if _, err := ac.Config().Releases.Delete(rel.Name, rel.Version); err != nil {
164+
return nil, "", fmt.Errorf("failed removing pending release %q version %d metadata: %w", rel.Name, rel.Version, err)
165+
}
166+
// return an error to try to detect proper state (installation/upgrade) at next reconciliation
167+
return nil, "", fmt.Errorf("removed pending release %q version %d metadata", rel.Name, rel.Version)
168+
}
147169
if err != nil {
148170
return nil, "", fmt.Errorf("failed to get release state using server-side dry-run: %w", err)
149171
}
@@ -203,7 +225,7 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExte
203225

204226
func (h *Helm) buildHelmChart(bundleFS fs.FS, ext *ocv1.ClusterExtension) (*chart.Chart, error) {
205227
if h.BundleToHelmChartConverter == nil {
206-
return nil, errors.New("BundleToHelmChartConverter is nil")
228+
return nil, errBundleToHelmChartConverterNil
207229
}
208230
watchNamespace, err := GetWatchNamespace(ext)
209231
if err != nil {
@@ -244,24 +266,6 @@ func (h *Helm) getReleaseState(ctx context.Context, cl helmclient.ActionInterfac
244266
logger := log.FromContext(ctx)
245267
currentRelease, err := cl.Get(ext.GetName())
246268

247-
// if a release is pending at this point, that means that a helm action
248-
// (installation/upgrade) we were attempting was likely interrupted in-flight.
249-
// Pending release would leave us in reconciliation error loop because helm
250-
// wouldn't be able to progress automatically from it.
251-
//
252-
// one of the workarounds is to try and remove helm metadata relating to
253-
// that pending release which should 'reset' its state communicated to helm
254-
// and the next reconciliation should be able to successfully pick up from here
255-
// for context see: https://github.com/helm/helm/issues/5595 and https://github.com/helm/helm/issues/7476
256-
// and the discussion in https://github.com/operator-framework/operator-controller/pull/1776
257-
if err == nil && currentRelease.Info.Status.IsPending() {
258-
if _, err = cl.Config().Releases.Delete(currentRelease.Name, currentRelease.Version); err != nil {
259-
return nil, nil, StateError, fmt.Errorf("failed removing interrupted release %q version %d metadata: %w", currentRelease.Name, currentRelease.Version, err)
260-
}
261-
// return error to try to detect proper state (installation/upgrade) at next reconciliation
262-
return nil, nil, StateError, fmt.Errorf("removed interrupted release %q version %d metadata", currentRelease.Name, currentRelease.Version)
263-
}
264-
265269
if errors.Is(err, driver.ErrReleaseNotFound) {
266270
logger.V(4).Info("ClusterExtension dry-run install", "extension", ext.GetName())
267271
desiredRelease, err := cl.Install(ext.GetName(), ext.Spec.Namespace, chrt, values, func(i *action.Install) error {
@@ -277,6 +281,9 @@ func (h *Helm) getReleaseState(ctx context.Context, cl helmclient.ActionInterfac
277281
if err != nil {
278282
return nil, nil, StateError, err
279283
}
284+
if currentRelease.Info.Status.IsPending() {
285+
return currentRelease, nil, StateError, errPendingRelease
286+
}
280287

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

internal/operator-controller/applier/helm_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ func TestApply_Base(t *testing.T) {
229229
})
230230
}
231231

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

@@ -242,12 +242,12 @@ func TestApply_InterruptedRelease(t *testing.T) {
242242

243243
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
244244
require.Error(t, err)
245-
require.ErrorContains(t, err, "removing interrupted release")
245+
require.ErrorContains(t, err, "removing pending release")
246246
require.Nil(t, objs)
247247
require.Empty(t, state)
248248
})
249249

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

@@ -259,12 +259,12 @@ func TestApply_InterruptedRelease(t *testing.T) {
259259

260260
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
261261
require.Error(t, err)
262-
require.ErrorContains(t, err, "removing interrupted release")
262+
require.ErrorContains(t, err, "removing pending release")
263263
require.Nil(t, objs)
264264
require.Empty(t, state)
265265
})
266266

267-
t.Run("successfully removes an interrupted install release", func(t *testing.T) {
267+
t.Run("successfully removes a pending install release", func(t *testing.T) {
268268
testRel := &release.Release{Name: "testrel", Version: 0, Info: &release.Info{Status: release.StatusPendingInstall}}
269269
testStorage := storage.Init(driver.NewMemory())
270270
err := testStorage.Create(testRel)
@@ -278,12 +278,12 @@ func TestApply_InterruptedRelease(t *testing.T) {
278278

279279
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
280280
require.Error(t, err)
281-
require.ErrorContains(t, err, "removed interrupted release")
281+
require.ErrorContains(t, err, "removed pending release")
282282
require.Nil(t, objs)
283283
require.Empty(t, state)
284284
})
285285

286-
t.Run("successfully removes an interrupted upgrade release", func(t *testing.T) {
286+
t.Run("successfully removes an pending upgrade release", func(t *testing.T) {
287287
testRel := &release.Release{Name: "testrel", Version: 0, Info: &release.Info{Status: release.StatusPendingUpgrade}}
288288
testStorage := storage.Init(driver.NewMemory())
289289
err := testStorage.Create(testRel)
@@ -297,7 +297,7 @@ func TestApply_InterruptedRelease(t *testing.T) {
297297

298298
objs, state, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels)
299299
require.Error(t, err)
300-
require.ErrorContains(t, err, "removed interrupted release")
300+
require.ErrorContains(t, err, "removed pending release")
301301
require.Nil(t, objs)
302302
require.Empty(t, state)
303303
})

0 commit comments

Comments
 (0)