From 179dc0228cca4f11787330fa34a3cd52e1678cb0 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 8 Sep 2025 19:26:40 +0200 Subject: [PATCH] cli/command/image: use stdlib errors Signed-off-by: Sebastiaan van Stijn --- cli/command/image/build.go | 18 ++++++------ cli/command/image/build/context.go | 44 +++++++++++++++--------------- cli/command/image/history.go | 4 +-- cli/command/image/load.go | 7 +++-- cli/command/image/prune.go | 2 +- cli/command/image/push.go | 2 +- cli/command/image/save.go | 7 +++-- cli/command/image/trust.go | 10 +++---- 8 files changed, 48 insertions(+), 46 deletions(-) diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 5357c2d973..b2340168e5 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "os" @@ -26,7 +27,6 @@ import ( "github.com/moby/moby/api/types/container" registrytypes "github.com/moby/moby/api/types/registry" "github.com/moby/moby/client" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -212,7 +212,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) if options.imageIDFile != "" { // Avoid leaving a stale file if we eventually fail if err := os.Remove(options.imageIDFile); err != nil && !os.IsNotExist(err) { - return errors.Wrap(err, "Removing image ID file") + return fmt.Errorf("removing image ID file: %w", err) } } @@ -226,13 +226,13 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) case build.ContextTypeLocal: contextDir, relDockerfile, err = build.GetContextFromLocalDir(options.context, options.dockerfileName) if err != nil { - return errors.Errorf("unable to prepare context: %s", err) + return fmt.Errorf("unable to prepare context: %s", err) } if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) { - // Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx + // Dockerfile is outside build-context; read the Dockerfile and pass it as dockerfileCtx dockerfileCtx, err = os.Open(options.dockerfileName) if err != nil { - return errors.Errorf("unable to open Dockerfile: %v", err) + return fmt.Errorf("unable to open Dockerfile: %w", err) } defer dockerfileCtx.Close() } @@ -240,7 +240,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) var tempDir string tempDir, relDockerfile, err = build.GetContextFromGitURL(options.context, options.dockerfileName) if err != nil { - return errors.Errorf("unable to prepare context: %s", err) + return fmt.Errorf("unable to prepare context: %w", err) } defer func() { _ = os.RemoveAll(tempDir) @@ -252,7 +252,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) _, _ = fmt.Fprintln(dockerCli.Err(), progBuff) } default: - return errors.Errorf("unable to prepare context: path %q not found", options.context) + return fmt.Errorf("unable to prepare context: path %q not found", options.context) } // read from a directory into tar archive @@ -263,7 +263,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) } if err := build.ValidateContextDirectory(contextDir, excludes); err != nil { - return errors.Wrap(err, "error checking context") + return fmt.Errorf("error checking context: %w", err) } // And canonicalize dockerfile name to a platform-independent one @@ -370,7 +370,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) if options.imageIDFile != "" { if imageID == "" { - return errors.Errorf("Server did not provide an image ID. Cannot write %s", options.imageIDFile) + return fmt.Errorf("server did not provide an image ID. Cannot write %s", options.imageIDFile) } if err := os.WriteFile(options.imageIDFile, []byte(imageID), 0o666); err != nil { return err diff --git a/cli/command/image/build/context.go b/cli/command/image/build/context.go index 2f514018a5..fb524c36cc 100644 --- a/cli/command/image/build/context.go +++ b/cli/command/image/build/context.go @@ -6,6 +6,7 @@ import ( "bytes" "crypto/rand" "encoding/hex" + "errors" "fmt" "io" "net/http" @@ -22,7 +23,6 @@ import ( "github.com/moby/moby/api/pkg/progress" "github.com/moby/moby/api/pkg/streamformatter" "github.com/moby/patternmatcher" - "github.com/pkg/errors" ) const ( @@ -49,10 +49,10 @@ func ValidateContextDirectory(srcPath string, excludes []string) error { return filepath.Walk(contextRoot, func(filePath string, f os.FileInfo, err error) error { if err != nil { if os.IsPermission(err) { - return errors.Errorf("can't stat '%s'", filePath) + return fmt.Errorf("can't stat '%s'", filePath) } if os.IsNotExist(err) { - return errors.Errorf("file ('%s') not found or excluded by .dockerignore", filePath) + return fmt.Errorf("file ('%s') not found or excluded by .dockerignore", filePath) } return err } @@ -78,7 +78,7 @@ func ValidateContextDirectory(srcPath string, excludes []string) error { if !f.IsDir() { currentFile, err := os.Open(filePath) if err != nil && os.IsPermission(err) { - return errors.Errorf("no permission to read from '%s'", filePath) + return fmt.Errorf("no permission to read from '%s'", filePath) } currentFile.Close() } @@ -105,7 +105,7 @@ func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, isArchive bool, magic, err := buf.Peek(archiveHeaderSize * 2) if err != nil && err != io.EOF { - return nil, false, errors.Errorf("failed to peek context header from STDIN: %v", err) + return nil, false, fmt.Errorf("failed to peek context header from STDIN: %w", err) } return newReadCloserWrapper(buf, func() error { return input.Close() }), IsArchive(magic), nil @@ -118,7 +118,7 @@ func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) { // err is a named return value, due to the defer call below. dockerfileDir, err = os.MkdirTemp("", "docker-build-tempdockerfile-") if err != nil { - return "", errors.Errorf("unable to create temporary context directory: %v", err) + return "", fmt.Errorf("unable to create temporary context directory: %w", err) } defer func() { if err != nil { @@ -194,11 +194,11 @@ func IsArchive(header []byte) bool { // success. func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error) { if _, err := exec.LookPath("git"); err != nil { - return "", "", errors.Wrapf(err, "unable to find 'git'") + return "", "", fmt.Errorf("unable to find 'git': %w", err) } absContextDir, err := git.Clone(gitURL) if err != nil { - return "", "", errors.Wrapf(err, "unable to 'git clone' to temporary context directory") + return "", "", fmt.Errorf("unable to 'git clone' to temporary context directory: %w", err) } absContextDir, err = ResolveAndValidateContextPath(absContextDir) @@ -207,7 +207,7 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error) } relDockerfile, err := getDockerfileRelPath(absContextDir, dockerfileName) if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) { - return "", "", errors.Errorf("the Dockerfile (%s) must be within the build context", dockerfileName) + return "", "", fmt.Errorf("the Dockerfile (%s) must be within the build context", dockerfileName) } return absContextDir, relDockerfile, err @@ -220,7 +220,7 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error) func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.ReadCloser, string, error) { response, err := getWithStatusError(remoteURL) if err != nil { - return nil, "", errors.Errorf("unable to download remote context %s: %v", remoteURL, err) + return nil, "", fmt.Errorf("unable to download remote context %s: %w", remoteURL, err) } progressOutput := streamformatter.NewProgressOutput(out) @@ -244,9 +244,9 @@ func getWithStatusError(url string) (resp *http.Response, err error) { body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { - return nil, errors.Wrapf(err, "%s: error reading body", msg) + return nil, fmt.Errorf("%s: error reading body: %w", msg, err) } - return nil, errors.Errorf("%s: %s", msg, bytes.TrimSpace(body)) + return nil, fmt.Errorf("%s: %s", msg, bytes.TrimSpace(body)) } // GetContextFromLocalDir uses the given local directory as context for a @@ -264,7 +264,7 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, er // current directory and not the context directory. if dockerfileName != "" && dockerfileName != "-" { if dockerfileName, err = filepath.Abs(dockerfileName); err != nil { - return "", "", errors.Errorf("unable to get absolute path to Dockerfile: %v", err) + return "", "", fmt.Errorf("unable to get absolute path to Dockerfile: %w", err) } } @@ -277,7 +277,7 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, er func ResolveAndValidateContextPath(givenContextDir string) (string, error) { absContextDir, err := filepath.Abs(givenContextDir) if err != nil { - return "", errors.Errorf("unable to get absolute context directory of given context directory %q: %v", givenContextDir, err) + return "", fmt.Errorf("unable to get absolute context directory of given context directory %q: %w", givenContextDir, err) } // The context dir might be a symbolic link, so follow it to the actual @@ -290,17 +290,17 @@ func ResolveAndValidateContextPath(givenContextDir string) (string, error) { if !isUNC(absContextDir) { absContextDir, err = filepath.EvalSymlinks(absContextDir) if err != nil { - return "", errors.Errorf("unable to evaluate symlinks in context path: %v", err) + return "", fmt.Errorf("unable to evaluate symlinks in context path: %w", err) } } stat, err := os.Lstat(absContextDir) if err != nil { - return "", errors.Errorf("unable to stat context directory %q: %v", absContextDir, err) + return "", fmt.Errorf("unable to stat context directory %q: %w", absContextDir, err) } if !stat.IsDir() { - return "", errors.Errorf("context must be a directory: %s", absContextDir) + return "", fmt.Errorf("context must be a directory: %s", absContextDir) } return absContextDir, err } @@ -345,20 +345,20 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error) if !isUNC(absDockerfile) { absDockerfile, err = filepath.EvalSymlinks(absDockerfile) if err != nil { - return "", errors.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err) + return "", fmt.Errorf("unable to evaluate symlinks in Dockerfile path: %w", err) } } if _, err := os.Lstat(absDockerfile); err != nil { if os.IsNotExist(err) { - return "", errors.Errorf("Cannot locate Dockerfile: %q", absDockerfile) + return "", fmt.Errorf("cannot locate Dockerfile: %q", absDockerfile) } - return "", errors.Errorf("unable to stat Dockerfile: %v", err) + return "", fmt.Errorf("unable to stat Dockerfile: %w", err) } relDockerfile, err := filepath.Rel(absContextDir, absDockerfile) if err != nil { - return "", errors.Errorf("unable to get relative Dockerfile path: %v", err) + return "", fmt.Errorf("unable to get relative Dockerfile path: %w", err) } return relDockerfile, nil @@ -443,7 +443,7 @@ func Compress(buildCtx io.ReadCloser) (io.ReadCloser, error) { defer buildCtx.Close() if _, err := io.Copy(compressWriter, buildCtx); err != nil { - pipeWriter.CloseWithError(errors.Wrap(err, "failed to compress context")) + pipeWriter.CloseWithError(fmt.Errorf("failed to compress context: %w", err)) compressWriter.Close() return } diff --git a/cli/command/image/history.go b/cli/command/image/history.go index 4f166144ef..a8a4de8be1 100644 --- a/cli/command/image/history.go +++ b/cli/command/image/history.go @@ -2,6 +2,7 @@ package image import ( "context" + "fmt" "github.com/containerd/platforms" "github.com/docker/cli/cli" @@ -10,7 +11,6 @@ import ( "github.com/docker/cli/cli/command/formatter" flagsHelper "github.com/docker/cli/cli/flags" "github.com/moby/moby/client" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -61,7 +61,7 @@ func runHistory(ctx context.Context, dockerCli command.Cli, opts historyOptions) if opts.platform != "" { p, err := platforms.Parse(opts.platform) if err != nil { - return errors.Wrap(err, "invalid platform") + return fmt.Errorf("invalid platform: %w", err) } options = append(options, client.ImageHistoryWithPlatform(p)) } diff --git a/cli/command/image/load.go b/cli/command/image/load.go index 0abca52fb8..664d1fea56 100644 --- a/cli/command/image/load.go +++ b/cli/command/image/load.go @@ -2,6 +2,8 @@ package image import ( "context" + "errors" + "fmt" "io" "github.com/containerd/platforms" @@ -12,7 +14,6 @@ import ( "github.com/moby/moby/client" "github.com/moby/sys/sequential" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -60,7 +61,7 @@ func runLoad(ctx context.Context, dockerCli command.Cli, opts loadOptions) error // To avoid getting stuck, verify that a tar file is given either in // the input flag or through stdin and if not display an error message and exit. if dockerCli.In().IsTerminal() { - return errors.Errorf("requested load from stdin, but stdin is empty") + return errors.New("requested load from stdin, but stdin is empty") } default: // We use sequential.Open to use sequential file access on Windows, avoiding @@ -82,7 +83,7 @@ func runLoad(ctx context.Context, dockerCli command.Cli, opts loadOptions) error for _, p := range opts.platform { pp, err := platforms.Parse(p) if err != nil { - return errors.Wrap(err, "invalid platform") + return fmt.Errorf("invalid platform: %w", err) } platformList = append(platformList, pp) } diff --git a/cli/command/image/prune.go b/cli/command/image/prune.go index e02b006e9c..2c6d322a3e 100644 --- a/cli/command/image/prune.go +++ b/cli/command/image/prune.go @@ -2,6 +2,7 @@ package image import ( "context" + "errors" "fmt" "strconv" "strings" @@ -12,7 +13,6 @@ import ( "github.com/docker/cli/internal/prompt" "github.com/docker/cli/opts" "github.com/docker/go-units" - "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/cli/command/image/push.go b/cli/command/image/push.go index 6fcea53c3a..4ec8cd76e8 100644 --- a/cli/command/image/push.go +++ b/cli/command/image/push.go @@ -6,6 +6,7 @@ package image import ( "context" "encoding/json" + "errors" "fmt" "io" @@ -23,7 +24,6 @@ import ( "github.com/moby/moby/client" "github.com/morikuni/aec" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/cli/command/image/save.go b/cli/command/image/save.go index 11986feb31..1309b4c955 100644 --- a/cli/command/image/save.go +++ b/cli/command/image/save.go @@ -2,6 +2,8 @@ package image import ( "context" + "errors" + "fmt" "io" "github.com/containerd/platforms" @@ -11,7 +13,6 @@ import ( "github.com/moby/moby/client" "github.com/moby/sys/atomicwriter" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -58,7 +59,7 @@ func runSave(ctx context.Context, dockerCLI command.Cli, opts saveOptions) error for _, p := range opts.platform { pp, err := platforms.Parse(p) if err != nil { - return errors.Wrap(err, "invalid platform") + return fmt.Errorf("invalid platform: %w", err) } platformList = append(platformList, pp) } @@ -75,7 +76,7 @@ func runSave(ctx context.Context, dockerCLI command.Cli, opts saveOptions) error } else { writer, err := atomicwriter.New(opts.output, 0o600) if err != nil { - return errors.Wrap(err, "failed to save image") + return fmt.Errorf("failed to save image: %w", err) } defer writer.Close() output = writer diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go index d613486f73..80afa3252d 100644 --- a/cli/command/image/trust.go +++ b/cli/command/image/trust.go @@ -3,6 +3,7 @@ package image import ( "context" "encoding/hex" + "errors" "fmt" "io" @@ -16,7 +17,6 @@ import ( registrytypes "github.com/moby/moby/api/types/registry" "github.com/moby/moby/client" "github.com/opencontainers/go-digest" - "github.com/pkg/errors" "github.com/sirupsen/logrus" notaryclient "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/tuf/data" @@ -102,7 +102,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) ([]target, error) { notaryRepo, err := newNotaryClient(cli, imgRefAndAuth) if err != nil { - return nil, errors.Wrap(err, "error establishing connection to trust repository") + return nil, fmt.Errorf("error establishing connection to trust repository: %w", err) } ref := imgRefAndAuth.Reference() @@ -128,7 +128,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) refs = append(refs, t) } if len(refs) == 0 { - return nil, trust.NotaryError(ref.Name(), errors.Errorf("No trusted tags for %s", ref.Name())) + return nil, trust.NotaryError(ref.Name(), fmt.Errorf("no trusted tags for %s", ref.Name())) } return refs, nil } @@ -140,7 +140,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) // Only get the tag if it's in the top level targets role or the releases delegation role // ignore it if it's in any other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { - return nil, trust.NotaryError(ref.Name(), errors.Errorf("No trust data for %s", tagged.Tag())) + return nil, trust.NotaryError(ref.Name(), fmt.Errorf("no trust data for %s", tagged.Tag())) } logrus.Debugf("retrieving target for %s role", t.Role) @@ -181,7 +181,7 @@ func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedT notaryRepo, err := newNotaryClient(cli, imgRefAndAuth) if err != nil { - return nil, errors.Wrap(err, "error establishing connection to trust repository") + return nil, fmt.Errorf("error establishing connection to trust repository: %w", err) } t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)