Compare commits

...

17 Commits

Author SHA1 Message Date
Laura Brehm 11f1178e2d
Merge 508337b62e into abb8e9b78a 2024-10-21 19:03:12 +01:00
Sebastiaan van Stijn abb8e9b78a
Merge pull request #5546 from thaJeztah/hints_coverage
cli/hints: add tests
2024-10-21 18:08:28 +02:00
Laura Brehm 7029147458
Merge pull request #5557 from thaJeztah/minor_linting_issues 2024-10-21 17:00:40 +01:00
Paweł Gronowski d2b87a0a3b
Merge pull request #5553 from thaJeztah/login_idempotent
cli/config/credentials: skip saving config-file if credentials didn't change
2024-10-21 15:23:26 +02:00
Sebastiaan van Stijn 24ee5f228a
Merge pull request #5551 from thaJeztah/fix_ConfigureAuth_deprecation
cli/command: ConfigureAuth: fix deprecation comment
2024-10-21 14:28:43 +02:00
Sebastiaan van Stijn 8b6133a2b7
Merge pull request #5544 from thaJeztah/bump_engine_28
vendor: github.com/docker/docker 36a3bd090489 (master, v28.0-dev)
2024-10-21 13:28:35 +02:00
Sebastiaan van Stijn d3f6867e4d
cli/config/credentials: skip saving config-file if credentials didn't change
Before this change, the config-file was always updated, even if there
were no changes to save. This could cause issues when the config-file
already had credentials set and was read-only for the current user.

For example, on NixOS, this poses a problem because `config.json` is a
symlink to a write-protected file;

    $ readlink ~/.docker/config.json
    /home/username/.config/sops-nix/secrets/ghcr_auth

    $ readlink -f ~/.docker/config.json
    /run/user/1000/secrets.d/28/ghcr_auth

Which causes `docker login` to fail, even if no changes were to be made;

    Error saving credentials: rename /home/derek/.docker/config.json2180380217 /home/username/.config/sops-nix/secrets/ghcr_auth: invalid cross-device link

This patch updates the code to only update the config file if changes
were detected. It there's nothing to save, it skips updating the file,
as well as skips printing the warning about credentials being stored
insecurely.

With this patch applied:

    $ docker login -u yourname
    Password:

    WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
    Configure a credential helper to remove this warning. See
    https://docs.docker.com/go/credential-store/

    Login Succeeded

    $ docker login -u yourname
    Password:
    Login Succeeded

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-21 00:19:52 +02:00
Sebastiaan van Stijn 6b9083776f
cli/command: AddPlatformFlag: suppress unhandled error
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 17:51:36 +02:00
Sebastiaan van Stijn fb61156b05
cli/command/registry: fix minor linting issues
- fix camelCase naming of verifyLoginOptions
- suppress unhandled errors that can be ignored

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 17:51:12 +02:00
Sebastiaan van Stijn 062eecf14a
Merge pull request #5554 from albers/fix-completion-events-filter-daemon
Fix bash completion for `events --filter daemon=`
2024-10-19 17:55:12 +02:00
Harald Albers 3f7b156c85 Fix bash completion for `events --filter daemon=`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-10-19 15:40:07 +00:00
Sebastiaan van Stijn 54e3685bcd
cli/command: ConfigureAuth: fix deprecation comment
Deprecation comments must have an empty line before them, otherwise tools
and linters may not recognise them. While fixing this, also updated the
reference to PromptUserForCredentials to be a docs-link to make it clickable.

Updates 6e4818e7d6.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 13:05:31 +02:00
Sebastiaan van Stijn 87acf77aef
cli/hints: add tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 00:48:16 +02:00
Sebastiaan van Stijn 9b525bc9d1
vendor: github.com/docker/docker 36a3bd090489 (master, v28.0-dev)
full diff: 164cae56ed...36a3bd0904

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-18 17:48:05 +02:00
Sebastiaan van Stijn 8a7c5ae68f
Merge pull request #5542 from thaJeztah/base_completion_tests
cmd/docker: add tests for flag-completions, and refactor
2024-10-18 12:07:02 +02:00
Sebastiaan van Stijn 670f81803f
cmd/docker: add tests for flag-completions, and refactor
Remove the registerCompletionFuncForGlobalFlags for now, as
the error it returned was ignored, so it didn't add much
benefit, other than abstracting things.

