diff --git a/pkg/kube/ready.go b/pkg/kube/ready.go index 584b8853a..3759554c3 100644 --- a/pkg/kube/ready.go +++ b/pkg/kube/ready.go @@ -18,6 +18,7 @@ package kube // import "helm.sh/helm/v4/pkg/kube" import ( "context" + "errors" "fmt" appsv1 "k8s.io/api/apps/v1" @@ -41,6 +42,12 @@ import ( // ReadyCheckerOption is a function that configures a ReadyChecker. type ReadyCheckerOption func(*ReadyChecker) +var ErrJobFailed = errors.New("job is failed") + +var TerminalErrors = []error{ + ErrJobFailed, +} + // PausedAsReady returns a ReadyCheckerOption that configures a ReadyChecker // to consider paused resources to be ready. For example a Deployment // with spec.paused equal to true would be considered ready. @@ -241,7 +248,7 @@ func (c *ReadyChecker) jobReady(job *batchv1.Job) (bool, error) { if job.Status.Failed > *job.Spec.BackoffLimit { c.log("Job is failed: %s/%s", job.GetNamespace(), job.GetName()) // If a job is failed, it can't recover, so throw an error - return false, fmt.Errorf("job is failed: %s/%s", job.GetNamespace(), job.GetName()) + return false, fmt.Errorf("%w: %s/%s", ErrJobFailed, job.GetNamespace(), job.GetName()) } if job.Spec.Completions != nil && job.Status.Succeeded < *job.Spec.Completions { c.log("Job is not completed: %s/%s", job.GetNamespace(), job.GetName()) diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 7eb931496..b37715be5 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -85,6 +85,12 @@ func (w *waiter) isRetryableError(err error, resource *resource.Info) bool { return false } w.log("Error received when checking status of resource %s. Error: '%s', Resource details: '%s'", resource.Name, err, resource) + for _, terminalError := range TerminalErrors { + if errors.Is(err, terminalError) { + w.log("Retryable error? %t", false) + return false + } + } if ev, ok := err.(*apierrors.StatusError); ok { statusCode := ev.Status().Code retryable := w.isRetryableHTTPStatusCode(statusCode)