Skip to content
This repository was archived by the owner on Jun 13, 2023. It is now read-only.

Commit ddf6922

Browse files
authored
Fix uninstallation bugs in decliarative library (#196)
* add helm client not found error check to all place where KubeClient().Delete get called remove check namespace in checkWaitForResources return only error but not (bool, error) to simplify return condition handling * refactoring uninstallResources * bring back ErrUninstallInconsistent
1 parent a986799 commit ddf6922

File tree

4 files changed

+81
-81
lines changed

4 files changed

+81
-81
lines changed

pkg/manifest/helm_processor.go

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ func (h *helm) Install(stringifedManifest string, info *types.InstallInfo, trans
128128
"resource", client.ObjectKeyFromObject(info.BaseResource).String())
129129

130130
// verify resources
131-
if ready, err := h.verifyResources(info.Ctx, resourceLists,
132-
info.CheckReadyStates, types.OperationCreate); !ready || err != nil {
133-
return ready, err
131+
if err := h.checkTargetResources(info.Ctx, resourceLists.Target,
132+
types.OperationCreate, info.CheckReadyStates); err != nil {
133+
return false, err
134134
}
135135

136136
// update helm repositories
@@ -168,8 +168,8 @@ func (h *helm) Uninstall(stringifedManifest string, info *types.InstallInfo, tra
168168
}
169169

170170
// uninstall resources
171-
_, err = h.uninstallResources(resourceLists)
172-
if IgnoreHelmClientNotFound(err) != nil {
171+
err = h.uninstallResources(resourceLists.Installed)
172+
if err != nil {
173173
return false, err
174174
}
175175

@@ -187,10 +187,10 @@ func (h *helm) Uninstall(stringifedManifest string, info *types.InstallInfo, tra
187187
"resource", client.ObjectKeyFromObject(info.BaseResource).String())
188188

189189
// verify resource uninstallation
190-
if ready, err := h.verifyResources(
191-
info.Ctx, resourceLists,
192-
info.CheckReadyStates, types.OperationDelete); !ready || err != nil {
193-
return ready, err
190+
if err := h.checkTargetResources(
191+
info.Ctx, resourceLists.Target, types.OperationDelete,
192+
info.CheckReadyStates); err != nil {
193+
return false, err
194194
}
195195

196196
// include CRDs means that the Chart will include the CRDs during rendering, if this is set to false,
@@ -219,12 +219,12 @@ func (h *helm) Uninstall(stringifedManifest string, info *types.InstallInfo, tra
219219
return true, nil
220220
}
221221

222-
func IgnoreHelmClientNotFound(err error) error {
222+
func isHelmClientNotFound(err error) bool {
223223
// Refactoring this error check after this PR get merged and released https://github.com/helm/helm/pull/11591
224224
if err != nil && strings.Contains(err.Error(), "object not found, skipping delete") {
225-
return nil
225+
return true
226226
}
227-
return err
227+
return false
228228
}
229229

230230
// uninstallChartCRDs uses a types.InstallInfo to lookup a chart and then tries to remove all CRDs found within it
@@ -244,23 +244,21 @@ func (h *helm) uninstallChartCRDs(info *types.InstallInfo) error {
244244
if err != nil {
245245
return err
246246
}
247-
resList, err := h.getTargetResources(info.Ctx, crds.String(), nil, nil, false)
247+
resourceList, err := h.getTargetResources(info.Ctx, crds.String(), nil, nil, false)
248+
if resourceList == nil {
249+
return nil
250+
}
248251
if err != nil {
249252
return err
250253
}
251-
252-
_, deleteErrs := h.clients.KubeClient().Delete(resList)
253-
criticalDeleteErrs := make([]error, 0, len(deleteErrs))
254-
for i := range deleteErrs {
255-
if deleteErrs[i] != nil && !apierrors.IsNotFound(deleteErrs[i]) {
256-
criticalDeleteErrs = append(criticalDeleteErrs, deleteErrs[i])
254+
_, deleteErrs := h.clients.KubeClient().Delete(resourceList)
255+
if len(deleteErrs) > 0 {
256+
filteredErrs := filterNotFoundError(deleteErrs)
257+
if len(filteredErrs) > 0 {
258+
return types.NewMultiError(filteredErrs)
257259
}
258260
}
259261

260-
if len(criticalDeleteErrs) > 0 {
261-
return types.NewMultiError(criticalDeleteErrs)
262-
}
263-
264262
return nil
265263
}
266264

@@ -299,21 +297,21 @@ func (h *helm) IsConsistent(stringifedManifest string, info *types.InstallInfo,
299297
}
300298

301299
// verify resources
302-
ready, err := h.verifyResources(info.Ctx, resourceLists, info.CheckReadyStates, types.OperationCreate)
300+
if err := h.checkTargetResources(info.Ctx,
301+
resourceLists.Target,
302+
types.OperationCreate,
303+
info.CheckReadyStates); err != nil {
304+
return false, err
305+
}
303306

304307
h.logger.V(util.DebugLogLevel).Info("consistency check finished",
305-
"consistent", ready,
306308
"create count", len(result.Created),
307309
"update count", len(result.Updated),
308310
"chart", info.ChartName,
309311
"release", info.ReleaseName,
310312
"resource", client.ObjectKeyFromObject(info.BaseResource).String(),
311313
"time", time.Since(startConsistencyCheck).String())
312314

313-
if !ready || err != nil {
314-
return ready, err
315-
}
316-
317315
// update helm repositories
318316
if info.UpdateRepositories {
319317
if err = h.updateRepos(info.Ctx); err != nil {
@@ -324,13 +322,6 @@ func (h *helm) IsConsistent(stringifedManifest string, info *types.InstallInfo,
324322
return true, nil
325323
}
326324

327-
func (h *helm) verifyResources(ctx context.Context, resourceLists types.ResourceLists, verifyReadyStates bool,
328-
operationType types.HelmOperation,
329-
) (bool, error) {
330-
return h.checkWaitForResources(ctx, resourceLists.GetWaitForResources(), operationType,
331-
verifyReadyStates)
332-
}
333-
334325
func (h *helm) installResources(resourceLists types.ResourceLists, force bool) (*kube.Result, error) {
335326
// create namespace resource first!
336327
if len(resourceLists.Namespace) > 0 {
@@ -348,25 +339,35 @@ func (h *helm) installResources(resourceLists types.ResourceLists, force bool) (
348339
return h.clients.KubeClient().Update(resourceLists.Installed, resourceLists.Target, force)
349340
}
350341

351-
func (h *helm) uninstallResources(resourceLists types.ResourceLists) (*kube.Result, error) {
352-
var response *kube.Result
353-
var delErrors []error
354-
if resourceLists.Installed != nil {
355-
response, delErrors = h.clients.KubeClient().Delete(resourceLists.Installed)
356-
if len(delErrors) > 0 {
357-
var wrappedError error
358-
for _, err := range delErrors {
359-
wrappedError = fmt.Errorf("%w", err)
342+
func (h *helm) uninstallResources(installedResources kube.ResourceList) error {
343+
var deleteErrs []error
344+
if installedResources != nil {
345+
_, deleteErrs = h.clients.KubeClient().Delete(installedResources)
346+
if len(deleteErrs) > 0 {
347+
filteredErrs := filterNotFoundError(deleteErrs)
348+
if len(filteredErrs) > 0 {
349+
return types.NewMultiError(filteredErrs)
360350
}
361-
return nil, wrappedError
362351
}
363352
}
364-
return response, nil
353+
return nil
365354
}
366355

367-
func (h *helm) checkWaitForResources(ctx context.Context, targetResources kube.ResourceList,
368-
operation types.HelmOperation, verifyWithoutTimeout bool,
369-
) (bool, error) {
356+
func filterNotFoundError(delErrors []error) []error {
357+
wrappedErrors := make([]error, 0)
358+
for _, err := range delErrors {
359+
if isHelmClientNotFound(err) || apierrors.IsNotFound(err) {
360+
continue
361+
}
362+
wrappedErrors = append(wrappedErrors, err)
363+
}
364+
return wrappedErrors
365+
}
366+
367+
func (h *helm) checkTargetResources(ctx context.Context,
368+
targetResources kube.ResourceList,
369+
operation types.HelmOperation,
370+
verifyWithoutTimeout bool) error {
370371
// verifyWithoutTimeout flag checks native resources are in their respective ready states
371372
// without a timeout defined
372373
if verifyWithoutTimeout {
@@ -375,7 +376,7 @@ func (h *helm) checkWaitForResources(ctx context.Context, targetResources kube.R
375376
}
376377
clientSet, err := h.clients.KubernetesClientSet()
377378
if err != nil {
378-
return false, err
379+
return err
379380
}
380381
readyChecker := kube.NewReadyChecker(clientSet,
381382
func(format string, args ...interface{}) {
@@ -390,20 +391,20 @@ func (h *helm) checkWaitForResources(ctx context.Context, targetResources kube.R
390391
// if Wait or WaitForJobs is enabled, resources are verified to be in ready state with a timeout
391392
if !h.clients.Install().Wait || h.clients.Install().Timeout == 0 {
392393
// return here as ready, since waiting flags were not set
393-
return true, nil
394+
return nil
394395
}
395396

396397
if operation == types.OperationDelete {
397398
// WaitForDelete reports an error if resources are not deleted in the specified timeout
398-
return true, h.clients.KubeClient().WaitForDelete(targetResources, h.clients.Install().Timeout)
399+
return h.clients.KubeClient().WaitForDelete(targetResources, h.clients.Install().Timeout)
399400
}
400401

401402
if h.clients.Install().WaitForJobs {
402403
// WaitWithJobs reports an error if resources are not deleted in the specified timeout
403-
return true, h.clients.KubeClient().WaitWithJobs(targetResources, h.clients.Install().Timeout)
404+
return h.clients.KubeClient().WaitWithJobs(targetResources, h.clients.Install().Timeout)
404405
}
405406
// Wait reports an error if resources are not deleted in the specified timeout
406-
return true, h.clients.KubeClient().Wait(targetResources, h.clients.Install().Timeout)
407+
return h.clients.KubeClient().Wait(targetResources, h.clients.Install().Timeout)
407408
}
408409

409410
func (h *helm) updateRepos(ctx context.Context) error {

pkg/manifest/helm_processor_util.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package manifest
33
import (
44
"context"
55

6+
"errors"
67
"helm.sh/helm/v3/pkg/kube"
78
apierrors "k8s.io/apimachinery/pkg/api/errors"
89
"k8s.io/apimachinery/pkg/api/meta"
@@ -11,20 +12,23 @@ import (
1112
"k8s.io/cli-runtime/pkg/resource"
1213
)
1314

14-
func checkResourcesDeleted(targetResources kube.ResourceList) (bool, error) {
15-
resourcesDeleted := true
16-
err := targetResources.Visit(func(info *resource.Info, err error) error {
15+
var ErrResourceNotReady = errors.New("resource not ready")
16+
var ErrResourceNotDeleted = errors.New("resource not deleted")
17+
18+
func checkResourcesDeleted(targetResources kube.ResourceList) error {
19+
return targetResources.Visit(func(info *resource.Info, err error) error {
1720
if err != nil {
1821
return err
1922
}
2023
err = info.Get()
21-
if err == nil || !apierrors.IsNotFound(err) {
22-
resourcesDeleted = false
24+
if err == nil {
25+
return ErrResourceNotDeleted
26+
}
27+
if !apierrors.IsNotFound(err) {
2328
return err
2429
}
2530
return nil
2631
})
27-
return resourcesDeleted, err
2832
}
2933

3034
func setNamespaceIfNotPresent(targetNamespace string, resourceInfo *resource.Info,
@@ -60,20 +64,18 @@ func overrideNamespace(resourceList kube.ResourceList, targetNamespace string) e
6064
})
6165
}
6266

63-
func checkReady(ctx context.Context, resourceList kube.ResourceList,
64-
readyChecker kube.ReadyChecker,
65-
) (bool, error) {
66-
resourcesReady := true
67-
err := resourceList.Visit(func(info *resource.Info, err error) error {
67+
func checkReady(ctx context.Context, resourceList kube.ResourceList, readyChecker kube.ReadyChecker) error {
68+
return resourceList.Visit(func(info *resource.Info, err error) error {
6869
if err != nil {
6970
return err
7071
}
71-
72-
if ready, err := readyChecker.IsReady(ctx, info); !ready || err != nil {
73-
resourcesReady = ready
72+
ready, err := readyChecker.IsReady(ctx, info)
73+
if !ready {
74+
return ErrResourceNotReady
75+
}
76+
if err != nil {
7477
return err
7578
}
7679
return nil
7780
})
78-
return resourcesReady, err
7981
}

pkg/manifest/operations.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ var (
4040
ErrCRDsNotRemoved = errors.New("CRDs not completely removed")
4141
ErrUninstallInconsistent = errors.New("uninstallation inconsistent")
4242
)
43+
4344
// InstallChart installs the resources based on types.InstallInfo and an appropriate rendering mechanism.
4445
func InstallChart(options OperationOptions) (bool, error) {
4546
ops, err := NewOperations(options)
@@ -296,18 +297,18 @@ func (o *Operations) uninstall() (bool, error) {
296297
// uninstall resources
297298
consistent, err := o.renderSrc.Uninstall(parsedFile.GetContent(),
298299
o.installInfo, o.resourceTransforms, o.postRuns)
299-
if !consistent {
300-
return false, ErrUninstallInconsistent
301-
}
302300
if !UninstallSuccess(err) {
303301
return false, err
304302
}
303+
if !consistent {
304+
return false, ErrUninstallInconsistent
305+
}
305306

306307
// delete crds last - if not present ignore!
307-
crdDeleted := resource.RemoveCRDs(o.installInfo.Ctx, o.installInfo.Crds, o.client)
308-
if !crdDeleted {
309-
return false, ErrCRDsNotRemoved
310-
}
308+
crdDeleted := resource.RemoveCRDs(o.installInfo.Ctx, o.installInfo.Crds, o.client)
309+
if !crdDeleted {
310+
return false, ErrCRDsNotRemoved
311+
}
311312

312313
// custom states check
313314
if o.installInfo.CheckFn != nil {

pkg/types/installs.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,6 @@ type ResourceLists struct {
153153
Namespace kube.ResourceList
154154
}
155155

156-
func (r *ResourceLists) GetWaitForResources() kube.ResourceList {
157-
return append(r.Target, r.Namespace...)
158-
}
159-
160156
// InstallInfo represents deployment information artifacts to be processed.
161157
type InstallInfo struct {
162158
// ChartInfo represents chart information to be processed

0 commit comments

Comments
 (0)