Split the underlying completion-functions to separate
functions, and add some basic tests for them.

Remove the completions helper, as it now didn't add much,
and it saved having the dependency on the package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-18 11:23:55 +02:00
Laura Brehm 508337b62e
Replace deprecated usages of `assert.ErrorType`
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-08-01 11:58:28 +01:00
34 changed files with 310 additions and 105 deletions

View File

@ -1,14 +1,13 @@
package manager
import (
"errors"
"fmt"
"reflect"
"strings"
"testing"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
"gotest.tools/v3/assert/cmp"
)
type fakeCandidate struct {
@ -80,7 +79,8 @@ func TestValidateCandidate(t *testing.T) {
assert.ErrorContains(t, err, tc.err)
case tc.invalid != "":
assert.NilError(t, err)
assert.Assert(t, cmp.ErrorType(p.Err, reflect.TypeOf(&pluginError{})))
var expectedError *pluginError
assert.Check(t, errors.As(p.Err, &expectedError))
assert.ErrorContains(t, p.Err, tc.invalid)
default:
assert.NilError(t, err)

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/docker/errdefs"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRemove(t *testing.T) {
@ -18,7 +17,7 @@ func TestRemove(t *testing.T) {
_, err := cli.ContextStore().GetMetadata("current")
assert.NilError(t, err)
_, err = cli.ContextStore().GetMetadata("other")
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
assert.Check(t, errdefs.IsNotFound(err))
}
func TestRemoveNotAContext(t *testing.T) {

View File

@ -47,7 +47,7 @@ func TestUse(t *testing.T) {
func TestUseNoExist(t *testing.T) {
cli := makeFakeCli(t)
err := newUseCommand(cli).RunE(nil, []string{"test"})
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
assert.Check(t, errdefs.IsNotFound(err))
}
// TestUseDefaultWithoutConfigFile verifies that the CLI does not create

View File

@ -14,7 +14,6 @@ import (
"github.com/docker/docker/errdefs"
"github.com/docker/go-connections/tlsconfig"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
)
@ -158,7 +157,7 @@ func TestErrCreateDefault(t *testing.T) {
Metadata: testContext{Bar: "baz"},
Name: "default",
})
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
assert.Check(t, errdefs.IsInvalidParameter(err))
assert.Error(t, err, "default context cannot be created nor updated")
}
@ -166,7 +165,7 @@ func TestErrRemoveDefault(t *testing.T) {
meta := testDefaultMetadata()
s := testStore(t, meta, store.ContextTLSData{})
err := s.Remove("default")
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
assert.Check(t, errdefs.IsInvalidParameter(err))
assert.Error(t, err, "default context cannot be removed")
}
@ -174,5 +173,5 @@ func TestErrTLSDataError(t *testing.T) {
meta := testDefaultMetadata()
s := testStore(t, meta, store.ContextTLSData{})
_, err := s.GetTLSData("default", "noop", "noop")
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
assert.Check(t, errdefs.IsNotFound(err))
}

View File

@ -86,7 +86,8 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
}
// ConfigureAuth handles prompting of user's username and password if needed.
// Deprecated: use PromptUserForCredentials instead.
//
// Deprecated: use [PromptUserForCredentials] instead.
func ConfigureAuth(ctx context.Context, cli Cli, flUser, flPassword string, authConfig *registrytypes.AuthConfig, _ bool) error {
defaultUsername := authConfig.Username
serverAddress := authConfig.ServerAddress

View File

@ -58,9 +58,9 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
return cmd
}
func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
func verifyLoginOptions(dockerCli command.Cli, opts *loginOptions) error {
if opts.password != "" {
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
_, _ = fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
if opts.passwordStdin {
return errors.New("--password and --password-stdin are mutually exclusive")
}
@ -83,7 +83,7 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
}
func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) error {
if err := verifyloginOptions(dockerCli, &opts); err != nil {
if err := verifyLoginOptions(dockerCli, &opts); err != nil {
return err
}
var (
@ -174,7 +174,7 @@ func loginUser(ctx context.Context, dockerCli command.Cli, opts loginOptions, de
if !errors.Is(err, manager.ErrDeviceLoginStartFail) {
return response, err
}
fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
_, _ = fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
}
return loginWithUsernameAndPassword(ctx, dockerCli, opts, defaultUsername, serverAddress)

View File

@ -199,7 +199,7 @@ func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
flags.SetAnnotation("platform", "version", []string{"1.32"})
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
}
// ValidateOutputPath validates the output paths of the `export` and `save` commands.

View File

@ -5,6 +5,7 @@ package loader
import (
"bytes"
"errors"
"os"
"runtime"
"sort"
@ -878,7 +879,8 @@ services:
service: foo
`)
assert.ErrorType(t, err, &ForbiddenPropertiesError{})
var expectedErr *ForbiddenPropertiesError
assert.Check(t, errors.As(err, &expectedErr))
props := err.(*ForbiddenPropertiesError).Properties
assert.Check(t, is.Len(props, 2))

View File

@ -4,6 +4,7 @@
package template
import (
"errors"
"fmt"
"testing"
@ -128,7 +129,8 @@ func TestMandatoryVariableErrors(t *testing.T) {
for _, tc := range testCases {
_, err := Substitute(tc.template, defaultMapping)
assert.Check(t, is.ErrorContains(err, tc.expectedError))
assert.Check(t, is.ErrorType(err, &InvalidTemplateError{}))
var expectedError *InvalidTemplateError
assert.Check(t, errors.As(err, &expectedError))
}
}

View File

@ -30,6 +30,10 @@ func NewFileStore(file store) Store {
// Erase removes the given credentials from the file store.
func (c *fileStore) Erase(serverAddress string) error {
if _, exists := c.file.GetAuthConfigs()[serverAddress]; !exists {
// nothing to do; no credentials found for the given serverAddress
return nil
}
delete(c.file.GetAuthConfigs(), serverAddress)
return c.file.Save()
}
@ -70,9 +74,14 @@ https://docs.docker.com/go/credential-store/
// CLI invocation (no need to warn the user multiple times per command).
var alreadyPrinted atomic.Bool
// Store saves the given credentials in the file store.
// Store saves the given credentials in the file store. This function is
// idempotent and does not update the file if credentials did not change.
func (c *fileStore) Store(authConfig types.AuthConfig) error {
authConfigs := c.file.GetAuthConfigs()
if oldAuthConfig, ok := authConfigs[authConfig.ServerAddress]; ok && oldAuthConfig == authConfig {
// Credentials didn't change, so skip updating the configuration file.
return nil
}
authConfigs[authConfig.ServerAddress] = authConfig
if err := c.file.Save(); err != nil {
return err

View File

@ -26,7 +26,7 @@ func testMetadata(name string) Metadata {
func TestMetadataGetNotExisting(t *testing.T) {
testee := metadataStore{root: t.TempDir(), config: testCfg}
_, err := testee.get("noexist")
assert.ErrorType(t, err, errdefs.IsNotFound)
assert.Check(t, errdefs.IsNotFound(err))
}
func TestMetadataCreateGetRemove(t *testing.T) {
@ -60,7 +60,7 @@ func TestMetadataCreateGetRemove(t *testing.T) {
assert.NilError(t, testee.remove("test-context"))
assert.NilError(t, testee.remove("test-context")) // support duplicate remove
_, err = testee.get("test-context")
assert.ErrorType(t, err, errdefs.IsNotFound)
assert.Check(t, errdefs.IsNotFound(err))
}
func TestMetadataRespectJsonAnnotation(t *testing.T) {

View File

@ -107,7 +107,7 @@ func TestRemove(t *testing.T) {
}))
assert.NilError(t, s.Remove("source"))
_, err = s.GetMetadata("source")
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
assert.Check(t, errdefs.IsNotFound(err))
f, err := s.ListTLSFiles("source")
assert.NilError(t, err)
assert.Equal(t, 0, len(f))
@ -122,7 +122,7 @@ func TestListEmptyStore(t *testing.T) {
func TestErrHasCorrectContext(t *testing.T) {
_, err := New(t.TempDir(), testCfg).GetMetadata("no-exists")
assert.ErrorContains(t, err, "no-exists")
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
assert.Check(t, errdefs.IsNotFound(err))
}
func TestDetectImportContentType(t *testing.T) {

View File

@ -13,7 +13,7 @@ func TestTlsCreateUpdateGetRemove(t *testing.T) {
const contextName = "test-ctx"
_, err := testee.getData(contextName, "test-ep", "test-data")
assert.ErrorType(t, err, errdefs.IsNotFound)
assert.Check(t, errdefs.IsNotFound(err))
err = testee.createOrUpdate(contextName, "test-ep", "test-data", []byte("data"))
assert.NilError(t, err)
@ -29,7 +29,7 @@ func TestTlsCreateUpdateGetRemove(t *testing.T) {
err = testee.removeEndpoint(contextName, "test-ep")
assert.NilError(t, err)
_, err = testee.getData(contextName, "test-ep", "test-data")
assert.ErrorType(t, err, errdefs.IsNotFound)
assert.Check(t, errdefs.IsNotFound(err))
}
func TestTlsListAndBatchRemove(t *testing.T) {

View File

@ -5,7 +5,9 @@ import (
"strconv"
)
// Enabled returns whether cli hints are enabled or not
// Enabled returns whether cli hints are enabled or not. Hints are enabled by
// default, but can be disabled through the "DOCKER_CLI_HINTS" environment
// variable.
func Enabled() bool {
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
enabled, err := strconv.ParseBool(v)

52
cli/hints/hints_test.go Normal file
View File

@ -0,0 +1,52 @@
package hints
import (
"testing"
"gotest.tools/v3/assert"
)
func TestEnabled(t *testing.T) {
tests := []struct {
doc string
env string
expected bool
}{
{
doc: "default",
expected: true,
},
{
doc: "DOCKER_CLI_HINTS=1",
env: "1",
expected: true,
},
{
doc: "DOCKER_CLI_HINTS=true",
env: "true",
expected: true,
},
{
doc: "DOCKER_CLI_HINTS=0",
env: "0",
expected: false,
},
{
doc: "DOCKER_CLI_HINTS=false",
env: "false",
expected: false,
},
{
doc: "DOCKER_CLI_HINTS=not-a-bool",
env: "not-a-bool",
expected: true,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
t.Setenv("DOCKER_CLI_HINTS", tc.env)
assert.Equal(t, Enabled(), tc.expected)
})
}
}

View File

@ -1,7 +1,6 @@
package main
import (
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/context/store"
"github.com/spf13/cobra"
)
@ -10,18 +9,15 @@ type contextStoreProvider interface {
ContextStore() store.Store
}
func registerCompletionFuncForGlobalFlags(dockerCLI contextStoreProvider, cmd *cobra.Command) error {
err := cmd.RegisterFlagCompletionFunc("context", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
func completeContextNames(dockerCLI contextStoreProvider) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
names, _ := store.Names(dockerCLI.ContextStore())
return names, cobra.ShellCompDirectiveNoFileComp
})
if err != nil {
return err
}
err = cmd.RegisterFlagCompletionFunc("log-level", completion.FromList("debug", "info", "warn", "error", "fatal"))
if err != nil {
return err
}
return nil
}
var logLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"}
func completeLogLevels(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return cobra.FixedCompletions(logLevels, cobra.ShellCompDirectiveNoFileComp)(nil, nil, "")
}

View File

@ -0,0 +1,49 @@
package main
import (
"testing"
"github.com/docker/cli/cli/context/store"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
type fakeCLI struct {
contextStore store.Store
}
func (c *fakeCLI) ContextStore() store.Store {
return c.contextStore
}
type fakeContextStore struct {
store.Store
names []string
}
func (f fakeContextStore) List() (c []store.Metadata, _ error) {
for _, name := range f.names {
c = append(c, store.Metadata{Name: name})
}
return c, nil
}
func TestCompleteContextNames(t *testing.T) {
expectedNames := []string{"context-b", "context-c", "context-a"}
cli := &fakeCLI{
contextStore: fakeContextStore{
names: expectedNames,
},
}
values, directives := completeContextNames(cli)(nil, nil, "")
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveNoFileComp))
assert.Check(t, is.DeepEqual(values, expectedNames))
}
func TestCompleteLogLevels(t *testing.T) {
values, directives := completeLogLevels(nil, nil, "")
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveNoFileComp))
assert.Check(t, is.DeepEqual(values, logLevels))
}

View File

@ -100,7 +100,11 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
cmd.SetErr(dockerCli.Err())
opts, helpCmd = cli.SetupRootCommand(cmd)
_ = registerCompletionFuncForGlobalFlags(dockerCli, cmd)
// TODO(thaJeztah): move configuring completion for these flags to where the flags are added.
_ = cmd.RegisterFlagCompletionFunc("context", completeContextNames(dockerCli))
_ = cmd.RegisterFlagCompletionFunc("log-level", completeLogLevels)
cmd.Flags().BoolP("version", "v", false, "Print version information and quit")
setFlagErrorFunc(dockerCli, cmd)

View File

@ -5081,7 +5081,7 @@ _docker_system_events() {
return
;;
daemon)
local name=$(__docker_q info | sed -n 's/^\(ID\|Name\): //p')
local name=$(__docker_q info --format '{{.Info.Name}} {{.Info.ID}}')
COMPREPLY=( $( compgen -W "$name" -- "${cur##*=}" ) )
return
;;

View File

@ -3,7 +3,7 @@ module github.com/docker/cli/docs/generate
// dummy go.mod to avoid dealing with dependencies specific
// to docs generation and not really part of the project.
go 1.16
go 1.22.0
//require (
// github.com/docker/cli v0.0.0+incompatible

View File

@ -3,7 +3,7 @@ module github.com/docker/cli/man
// dummy go.mod to avoid dealing with dependencies specific
// to manpages generation and not really part of the project.
go 1.16
go 1.12.0
//require (
// github.com/docker/cli v0.0.0+incompatible

View File

@ -18,7 +18,7 @@ init() {
cat > go.mod <<EOL
module github.com/docker/cli
go 1.19
go 1.22.0
EOL
}

View File

@ -4,7 +4,7 @@ module github.com/docker/cli
// There is no 'go.mod' file, as that would imply opting in for all the rules
// around SemVer, which this repo cannot abide by as it uses CalVer.
go 1.21.0
go 1.22.0
require (
dario.cat/mergo v1.0.1
@ -13,7 +13,7 @@ require (
github.com/distribution/reference v0.6.0
github.com/docker/cli-docs-tool v0.8.0
github.com/docker/distribution v2.8.3+incompatible
github.com/docker/docker v27.0.2-0.20240912171519-164cae56ed95+incompatible // master (v-next)
github.com/docker/docker v27.0.2-0.20241018142220-36a3bd090489+incompatible // master (v-next)
github.com/docker/docker-credential-helpers v0.8.2
github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0

View File

@ -57,8 +57,8 @@ github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsB
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.0.2-0.20240912171519-164cae56ed95+incompatible h1:HRK75BHG33htes7s+v/fJ8saCNw3B7f3spcgLsvbLRQ=
github.com/docker/docker v27.0.2-0.20240912171519-164cae56ed95+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.0.2-0.20241018142220-36a3bd090489+incompatible h1:utxxyIvPGk7UmtlGHirUyNUP2Spf8yL660PCbmb7tsk=
github.com/docker/docker v27.0.2-0.20241018142220-36a3bd090489+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=

View File

@ -6005,7 +6005,7 @@ definitions:
accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates
from unknown CAs) communication.
By default, local registries (`127.0.0.0/8`) are configured as
By default, local registries (`::1/128` and `127.0.0.0/8`) are configured as
insecure. All other registries are secure. Communicating with an
insecure registry is not possible if the daemon assumes that registry
is secure.
@ -6170,6 +6170,8 @@ definitions:
Expected:
description: |
Commit ID of external tool expected by dockerd as set at build time.
**Deprecated**: This field is deprecated and will be omitted in a API v1.49.
type: "string"
example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4"
@ -7881,10 +7883,12 @@ paths:
type: "string"
- name: "h"
in: "query"
required: true
description: "Height of the TTY session in characters"
type: "integer"
- name: "w"
in: "query"
required: true
description: "Width of the TTY session in characters"
type: "integer"
tags: ["Container"]
@ -10236,10 +10240,12 @@ paths:
type: "string"
- name: "h"
in: "query"
required: true
description: "Height of the TTY session in characters"
type: "integer"
- name: "w"
in: "query"
required: true
description: "Width of the TTY session in characters"
type: "integer"
tags: ["Exec"]

View File

@ -137,8 +137,13 @@ type PluginsInfo struct {
// Commit holds the Git-commit (SHA1) that a binary was built from, as reported
// in the version-string of external tools, such as containerd, or runC.
type Commit struct {
ID string // ID is the actual commit ID of external tool.
Expected string // Expected is the commit ID of external tool expected by dockerd as set at build time.
// ID is the actual commit ID or version of external tool.
ID string
// Expected is the commit ID of external tool expected by dockerd as set at build time.
//
// Deprecated: this field is no longer used in API v1.49, but kept for backward-compatibility with older API versions.
Expected string
}
// NetworkAddressPool is a temp struct used by [Info] struct.

View File

@ -172,4 +172,6 @@ type BuildCachePruneOptions struct {
All bool
KeepStorage int64
Filters filters.Args
// FIXME(thaJeztah): add new options; see https://github.com/moby/moby/issues/48639
}

View File

@ -2,7 +2,7 @@
Package client is a Go client for the Docker Engine API.
For more information about the Engine API, see the documentation:
https://docs.docker.com/engine/api/
https://docs.docker.com/reference/api/engine/
# Usage

View File

@ -5,6 +5,8 @@ import (
"encoding/json"
"net/url"
"path"
"sort"
"strings"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
@ -12,12 +14,6 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
type configWrapper struct {
*container.Config
HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig
}
// ContainerCreate creates a new container based on the given configuration.
// It can be associated with a name, but it's not mandatory.
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
@ -58,6 +54,9 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
// When using API under 1.42, the Linux daemon doesn't respect the ConsoleSize
hostConfig.ConsoleSize = [2]uint{0, 0}
}
hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd)
hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop)
}
// Since API 1.44, the container-wide MacAddress is deprecated and will trigger a WARNING if it's specified.
@ -74,7 +73,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
query.Set("name", containerName)
}
body := configWrapper{
body := container.CreateRequest{
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
@ -114,3 +113,42 @@ func hasEndpointSpecificMacAddress(networkingConfig *network.NetworkingConfig) b
}
return false
}
// allCapabilities is a magic value for "all capabilities"
const allCapabilities = "ALL"
// normalizeCapabilities normalizes capabilities to their canonical form,
// removes duplicates, and sorts the results.
//
// It is similar to [github.com/docker/docker/oci/caps.NormalizeLegacyCapabilities],
// but performs no validation based on supported capabilities.
func normalizeCapabilities(caps []string) []string {
var normalized []string
unique := make(map[string]struct{})
for _, c := range caps {
c = normalizeCap(c)
if _, ok := unique[c]; ok {
continue
}
unique[c] = struct{}{}
normalized = append(normalized, c)
}
sort.Strings(normalized)
return normalized
}
// normalizeCap normalizes a capability to its canonical format by upper-casing
// and adding a "CAP_" prefix (if not yet present). It also accepts the "ALL"
// magic-value.
func normalizeCap(cap string) string {
cap = strings.ToUpper(cap)
if cap == allCapabilities {
return cap
}
if !strings.HasPrefix(cap, "CAP_") {
cap = "CAP_" + cap
}
return cap
}

View File

@ -19,9 +19,10 @@ func (cli *Client) ContainerExecResize(ctx context.Context, execID string, optio
}
func (cli *Client) resize(ctx context.Context, basePath string, height, width uint) error {
// FIXME(thaJeztah): the API / backend accepts uint32, but container.ResizeOptions uses uint.
query := url.Values{}
query.Set("h", strconv.Itoa(int(height)))
query.Set("w", strconv.Itoa(int(width)))
query.Set("h", strconv.FormatUint(uint64(height), 10))
query.Set("w", strconv.FormatUint(uint64(width), 10))
resp, err := cli.post(ctx, basePath+"/resize", query, nil, nil)
ensureReaderClosed(resp)

View File

@ -12,6 +12,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
)
// ImageBuild sends a request to the daemon to build images.
@ -44,10 +45,15 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
}
func (cli *Client) imageBuildOptionsToQuery(ctx context.Context, options types.ImageBuildOptions) (url.Values, error) {
query := url.Values{
"t": options.Tags,
"securityopt": options.SecurityOpt,
"extrahosts": options.ExtraHosts,
query := url.Values{}
if len(options.Tags) > 0 {
query["t"] = options.Tags
}
if len(options.SecurityOpt) > 0 {
query["securityopt"] = options.SecurityOpt
}
if len(options.ExtraHosts) > 0 {
query["extrahosts"] = options.ExtraHosts
}
if options.SuppressOutput {
query.Set("q", "1")
@ -58,9 +64,11 @@ func (cli *Client) imageBuildOptionsToQuery(ctx context.Context, options types.I
if options.NoCache {
query.Set("nocache", "1")
}
if options.Remove {
query.Set("rm", "1")
} else {
if !options.Remove {
// only send value when opting out because the daemon's default is
// to remove intermediate containers after a successful build,
//
// TODO(thaJeztah): deprecate "Remove" option, and provide a "NoRemove" or "Keep" option instead.
query.Set("rm", "0")
}
@ -83,42 +91,70 @@ func (cli *Client) imageBuildOptionsToQuery(ctx context.Context, options types.I
query.Set("isolation", string(options.Isolation))
}
query.Set("cpusetcpus", options.CPUSetCPUs)
query.Set("networkmode", options.NetworkMode)
query.Set("cpusetmems", options.CPUSetMems)
query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
query.Set("memory", strconv.FormatInt(options.Memory, 10))
query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
query.Set("cgroupparent", options.CgroupParent)
query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10))
query.Set("dockerfile", options.Dockerfile)
query.Set("target", options.Target)
ulimitsJSON, err := json.Marshal(options.Ulimits)
if err != nil {
return query, err
if options.CPUSetCPUs != "" {
query.Set("cpusetcpus", options.CPUSetCPUs)
}
query.Set("ulimits", string(ulimitsJSON))
buildArgsJSON, err := json.Marshal(options.BuildArgs)
if err != nil {
return query, err
if options.NetworkMode != "" && options.NetworkMode != network.NetworkDefault {
query.Set("networkmode", options.NetworkMode)
}
query.Set("buildargs", string(buildArgsJSON))
labelsJSON, err := json.Marshal(options.Labels)
if err != nil {
return query, err
if options.CPUSetMems != "" {
query.Set("cpusetmems", options.CPUSetMems)
}
query.Set("labels", string(labelsJSON))
cacheFromJSON, err := json.Marshal(options.CacheFrom)
if err != nil {
return query, err
if options.CPUShares != 0 {
query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
}
if options.CPUQuota != 0 {
query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
}
if options.CPUPeriod != 0 {
query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
}
if options.Memory != 0 {
query.Set("memory", strconv.FormatInt(options.Memory, 10))
}
if options.MemorySwap != 0 {
query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
}
if options.CgroupParent != "" {
query.Set("cgroupparent", options.CgroupParent)
}
if options.ShmSize != 0 {
query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10))
}
if options.Dockerfile != "" {
query.Set("dockerfile", options.Dockerfile)
}
if options.Target != "" {
query.Set("target", options.Target)
}
if len(options.Ulimits) != 0 {
ulimitsJSON, err := json.Marshal(options.Ulimits)
if err != nil {
return query, err
}
query.Set("ulimits", string(ulimitsJSON))
}
if len(options.BuildArgs) != 0 {
buildArgsJSON, err := json.Marshal(options.BuildArgs)
if err != nil {
return query, err
}
query.Set("buildargs", string(buildArgsJSON))
}
if len(options.Labels) != 0 {
labelsJSON, err := json.Marshal(options.Labels)
if err != nil {
return query, err
}
query.Set("labels", string(labelsJSON))
}
if len(options.CacheFrom) != 0 {
cacheFromJSON, err := json.Marshal(options.CacheFrom)
if err != nil {
return query, err
}
query.Set("cachefrom", string(cacheFromJSON))
}
query.Set("cachefrom", string(cacheFromJSON))
if options.SessionID != "" {
query.Set("session", options.SessionID)
}
@ -131,7 +167,9 @@ func (cli *Client) imageBuildOptionsToQuery(ctx context.Context, options types.I
if options.BuildID != "" {
query.Set("buildid", options.BuildID)
}
query.Set("version", string(options.Version))
if options.Version != "" {
query.Set("version", string(options.Version))
}
if options.Outputs != nil {
outputsJSON, err := json.Marshal(options.Outputs)

View File

@ -654,7 +654,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
ta.Buffer.Reset(ta.TarWriter)
defer ta.Buffer.Reset(nil)
_, err = io.Copy(ta.Buffer, file)
_, err = pools.Copy(ta.Buffer, file)
file.Close()
if err != nil {
return err
@ -705,7 +705,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
if err != nil {
return err
}
if _, err := io.Copy(file, reader); err != nil {
if _, err := pools.Copy(file, reader); err != nil {
file.Close()
return err
}
@ -1375,7 +1375,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if _, err := io.Copy(tw, srcF); err != nil {
if _, err := pools.Copy(tw, srcF); err != nil {
return err
}
return nil

View File

@ -184,7 +184,7 @@ func (config *serviceConfig) loadMirrors(mirrors []string) error {
func (config *serviceConfig) loadInsecureRegistries(registries []string) error {
// Localhost is by default considered as an insecure registry. This is a
// stop-gap for people who are running a private registry on localhost.
registries = append(registries, "127.0.0.0/8")
registries = append(registries, "::1/128", "127.0.0.0/8")
var (
insecureRegistryCIDRs = make([]*registry.NetIPNet, 0)

2
vendor/modules.txt vendored
View File

@ -55,7 +55,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid
# github.com/docker/docker v27.0.2-0.20240912171519-164cae56ed95+incompatible
# github.com/docker/docker v27.0.2-0.20241018142220-36a3bd090489+incompatible
## explicit
github.com/docker/docker/api
github.com/docker/docker/api/types