From d0ec8fa5cf044c70fbfb35334cfe29bd83c03b45 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 12 Apr 2023 21:10:13 +0200 Subject: [PATCH 1/9] cli/command: ConfigureAuth: fix terminal state not being restored on error ConfigureAuth used the readInput() utility to read the username and password. However, this utility did not return errors it encountered, but instead did an os.Exit(1). A result of this was that the terminal was not restored if an error happened. When reading the password, the terminal is configured to disable echo (i.e. characters are not printed), and failing to restore the previous state means that the terminal is now "non-functional". This patch: - changes readInput() to return errors it encounters - uses a defer() to restore terminal state Signed-off-by: Sebastiaan van Stijn --- cli/command/registry.go | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/cli/command/registry.go b/cli/command/registry.go index 22c5cff0c2..621a6c7fc5 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -113,7 +113,11 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes fmt.Fprintln(cli.Out(), "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.") } promptWithDefault(cli.Out(), "Username", authconfig.Username) - flUser = readInput(cli.In(), cli.Out()) + var err error + flUser, err = readInput(cli.In()) + if err != nil { + return err + } flUser = strings.TrimSpace(flUser) if flUser == "" { flUser = authconfig.Username @@ -128,12 +132,15 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes return err } fmt.Fprintf(cli.Out(), "Password: ") - term.DisableEcho(cli.In().FD(), oldState) - - flPassword = readInput(cli.In(), cli.Out()) + _ = term.DisableEcho(cli.In().FD(), oldState) + defer func() { + _ = term.RestoreTerminal(cli.In().FD(), oldState) + }() + flPassword, err = readInput(cli.In()) + if err != nil { + return err + } fmt.Fprint(cli.Out(), "\n") - - term.RestoreTerminal(cli.In().FD(), oldState) if flPassword == "" { return errors.Errorf("Error: Password Required") } @@ -145,14 +152,14 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes return nil } -func readInput(in io.Reader, out io.Writer) string { - reader := bufio.NewReader(in) - line, _, err := reader.ReadLine() +// readInput reads, and returns user input from in. It tries to return a +// single line, not including the end-of-line bytes. +func readInput(in io.Reader) (string, error) { + line, _, err := bufio.NewReader(in).ReadLine() if err != nil { - fmt.Fprintln(out, err.Error()) - os.Exit(1) + return "", errors.Wrap(err, "error while reading input") } - return string(line) + return string(line), nil } func promptWithDefault(out io.Writer, prompt string, configDefault string) { From 68d791e56d6fb2c4b430041749c9be33933e12e4 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 12 Apr 2023 21:16:03 +0200 Subject: [PATCH 2/9] cli/command: ConfigureAuth: trim whitespace both for username and password changes readInput() to trim whitespace. The existing code tried to be conservative and only trimmed whitespace for username (not for password). Passwords with leading/trailing whitespace would be _very_ unlikely, and trimming whitespace is generally accepted. Signed-off-by: Sebastiaan van Stijn --- cli/command/registry.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/command/registry.go b/cli/command/registry.go index 621a6c7fc5..243e26c928 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -118,7 +118,6 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes if err != nil { return err } - flUser = strings.TrimSpace(flUser) if flUser == "" { flUser = authconfig.Username } @@ -153,13 +152,14 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes } // readInput reads, and returns user input from in. It tries to return a -// single line, not including the end-of-line bytes. +// single line, not including the end-of-line bytes, and trims leading +// and trailing whitespace. func readInput(in io.Reader) (string, error) { line, _, err := bufio.NewReader(in).ReadLine() if err != nil { return "", errors.Wrap(err, "error while reading input") } - return string(line), nil + return strings.TrimSpace(string(line)), nil } func promptWithDefault(out io.Writer, prompt string, configDefault string) { From 5e76d41bf60d8c991184410cb83f16e864fd470f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 12 Apr 2023 10:29:27 +0200 Subject: [PATCH 3/9] cli/command: ConfigureAuth: fix links to related tickets Also adds a TODO to verify if this special handling is still needed. Signed-off-by: Sebastiaan van Stijn --- cli/command/registry.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cli/command/registry.go b/cli/command/registry.go index 243e26c928..2248f6515d 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -89,7 +89,14 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is // ConfigureAuth handles prompting of user's username and password if needed func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes.AuthConfig, isDefaultRegistry bool) error { - // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 + // On Windows, force the use of the regular OS stdin stream. + // + // See: + // - https://github.com/moby/moby/issues/14336 + // - https://github.com/moby/moby/issues/14210 + // - https://github.com/moby/moby/pull/17738 + // + // TODO(thaJeztah): we need to confirm if this special handling is still needed, as we may not be doing this in other places. if runtime.GOOS == "windows" { cli.SetIn(streams.NewIn(os.Stdin)) } From 534bfc2301e386499239d38a95ec8adc7c22ef97 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 11 Apr 2023 17:55:00 +0200 Subject: [PATCH 4/9] cli/command/registry: remove intermediate var that collided This also simplifies the code a bit. Signed-off-by: Sebastiaan van Stijn --- cli/command/registry/search.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cli/command/registry/search.go b/cli/command/registry/search.go index 9efc1f3d5a..8c27b9c207 100644 --- a/cli/command/registry/search.go +++ b/cli/command/registry/search.go @@ -63,16 +63,12 @@ func runSearch(dockerCli command.Cli, options searchOptions) error { return err } - searchOptions := types.ImageSearchOptions{ + results, err := dockerCli.Client().ImageSearch(ctx, options.term, types.ImageSearchOptions{ RegistryAuth: encodedAuth, PrivilegeFunc: requestPrivilege, Filters: options.filter.Value(), Limit: options.limit, - } - - clnt := dockerCli.Client() - - results, err := clnt.ImageSearch(ctx, options.term, searchOptions) + }) if err != nil { return err } From 372bb56ade80c453504d9ee1c628c06f74f61306 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 11 Apr 2023 18:16:30 +0200 Subject: [PATCH 5/9] cli/command: replace EncodeAuthToBase64 for registry.EncodeAuthConfig Replace uses of this function in favor of the implementation in the API types, so that we have a single, canonical implementation. Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 3 ++- cli/command/image/push.go | 3 ++- cli/command/image/trust.go | 2 +- cli/command/plugin/install.go | 4 ++-- cli/command/plugin/push.go | 4 ++-- cli/command/registry.go | 21 ++++++++++----------- cli/command/registry/search.go | 7 +++---- cli/command/trust/sign.go | 3 ++- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index c7ffcb562d..1a48c69f15 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -16,6 +16,7 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" + registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/versions" apiclient "github.com/docker/docker/client" "github.com/docker/docker/pkg/jsonmessage" @@ -125,7 +126,7 @@ func pullImage(ctx context.Context, dockerCli command.Cli, image string, platfor } authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) - encodedAuth, err := command.EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return err } diff --git a/cli/command/image/push.go b/cli/command/image/push.go index e870432f08..f60a92c33c 100644 --- a/cli/command/image/push.go +++ b/cli/command/image/push.go @@ -11,6 +11,7 @@ import ( "github.com/docker/cli/cli/streams" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/registry" "github.com/pkg/errors" @@ -76,7 +77,7 @@ func RunPush(dockerCli command.Cli, opts pushOptions) error { // Resolve the Auth config relevant for this server authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) - encodedAuth, err := command.EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return err } diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go index 9e06c4604b..98c325b061 100644 --- a/cli/command/image/trust.go +++ b/cli/command/image/trust.go @@ -264,7 +264,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts PullOptions) error { ref := reference.FamiliarString(imgRefAndAuth.Reference()) - encodedAuth, err := command.EncodeAuthToBase64(*imgRefAndAuth.AuthConfig()) + encodedAuth, err := registrytypes.EncodeAuthConfig(*imgRefAndAuth.AuthConfig()) if err != nil { return err } diff --git a/cli/command/plugin/install.go b/cli/command/plugin/install.go index d178f19cfe..2b6d3040cf 100644 --- a/cli/command/plugin/install.go +++ b/cli/command/plugin/install.go @@ -10,6 +10,7 @@ import ( "github.com/docker/cli/cli/command/image" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/registry" "github.com/pkg/errors" @@ -86,8 +87,7 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti } authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) - - encodedAuth, err := command.EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return types.PluginInstallOptions{}, err } diff --git a/cli/command/plugin/push.go b/cli/command/plugin/push.go index 8b9dc09ca4..bb03bcfc11 100644 --- a/cli/command/plugin/push.go +++ b/cli/command/plugin/push.go @@ -7,6 +7,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/image" "github.com/docker/distribution/reference" + registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/registry" "github.com/pkg/errors" @@ -55,8 +56,7 @@ func runPush(dockerCli command.Cli, opts pushOptions) error { return err } authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) - - encodedAuth, err := command.EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return err } diff --git a/cli/command/registry.go b/cli/command/registry.go index 2248f6515d..41cc951b93 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -3,8 +3,6 @@ package command import ( "bufio" "context" - "encoding/base64" - "encoding/json" "fmt" "io" "os" @@ -21,13 +19,9 @@ import ( "github.com/pkg/errors" ) -// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload +// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload. func EncodeAuthToBase64(authConfig registrytypes.AuthConfig) (string, error) { - buf, err := json.Marshal(authConfig) - if err != nil { - return "", err - } - return base64.URLEncoding.EncodeToString(buf), nil + return registrytypes.EncodeAuthConfig(authConfig) } // RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info @@ -45,7 +39,7 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf if err != nil { return "", err } - return EncodeAuthToBase64(authConfig) + return registrytypes.EncodeAuthConfig(authConfig) } } @@ -177,14 +171,19 @@ func promptWithDefault(out io.Writer, prompt string, configDefault string) { } } -// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image +// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete +// image. The auth configuration is serialized as a base64url encoded RFC4648, +// section 5) JSON string for sending through the X-Registry-Auth header. +// +// For details on base64url encoding, see: +// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5 func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (string, error) { // Retrieve encoded auth token from the image reference authConfig, err := resolveAuthConfigFromImage(ctx, cli, image) if err != nil { return "", err } - encodedAuth, err := EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return "", err } diff --git a/cli/command/registry/search.go b/cli/command/registry/search.go index 8c27b9c207..6ba00b5236 100644 --- a/cli/command/registry/search.go +++ b/cli/command/registry/search.go @@ -8,6 +8,7 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/opts" "github.com/docker/docker/api/types" + registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/registry" "github.com/spf13/cobra" ) @@ -54,15 +55,13 @@ func runSearch(dockerCli command.Cli, options searchOptions) error { } ctx := context.Background() - authConfig := command.ResolveAuthConfig(ctx, dockerCli, indexInfo) - requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search") - - encodedAuth, err := command.EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return err } + requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search") results, err := dockerCli.Client().ImageSearch(ctx, options.term, types.ImageSearchOptions{ RegistryAuth: encodedAuth, PrivilegeFunc: requestPrivilege, diff --git a/cli/command/trust/sign.go b/cli/command/trust/sign.go index e5d7f6b7cd..5f8f3c1014 100644 --- a/cli/command/trust/sign.go +++ b/cli/command/trust/sign.go @@ -13,6 +13,7 @@ import ( "github.com/docker/cli/cli/command/image" "github.com/docker/cli/cli/trust" "github.com/docker/docker/api/types" + registrytypes "github.com/docker/docker/api/types/registry" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/theupdateframework/notary/client" @@ -93,7 +94,7 @@ func runSignImage(cli command.Cli, options signOptions) error { fmt.Fprintf(cli.Err(), "Signing and pushing trust data for local image %s, may overwrite remote trust data\n", imageName) authConfig := command.ResolveAuthConfig(ctx, cli, imgRefAndAuth.RepoInfo().Index) - encodedAuth, err := command.EncodeAuthToBase64(authConfig) + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { return err } From e0b47cc2cc1570750421cc5d0fefa884b9dbe291 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 11 Apr 2023 20:19:17 +0200 Subject: [PATCH 6/9] cli/command/image: imagePullPrivileged: remove intermediate variables Signed-off-by: Sebastiaan van Stijn --- cli/command/image/trust.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go index 98c325b061..3da1e8032e 100644 --- a/cli/command/image/trust.go +++ b/cli/command/image/trust.go @@ -262,20 +262,17 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) // imagePullPrivileged pulls the image and displays it to the output func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts PullOptions) error { - ref := reference.FamiliarString(imgRefAndAuth.Reference()) - encodedAuth, err := registrytypes.EncodeAuthConfig(*imgRefAndAuth.AuthConfig()) if err != nil { return err } requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(cli, imgRefAndAuth.RepoInfo().Index, "pull") - options := types.ImagePullOptions{ + responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), types.ImagePullOptions{ RegistryAuth: encodedAuth, PrivilegeFunc: requestPrivilege, All: opts.all, Platform: opts.platform, - } - responseBody, err := cli.Client().ImagePull(ctx, ref, options) + }) if err != nil { return err } From a27acd62b39e37145e209e4275d40fb4121d2f2f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 11 Apr 2023 20:23:11 +0200 Subject: [PATCH 7/9] cli/command/container/create: pullImage() remove intermediate vars Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 1a48c69f15..05ac2fe0e0 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -131,12 +131,10 @@ func pullImage(ctx context.Context, dockerCli command.Cli, image string, platfor return err } - options := types.ImageCreateOptions{ + responseBody, err := dockerCli.Client().ImageCreate(ctx, image, types.ImageCreateOptions{ RegistryAuth: encodedAuth, Platform: platform, - } - - responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options) + }) if err != nil { return err } From b9b98aee5dc96a8b6cafd72578b06227dd0ddfc9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 11 Apr 2023 20:26:43 +0200 Subject: [PATCH 8/9] cli/command/container/create: pullImage(): use RetrieveAuthTokenFromImage replace the local code with RetrieveAuthTokenFromImage, which does exactly the same; https://github.com/docker/cli/blob/623356001f6310fef7a8cca0b9ba0ac1b735bb38/cli/command/registry.go#L163-L188 Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 05ac2fe0e0..7f66b96e59 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -16,11 +16,9 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/versions" apiclient "github.com/docker/docker/client" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/registry" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -114,19 +112,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio } func pullImage(ctx context.Context, dockerCli command.Cli, image string, platform string, out io.Writer) error { - ref, err := reference.ParseNormalizedNamed(image) - if err != nil { - return err - } - - // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, err := registry.ParseRepositoryInfo(ref) - if err != nil { - return err - } - - authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) - encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) + encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) if err != nil { return err } From 5d856a5d915da07e77d1b4498fd4fbfad180b169 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 11 Apr 2023 23:52:40 +0200 Subject: [PATCH 9/9] cli/command/container: pullImage: use DisplayJSONMessagesToStream utility This utility provides the same logic as was implemented here (and using it aligns with the "docker pull" equivalent). Also added a TODO to replace this function with the regular "docker pull" code. Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 7f66b96e59..18c432f76a 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -12,6 +12,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/image" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/opts" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -111,7 +112,8 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio return nil } -func pullImage(ctx context.Context, dockerCli command.Cli, image string, platform string, out io.Writer) error { +// FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa). +func pullImage(ctx context.Context, dockerCli command.Cli, image string, opts *createOptions) error { encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) if err != nil { return err @@ -119,19 +121,18 @@ func pullImage(ctx context.Context, dockerCli command.Cli, image string, platfor responseBody, err := dockerCli.Client().ImageCreate(ctx, image, types.ImageCreateOptions{ RegistryAuth: encodedAuth, - Platform: platform, + Platform: opts.platform, }) if err != nil { return err } defer responseBody.Close() - return jsonmessage.DisplayJSONMessagesStream( - responseBody, - out, - dockerCli.Out().FD(), - dockerCli.Out().IsTerminal(), - nil) + out := dockerCli.Err() + if opts.quiet { + out = io.Discard + } + return jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(out), nil) } type cidFile struct { @@ -221,11 +222,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c } pullAndTagImage := func() error { - pullOut := dockerCli.Err() - if opts.quiet { - pullOut = io.Discard - } - if err := pullImage(ctx, dockerCli, config.Image, opts.platform, pullOut); err != nil { + if err := pullImage(ctx, dockerCli, config.Image, opts); err != nil { return err } if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {