Skip to content

Commit 49b53b0

Browse files
authored
Helm: fix misleading "release not found" errors during CR deletion (#2359)
* pkg/helm: handle helm v3 ErrReleaseNotFound correctly * pkg/helm: wait for CR deletion after uninstall and finalizer removal * CHANGELOG.md: update for #2359
1 parent b9f069f commit 49b53b0

File tree

3 files changed

+48
-15
lines changed

3 files changed

+48
-15
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
### Removed
2626

2727
### Bug Fixes
28+
2829
- Fix `operator-sdk build`'s `--image-build-args` to support spaces within quotes like `--label some.name="First Last"`. ([#2312](https://github.com/operator-framework/operator-sdk/pull/2312))
30+
- Fix misleading Helm operator "release not found" errors during CR deletion. ([#2359](https://github.com/operator-framework/operator-sdk/pull/2359))
2931

3032
## v0.13.0
3133

pkg/helm/controller/reconcile.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@ package controller
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
2021
"time"
2122

2223
rpb "helm.sh/helm/v3/pkg/release"
24+
"helm.sh/helm/v3/pkg/storage/driver"
2325
apierrors "k8s.io/apimachinery/pkg/api/errors"
2426
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2527
"k8s.io/apimachinery/pkg/runtime"
2628
"k8s.io/apimachinery/pkg/runtime/schema"
29+
"k8s.io/apimachinery/pkg/util/wait"
2730
"k8s.io/client-go/tools/record"
2831
"sigs.k8s.io/controller-runtime/pkg/client"
2932
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -126,7 +129,7 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
126129
}
127130

128131
uninstalledRelease, err := manager.UninstallRelease(context.TODO())
129-
if err != nil && err != release.ErrNotFound {
132+
if err != nil && !errors.Is(err, driver.ErrReleaseNotFound) {
130133
log.Error(err, "Failed to uninstall release")
131134
status.SetCondition(types.HelmAppCondition{
132135
Type: types.ConditionReleaseFailed,
@@ -139,7 +142,7 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
139142
}
140143
status.RemoveCondition(types.ConditionReleaseFailed)
141144

142-
if err == release.ErrNotFound {
145+
if errors.Is(err, driver.ErrReleaseNotFound) {
143146
log.Info("Release not found, removing finalizer")
144147
} else {
145148
log.Info("Uninstalled release")
@@ -154,6 +157,7 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
154157
status.DeployedRelease = nil
155158
}
156159
if err := r.updateResourceStatus(o, status); err != nil {
160+
log.Info("Failed to update CR status")
157161
return reconcile.Result{}, err
158162
}
159163

@@ -164,10 +168,21 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
164168
}
165169
}
166170
o.SetFinalizers(finalizers)
167-
err = r.updateResource(o)
171+
if err := r.updateResource(o); err != nil {
172+
log.Info("Failed to remove CR uninstall finalizer")
173+
return reconcile.Result{}, err
174+
}
168175

169-
// Need to requeue because finalizer update does not change metadata.generation
170-
return reconcile.Result{Requeue: true}, err
176+
// Since the client is hitting a cache, waiting for the
177+
// deletion here will guarantee that the next reconciliation
178+
// will see that the CR has been deleted and that there's
179+
// nothing left to do.
180+
if err := r.waitForDeletion(o); err != nil {
181+
log.Info("Failed waiting for CR deletion")
182+
return reconcile.Result{}, err
183+
}
184+
185+
return reconcile.Result{}, nil
171186
}
172187

173188
if !manager.IsInstalled() {
@@ -313,6 +328,26 @@ func (r HelmOperatorReconciler) updateResourceStatus(o *unstructured.Unstructure
313328
return r.Client.Status().Update(context.TODO(), o)
314329
}
315330

331+
func (r HelmOperatorReconciler) waitForDeletion(o runtime.Object) error {
332+
key, err := client.ObjectKeyFromObject(o)
333+
if err != nil {
334+
return err
335+
}
336+
337+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
338+
defer cancel()
339+
return wait.PollImmediateUntil(time.Millisecond*10, func() (bool, error) {
340+
err := r.Client.Get(ctx, key, o)
341+
if apierrors.IsNotFound(err) {
342+
return true, nil
343+
}
344+
if err != nil {
345+
return false, err
346+
}
347+
return false, nil
348+
}, ctx.Done())
349+
}
350+
316351
func contains(l []string, s string) bool {
317352
for _, elem := range l {
318353
if elem == s {

pkg/helm/release/manager.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"helm.sh/helm/v3/pkg/kube"
2828
rpb "helm.sh/helm/v3/pkg/release"
2929
"helm.sh/helm/v3/pkg/storage"
30+
"helm.sh/helm/v3/pkg/storage/driver"
3031
apierrors "k8s.io/apimachinery/pkg/api/errors"
3132
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3233
"k8s.io/apimachinery/pkg/runtime"
@@ -38,11 +39,6 @@ import (
3839
"github.com/operator-framework/operator-sdk/pkg/helm/internal/types"
3940
)
4041

41-
var (
42-
// ErrNotFound indicates the release was not found.
43-
ErrNotFound = errors.New("release not found")
44-
)
45-
4642
// Manager manages a Helm release. It can install, update, reconcile,
4743
// and uninstall a release.
4844
type Manager interface {
@@ -109,7 +105,7 @@ func (m *manager) Sync(ctx context.Context) error {
109105

110106
// Load the most recently deployed release from the storage backend.
111107
deployedRelease, err := m.getDeployedRelease()
112-
if err == ErrNotFound {
108+
if errors.Is(err, driver.ErrReleaseNotFound) {
113109
return nil
114110
}
115111
if err != nil {
@@ -138,7 +134,7 @@ func (m manager) getDeployedRelease() (*rpb.Release, error) {
138134
deployedRelease, err := m.storageBackend.Deployed(m.releaseName)
139135
if err != nil {
140136
if strings.Contains(err.Error(), "has no deployed releases") {
141-
return nil, ErrNotFound
137+
return nil, driver.ErrReleaseNotFound
142138
}
143139
return nil, err
144140
}
@@ -299,13 +295,13 @@ func (m manager) UninstallRelease(ctx context.Context) (*rpb.Release, error) {
299295
// Get history of this release
300296
h, err := m.storageBackend.History(m.releaseName)
301297
if err != nil {
302-
return nil, fmt.Errorf("failed to get release history: %s", err)
298+
return nil, fmt.Errorf("failed to get release history: %w", err)
303299
}
304300

305301
// If there is no history, the release has already been uninstalled,
306-
// so return ErrNotFound.
302+
// so return ErrReleaseNotFound.
307303
if len(h) == 0 {
308-
return nil, ErrNotFound
304+
return nil, driver.ErrReleaseNotFound
309305
}
310306

311307
uninstall := action.NewUninstall(m.actionConfig)

0 commit comments

Comments
 (0)