diff --git a/internal/plugin/runtime_subprocess.go b/internal/plugin/runtime_subprocess.go index cd1a0842c..651df47b4 100644 --- a/internal/plugin/runtime_subprocess.go +++ b/internal/plugin/runtime_subprocess.go @@ -18,6 +18,7 @@ package plugin import ( "bytes" "context" + "errors" "fmt" "io" "log/slog" @@ -156,7 +157,9 @@ func (r *SubprocessPluginRuntime) InvokeHook(event string) error { slog.Debug("executing plugin hook command", slog.String("pluginName", r.metadata.Name), slog.String("command", cmd.String())) if err := cmd.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { + var eerr *exec.ExitError + ok := errors.As(err, &eerr) + if ok { os.Stderr.Write(eerr.Stderr) return fmt.Errorf("plugin %s hook for %q exited with error", event, r.metadata.Name) } @@ -170,7 +173,8 @@ func (r *SubprocessPluginRuntime) InvokeHook(event string) error { // then replace the other three with a call to this func func executeCmd(prog *exec.Cmd, pluginName string) error { if err := prog.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { + var eerr *exec.ExitError + if ok := errors.As(err, &eerr); ok { slog.Debug( "plugin execution failed", slog.String("pluginName", pluginName), diff --git a/internal/sympath/walk.go b/internal/sympath/walk.go index 812bb68ce..b54b97ad4 100644 --- a/internal/sympath/walk.go +++ b/internal/sympath/walk.go @@ -21,6 +21,7 @@ limitations under the License. package sympath import ( + "errors" "fmt" "log/slog" "os" @@ -40,7 +41,7 @@ func Walk(root string, walkFn filepath.WalkFunc) error { } else { err = symwalk(root, info, walkFn) } - if err == filepath.SkipDir { + if errors.Is(err, filepath.SkipDir) { return nil } return err @@ -75,7 +76,7 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { if info, err = os.Lstat(resolved); err != nil { return err } - if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir { + if err := symwalk(path, info, walkFn); err != nil && !errors.Is(err, filepath.SkipDir) { return err } return nil @@ -98,13 +99,13 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { filename := filepath.Join(path, name) fileInfo, err := os.Lstat(filename) if err != nil { - if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { + if err := walkFn(filename, fileInfo, err); err != nil && !errors.Is(err, filepath.SkipDir) { return err } } else { err = symwalk(filename, fileInfo, walkFn) if err != nil { - if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir { + if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || !errors.Is(err, filepath.SkipDir) { return err } } diff --git a/pkg/action/install.go b/pkg/action/install.go index 50df13c05..9169b4df4 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -502,7 +502,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource var err error // pre-install hooks if !i.DisableHooks { - if err := i.cfg.execHook(rel, release.HookPreInstall, i.WaitStrategy, i.WaitOptions, i.Timeout, i.ServerSideApply); err != nil { + if err := i.cfg.execHook(rel, release.HookPreInstall, i.WaitStrategy, i.Timeout, i.ServerSideApply); err != nil { return rel, fmt.Errorf("failed pre-install: %w", err) } } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 0f360fe37..770fef9f3 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -454,7 +454,7 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele // pre-upgrade hooks if !u.DisableHooks { - if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.WaitStrategy, u.WaitOptions, u.Timeout, serverSideApply); err != nil { + if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.WaitStrategy, u.Timeout, serverSideApply); err != nil { u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %w", err)) return } diff --git a/pkg/action/validate.go b/pkg/action/validate.go index 94bf4906b..8a127a512 100644 --- a/pkg/action/validate.go +++ b/pkg/action/validate.go @@ -93,7 +93,7 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN // Allow adoption of the resource if it is managed by Helm and is annotated with correct release name and namespace. if err := checkOwnership(existing, releaseName, releaseNamespace); err != nil { - return fmt.Errorf("%s exists and cannot be imported into the current release: %s", resourceString(info), err) + return fmt.Errorf("%s exists and cannot be imported into the current release: %w", resourceString(info), err) } infoCopy := *info @@ -116,13 +116,13 @@ func checkOwnership(obj runtime.Object, releaseName, releaseNamespace string) er var errs []error if err := requireValue(lbls, appManagedByLabel, appManagedByHelm); err != nil { - errs = append(errs, fmt.Errorf("label validation error: %s", err)) + errs = append(errs, fmt.Errorf("label validation error: %w", err)) } if err := requireValue(annos, helmReleaseNameAnnotation, releaseName); err != nil { - errs = append(errs, fmt.Errorf("annotation validation error: %s", err)) + errs = append(errs, fmt.Errorf("annotation validation error: %w", err)) } if err := requireValue(annos, helmReleaseNamespaceAnnotation, releaseNamespace); err != nil { - errs = append(errs, fmt.Errorf("annotation validation error: %s", err)) + errs = append(errs, fmt.Errorf("annotation validation error: %w", err)) } if len(errs) > 0 { @@ -154,7 +154,7 @@ func setMetadataVisitor(releaseName, releaseNamespace string, forceOwnership boo if !forceOwnership { if err := checkOwnership(info.Object, releaseName, releaseNamespace); err != nil { - return fmt.Errorf("%s cannot be owned: %s", resourceString(info), err) + return fmt.Errorf("%s cannot be owned: %w", resourceString(info), err) } } @@ -162,7 +162,7 @@ func setMetadataVisitor(releaseName, releaseNamespace string, forceOwnership boo appManagedByLabel: appManagedByHelm, }); err != nil { return fmt.Errorf( - "%s labels could not be updated: %s", + "%s labels could not be updated: %w", resourceString(info), err, ) } @@ -172,7 +172,7 @@ func setMetadataVisitor(releaseName, releaseNamespace string, forceOwnership boo helmReleaseNamespaceAnnotation: releaseNamespace, }); err != nil { return fmt.Errorf( - "%s annotations could not be updated: %s", + "%s annotations could not be updated: %w", resourceString(info), err, ) } diff --git a/pkg/chart/loader/archive/archive.go b/pkg/chart/loader/archive/archive.go index a35c0152d..837538097 100644 --- a/pkg/chart/loader/archive/archive.go +++ b/pkg/chart/loader/archive/archive.go @@ -171,7 +171,7 @@ func EnsureArchive(name string, raw *os.File) error { // Check the file format to give us a chance to provide the user with more actionable feedback. buffer := make([]byte, 512) _, err := raw.Read(buffer) - if err != nil && err != io.EOF { + if err != nil && !errors.Is(err, io.EOF) { return fmt.Errorf("file '%s' cannot be read: %w", name, err) } diff --git a/pkg/cmd/lint.go b/pkg/cmd/lint.go index ccc53ddd0..1b5c3b212 100644 --- a/pkg/cmd/lint.go +++ b/pkg/cmd/lint.go @@ -59,7 +59,7 @@ func newLintCmd(out io.Writer) *cobra.Command { if kubeVersion != "" { parsedKubeVersion, err := common.ParseKubeVersion(kubeVersion) if err != nil { - return fmt.Errorf("invalid kube version '%s': %s", kubeVersion, err) + return fmt.Errorf("invalid kube version '%s': %w", kubeVersion, err) } client.KubeVersion = parsedKubeVersion } diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index fd4815cc4..021be6215 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -261,7 +261,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { return err } } else { - return fmt.Errorf("unable to retrieve file info for '%s': %v", destPath, err) + return fmt.Errorf("unable to retrieve file info for '%s': %w", destPath, err) } // Prepare tmpPath @@ -281,17 +281,17 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { chartPath := filepath.Join(destPath, dep.Name) ch, err := loader.LoadDir(chartPath) if err != nil { - return fmt.Errorf("unable to load chart '%s': %v", chartPath, err) + return fmt.Errorf("unable to load chart '%s': %w", chartPath, err) } constraint, err := semver.NewConstraint(dep.Version) if err != nil { - return fmt.Errorf("dependency %s has an invalid version/constraint format: %s", dep.Name, err) + return fmt.Errorf("dependency %s has an invalid version/constraint format: %w", dep.Name, err) } v, err := semver.NewVersion(ch.Metadata.Version) if err != nil { - return fmt.Errorf("invalid version %s for dependency %s: %s", dep.Version, dep.Name, err) + return fmt.Errorf("invalid version %s for dependency %s: %w", dep.Version, dep.Name, err) } if !constraint.Check(v) { @@ -765,7 +765,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]* } url, err = repo.FindChartInRepoURL(repoURL, name, m.Getters, repo.WithChartVersion(version), repo.WithClientTLS(certFile, keyFile, caFile)) if err == nil { - return url, username, password, false, false, "", "", "", err + return url, username, password, false, false, "", "", "", nil } err = fmt.Errorf("chart %s not found in %s: %w", name, repoURL, err) return url, username, password, false, false, "", "", "", err diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 6fd2beed8..4c461bddd 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -117,9 +117,11 @@ type renderable struct { basePath string } -const warnStartDelim = "HELM_ERR_START" -const warnEndDelim = "HELM_ERR_END" -const recursionMaxNums = 1000 +const ( + warnStartDelim = "HELM_ERR_START" + warnEndDelim = "HELM_ERR_END" + recursionMaxNums = 1000 +) var warnRegex = regexp.MustCompile(warnStartDelim + `((?s).*)` + warnEndDelim) @@ -410,7 +412,9 @@ func parseTemplateSimpleErrorString(remainder string) (TraceableError, bool) { // Executing form: ": executing \"\" at <>: [ template:...]" // Matches https://cs.opensource.google/go/go/+/refs/tags/go1.23.6:src/text/template/exec.go;l=141 func parseTemplateExecutingAtErrorType(remainder string) (TraceableError, bool) { - if templateName, after, found := strings.Cut(remainder, ": executing "); found { + if idx := strings.Index(remainder, ": executing "); idx != -1 { + templateName := remainder[:idx] + after := remainder[idx+len(": executing "):] if len(after) == 0 || after[0] != '"' { return TraceableError{}, false } @@ -429,10 +433,12 @@ func parseTemplateExecutingAtErrorType(remainder string) (TraceableError, bool) return TraceableError{}, false } afterAt := afterFunc[len(atPrefix):] - locationName, errMsg, found := strings.Cut(afterAt, ">: ") - if !found { + endLoc := strings.Index(afterAt, ">: ") + if endLoc == -1 { return TraceableError{}, false } + locationName := afterAt[:endLoc] + errMsg := afterAt[endLoc+len(">: "):] // trim chained next error starting with space + "template:" if present if cut := strings.Index(errMsg, " template:"); cut != -1 { diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 44f31cdbe..92de331b9 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -246,7 +246,7 @@ func (c *Client) getKubeClient() (kubernetes.Interface, error) { // IsReachable tests connectivity to the cluster. func (c *Client) IsReachable() error { client, err := c.getKubeClient() - if err == genericclioptions.ErrEmptyConfig { + if errors.Is(err, genericclioptions.ErrEmptyConfig) { // re-replace kubernetes ErrEmptyConfig error with a friendly error // moar workarounds for Kubernetes API breaking. return errors.New("kubernetes cluster unreachable") @@ -944,11 +944,13 @@ func (c *Client) Delete(resources ResourceList, policy metav1.DeletionPropagatio func isIncompatibleServerError(err error) bool { // 415: Unsupported media type means we're talking to a server which doesn't // support server-side apply. - if _, ok := err.(*apierrors.StatusError); !ok { + var statusError *apierrors.StatusError + ok := errors.As(err, &statusError) + if !ok { // Non-StatusError means the error isn't because the server is incompatible. return false } - return err.(*apierrors.StatusError).Status().Code == http.StatusUnsupportedMediaType + return statusError.Status().Code == http.StatusUnsupportedMediaType } // getManagedFieldsManager returns the manager string. If one was set it will be returned. @@ -1234,7 +1236,7 @@ func patchResourceServerSide(target *resource.Info, dryRun bool, forceConflicts ) if err != nil { if isIncompatibleServerError(err) { - return fmt.Errorf("server-side apply not available on the server: %v", err) + return fmt.Errorf("server-side apply not available on the server: %w", err) } if apierrors.IsConflict(err) { @@ -1251,7 +1253,7 @@ func patchResourceServerSide(target *resource.Info, dryRun bool, forceConflicts func (c *Client) GetPodList(namespace string, listOptions metav1.ListOptions) (*v1.PodList, error) { podList, err := c.kubeClient.CoreV1().Pods(namespace).List(context.Background(), listOptions) if err != nil { - return nil, fmt.Errorf("failed to get pod list with options: %+v with error: %v", listOptions, err) + return nil, fmt.Errorf("failed to get pod list with options: %+v with error: %w", listOptions, err) } return podList, nil } diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index b5e91d8f3..dcfd1c68c 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -108,8 +108,8 @@ func (hw *legacyWaiter) isRetryableError(err error, resource *resource.Info) boo slog.String("resource", resource.Name), slog.Any("error", err), ) - ev := &apierrors.StatusError{} - if errors.As(err, &ev) { + var ev *apierrors.StatusError + if ok := errors.As(err, &ev); ok { statusCode := ev.Status().Code retryable := hw.isRetryableHTTPStatusCode(statusCode) slog.Debug( diff --git a/pkg/registry/transport.go b/pkg/registry/transport.go index f039a8159..f8a5eea59 100644 --- a/pkg/registry/transport.go +++ b/pkg/registry/transport.go @@ -18,6 +18,7 @@ package registry import ( "bytes" + "errors" "fmt" "io" "log/slog" @@ -137,7 +138,9 @@ func logResponseBody(resp *http.Response) string { Closer: body, } // read the body up to limit+1 to check if the body exceeds the limit - if _, err := io.CopyN(buf, body, payloadSizeLimit+1); err != nil && err != io.EOF { + if _, err := io.CopyN(buf, body, payloadSizeLimit+1); err != nil && + !errors.Is(err, io.EOF) && + !errors.Is(err, io.ErrUnexpectedEOF) { return fmt.Sprintf(" Error reading response body: %v", err) } diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index 21d9f6679..f274be1fb 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -485,7 +485,7 @@ func (s *SQL) Create(key string, rel release.Releaser) error { transaction, err := s.db.Beginx() if err != nil { s.Logger().Debug("failed to start SQL transaction", slog.Any("error", err)) - return fmt.Errorf("error beginning transaction: %v", err) + return fmt.Errorf("error beginning transaction: %w", err) } insertQuery, args, err := s.statementBuilder. @@ -623,7 +623,7 @@ func (s *SQL) Delete(key string) (release.Releaser, error) { transaction, err := s.db.Beginx() if err != nil { s.Logger().Debug("failed to start SQL transaction", slog.Any("error", err)) - return nil, fmt.Errorf("error beginning transaction: %v", err) + return nil, fmt.Errorf("error beginning transaction: %w", err) } selectQuery, args, err := s.statementBuilder. diff --git a/pkg/strvals/literal_parser.go b/pkg/strvals/literal_parser.go index 963558113..2317b3e19 100644 --- a/pkg/strvals/literal_parser.go +++ b/pkg/strvals/literal_parser.go @@ -106,7 +106,7 @@ func (t *literalParser) key(data map[string]any, nestedNameLevel int) (reterr er case lastRune == '=': // found end of key: swallow the '=' and get the value value, err := t.val() - if err == nil && !errors.Is(err, io.EOF) { + if err != nil && !errors.Is(err, io.EOF) { return err } set(data, string(key), string(value)) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index cecaa2453..1e1a4ba3a 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -240,16 +240,16 @@ func (t *parser) key(data map[string]any, nestedNameLevel int) (reterr error) { // End of key. Consume =, Get value. // FIXME: Get value list first vl, e := t.valList() - switch e { - case nil: + switch { + case e == nil: set(data, string(k), vl) return nil - case io.EOF: + case errors.Is(e, io.EOF): set(data, string(k), "") return e - case ErrNotList: + case errors.Is(e, ErrNotList): rs, e := t.val() - if e != nil && e != io.EOF { + if e != nil && !errors.Is(e, io.EOF) { return e } v, e := t.reader(rs) @@ -380,7 +380,7 @@ func (t *parser) listItem(list []any, i, nestedNameLevel int) ([]any, error) { return setIndex(list, i, "") case ErrNotList: rs, e := t.val() - if e != nil && e != io.EOF { + if e != nil && !errors.Is(e, io.EOF) { return list, e } v, e := t.reader(rs) @@ -479,7 +479,7 @@ func (t *parser) valList() ([]any, error) { for { switch rs, last, err := runesUntil(t.sc, stop); { case err != nil: - if err == io.EOF { + if errors.Is(err, io.EOF) { err = errors.New("list must terminate with '}'") } return list, err