Compare commits

...

27 Commits

Author SHA1 Message Date
9e34c9bb39 Merge pull request #5414 from vvoland/vendor-docker
Some checks failed
build / plugins (push) Has been cancelled
codeql / codeql (push) Has been cancelled
e2e / e2e (alpine, 23, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 23, experimental) (push) Has been cancelled
e2e / e2e (alpine, 23, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, 26.1, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 26.1, experimental) (push) Has been cancelled
e2e / e2e (alpine, 26.1, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 27, experimental) (push) Has been cancelled
e2e / e2e (alpine, 27, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 23, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 23, experimental) (push) Has been cancelled
e2e / e2e (debian, 23, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 26.1, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 26.1, experimental) (push) Has been cancelled
e2e / e2e (debian, 26.1, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 27, experimental) (push) Has been cancelled
e2e / e2e (debian, 27, non-experimental) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-13) (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-md (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
[27.x] vendor: github.com/docker/docker v27.2.1-dev (8b539b8df240)
2024-09-06 12:01:30 +00:00
324cdbca40 vendor: github.com/docker/docker v27.2.1-dev (8b539b8df240)
full diff: https://github.com/docker/docker/compare/v27.2.0...8b539b8df240

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-06 13:48:24 +02:00
b5290d4e0b Merge pull request #5411 from vvoland/5410-27.x
[27.x backport] update to go1.22.7
2024-09-06 10:28:04 +02:00
3db9538748 update to go1.22.7
- https://github.com/golang/go/issues?q=milestone%3AGo1.22.7+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.22.6...go1.22.7

These minor releases include 3 security fixes following the security policy:

- go/parser: stack exhaustion in all Parse* functions

    Calling any of the Parse functions on Go source code which contains deeply nested literals can cause a panic due to stack exhaustion.

    This is CVE-2024-34155 and Go issue https://go.dev/issue/69138.

- encoding/gob: stack exhaustion in Decoder.Decode

    Calling Decoder.Decode on a message which contains deeply nested structures can cause a panic due to stack exhaustion.

    This is a follow-up to CVE-2022-30635.

    Thanks to Md Sakib Anwar of The Ohio State University (anwar.40@osu.edu) for reporting this issue.

    This is CVE-2024-34156 and Go issue https://go.dev/issue/69139.

- go/build/constraint: stack exhaustion in Parse

    Calling Parse on a "// +build" build tag line with deeply nested expressions can cause a panic due to stack exhaustion.

    This is CVE-2024-34158 and Go issue https://go.dev/issue/69141.

View the release notes for more information:
https://go.dev/doc/devel/release#go1.23.1

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 3bf39d25a0)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-05 17:53:21 +02:00
1ab89e71fa Merge pull request #5409 from thaJeztah/27.x_update_docker
[27.x] vendor: github.com/docker/docker v27.2.0
2024-09-05 15:02:51 +02:00
667d9fd4df Merge pull request #5408 from thaJeztah/27.x_backport_mod_tidy
[27.x backport] vendor.mod: put github.com/pkg/browser in the right group
2024-09-05 14:53:18 +02:00
41e61c45d9 [27.x] vendor: github.com/docker/docker v27.2.0
Use a tagged version instead of the commit. No diff as they are the same;
https://github.com/docker/docker/compare/3ab5c7d0036c...v27.2.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-09-05 14:45:28 +02:00
869df10064 vendor.mod: put github.com/pkg/browser in the right group
commit fcfdd7b91f introduced github.com/pkg/browser
as a direct dependency, but it ended up in the group for indirect dependencies.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 1b8180a405)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-09-05 14:40:13 +02:00
6feee4ab35 Merge pull request #5402 from laurazard/backport-27.x-login-not-interactive
[27.x backport] login: handle non-tty scenario consistently
2024-09-03 14:45:27 +00:00
d0c1a80617 login: handle non-tty scenario consistently
Running `docker login` in a non-interactive environment sometimes errors
out if no username/pwd is provided. This handling is somewhat
inconsistent – this commit addresses that.

Before:
| `--username` | `--password` | Result                                                             |
|:------------:|:------------:| ------------------------------------------------------------------ |
|            |            |                                                                  |
|            |            | `Error: Cannot perform an interactive login from a non TTY device` |
|            |            | `Error: Cannot perform an interactive login from a non TTY device` |
|            |            | hangs                                                              |

After:
| `--username` | `--password` | Result                                                             |
|:------------:|:------------:| ------------------------------------------------------------------ |
|            |            |                                                                  |
|            |            | `Error: Cannot perform an interactive login from a non TTY device` |
|            |            | `Error: Cannot perform an interactive login from a non TTY device` |
|            |            | `Error: Cannot perform an interactive login from a non TTY device` |

It's worth calling out a separate scenario – if there are previous,
valid credentials, then running `docker login` with no username or
password provided will use the previously stored credentials, and not
error out.

```console
cat ~/.docker/config.json
{
        "auths": {
                "https://index.docker.io/v1/": {
                        "auth": "xxxxxxxxxxx"
                }
        }
}
⭑ docker login 0>/dev/null
Authenticating with existing credentials...

Login Succeeded
```

This commit also applies the same non-interactive handling logic to the
new web-based login flow, which means that now, if there are no prior
credentials stored and a user runs `docker login`, instead of initiating
the new web-based login flow, an error is returned.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit bbb6e7643d)
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-09-03 15:39:17 +01:00
383c428451 Merge pull request #5400 from vvoland/5387-27.x
[27.x backport] update to go1.22.6
2024-09-03 14:04:04 +02:00
5f8416e541 Merge pull request #5399 from vvoland/5376-27.x
[27.x backport] oauth/api: drain timer channel on each iteration
2024-09-03 14:02:04 +02:00
5bf5cb9ff6 update to go1.22.6
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit d7d56599ca)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-03 13:52:28 +02:00
3a15d5a640 Merge pull request #5395 from thaJeztah/27.x_backport_fix_plugins_CGO_ENABLED
[27.x backport] scripts/build/plugins: don't override CGO_ENABLED set by .variables
2024-09-03 13:25:44 +02:00
1dfd11acc0 oauth/api: drain timer channel on each iteration
Previously, if while polling for oauth device-code login results a user
suspended the process (such as with CTRL-Z) and then restored it with
`fg`, an error might occur in the form of:

```
failed waiting for authentication: You are polling faster than the specified interval of 5 seconds.
```

This is due to our use of a `time.Ticker` here - if no receiver drains
the ticker channel (and timers/tickers use a buffered channel behind the
scenes), more than one tick will pile up, causing the program to "tick"
twice, in fast succession, after it is resumed.

The new implementation replaces the `time.Ticker` with a `time.Timer`
(`time.Ticker` is just a nice wrapper) and introduces a helper function
`resetTimer` to ensure that before every `select`, the timer is stopped
and it's channel is drained.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit 60d0450287)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-03 13:16:03 +02:00
de2b49b074 scripts/build/plugins: don't override CGO_ENABLED set by .variables
The `.variables` sets `CGO_ENABLED=1` on arm; b0c41b78d8/scripts/build/.variables (L57-L68)
And if enabled, it sets `-buildmode=pie`; b0c41b78d8/scripts/build/.variables (L79-L88)

But that looks to be conflicting with the hardcoded `CGO_ENABLED=0` in
this script, which causes the build to fail on go1.22;

    > [build-plugins 1/1] RUN --mount=ro --mount=type=cache,target=/root/.cache     xx-go --wrap &&     TARGET=/out ./scripts/build/plugins e2e/cli-plugins/plugins/*:
    0.127 Building static docker-helloworld
    0.127 + CGO_ENABLED=0
    0.127 + GO111MODULE=auto
    0.127 + go build -o /out/plugins-linux-arm/docker-helloworld -tags ' osusergo' -ldflags ' -X "github.com/docker/cli/cli/version.GitCommit=5c123b1" -X "github.com/docker/cli/cli/version.BuildTime=2024-09-02T13:52:17Z" -X "github.com/docker/cli/cli/version.Version=pr-5387" -extldflags -static' -buildmode=pie github.com/docker/cli/cli-plugins/examples/helloworld
    0.135 -buildmode=pie requires external (cgo) linking, but cgo is not enabled

This patch sets the CGO_ENABLED variable before sourcing `.variables`,
so that other variables which are conditionally set are handled correctly.

Before this PR:

    #18 [build-plugins 1/1] RUN --mount=ro --mount=type=cache,target=/root/.cache     xx-go --wrap &&     TARGET=/out ./scripts/build/plugins e2e/cli-plugins/plugins/*
    #18 0.123 Building static docker-helloworld
    #18 0.124 + CGO_ENABLED=0
    #18 0.124 + GO111MODULE=auto
    #18 0.124 + go build -o /out/plugins-linux-arm/docker-helloworld -tags ' osusergo' -ldflags ' -X "github.com/docker/cli/cli/version.GitCommit=c8c402e" -X "github.com/docker/cli/cli/version.BuildTime=2024-09-03T08:28:25Z" -X "github.com/docker/cli/cli/version.Version=pr-5381" -extldflags -static' -buildmode=pie github.com/docker/cli/cli-plugins/examples/helloworld
    ....

With this PR:

    #18 [build-plugins 1/1] RUN --mount=ro --mount=type=cache,target=/root/.cache     xx-go --wrap &&     TARGET=/out ./scripts/build/plugins e2e/cli-plugins/plugins/*
    #18 0.110 Building static docker-helloworld
    #18 0.110 + GO111MODULE=auto
    #18 0.110 + go build -o /out/plugins-linux-arm/docker-helloworld -tags '' -ldflags ' -X "github.com/docker/cli/cli/version.GitCommit=050d9d6" -X "github.com/docker/cli/cli/version.BuildTime=2024-09-03T09:19:05Z" -X "github.com/docker/cli/cli/version.Version=pr-5387"' github.com/docker/cli/cli-plugins/examples/helloworld
    ....

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 9e29967960)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-09-03 12:56:19 +02:00
c5d846735c Merge pull request #5394 from dvdksn/bp_5386
[27.x backport] docs: update docker login reference #5386
2024-09-03 12:55:19 +02:00
6274754e66 copynit: s/WEB BASED/WEB-BASED/
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 81744d7aa8)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-09-03 12:22:57 +02:00
7a50cd0f01 docs: update docker login reference
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 2f206fff3c)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-09-03 12:22:52 +02:00
074dfc0f88 Merge pull request #5392 from vvoland/5345-27.x
[27.x backport] cli/connhelper: getConnectionHelper: move ssh-option funcs out of closure
2024-09-02 22:29:25 +02:00
92423287cc Merge pull request #5391 from vvoland/5389-27.x
[27.x backport] Dockerfile: update xx to v1.5.0
2024-09-02 22:27:57 +02:00
1a0b6a7a44 cli/connhelper: getConnectionHelper: move ssh-option funcs out of closure
The addSSHTimeout and disablePseudoTerminalAllocation were added in commits
a5ebe2282a and f3c2c26b10,
and called inside the Dialer function, which means they're called every
time the Dialer is called. Given that the sshFlags slice is not mutated
by the Dialer, we can call these functions once.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 0fd3fb0840)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-02 20:57:42 +02:00
8fcfc0b803 Dockerfile: update xx to v1.5.0
full diff: https://github.com/tonistiigi/xx/compare/v1.4.0...v1.5.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 1e6cbbc3f1)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-02 20:55:31 +02:00
28d2fed463 Merge pull request #5385 from vvoland/5383-27.x
[27.x backport] login: use normalized hostname when storing
2024-09-02 10:57:51 +01:00
83072c0232 login: use normalized hostname when storing
Normalization/converting the registry address to just a hostname happens
inside of `command.GetDefaultAuthConfig`. Use this value for the rest of
the login flow/storage.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit e532eead91)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-09-02 11:43:26 +02:00
40109aa45f Merge pull request #5380 from laurazard/dont-normalize-registry-1-backport
[27.x backport] Revert "login: normalize `registry-1.docker.io`"
2024-08-29 13:33:58 +02:00
32aadc9902 Revert "login: normalize registry-1.docker.io"
This reverts commit e6624676e0.

Since e6624676e0, during login, we started
normalizing `registry-1.docker.io` to `index.docker.io`. This means that
if a user logs in with `docker login -u [username]
registry-1.docker.io`, the user's credentials get stored in
credhelpers/config.json under `https://index.docker.io/v1/`.

However, while the registry code normalizes an image reference without
registry (`docker pull alpine:latest`) and image references explicitly for
`index.docker.io` (`docker pull index.docker.io/library/alpine:latest`)
to the official index server (`https://index.docker.io/v1/`), and
fetches credentials for that auth key, it does not normalize
`registry-1.docker.io`, which means pulling explicitly from there
(`docker pull registry-1.docker.io/alpine:latest`) will not use
credentials stored under `https://index.docker.io/v1/`.

As such, until changes are made to the registry/pull/push code to
normalize `registry-1.docker.io` to `https://index.docker.io/v1/`, we
should not normalize this during login.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit dab9674db9)
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-08-29 12:23:27 +01:00
23 changed files with 542 additions and 172 deletions

View File

@ -74,7 +74,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.21.13
go-version: 1.22.7
-
name: Test
run: |

View File

@ -4,8 +4,8 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.20
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.21.13
ARG XX_VERSION=1.4.0
ARG GO_VERSION=1.22.7
ARG XX_VERSION=1.5.0
ARG GOVERSIONINFO_VERSION=v1.3.0
ARG GOTESTSUM_VERSION=v1.10.0
ARG BUILDX_VERSION=0.16.1

View File

@ -124,17 +124,6 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
cli.SetIn(streams.NewIn(os.Stdin))
}
// Some links documenting this:
// - https://code.google.com/archive/p/mintty/issues/56
// - https://github.com/docker/docker/issues/15272
// - https://mintty.github.io/ (compatibility)
// Linux will hit this if you attempt `cat | docker login`, and Windows
// will hit this if you attempt docker login from mintty where stdin
// is a pipe, not a character based console.
if argPassword == "" && !cli.In().IsTerminal() {
return authConfig, errors.Errorf("Error: Cannot perform an interactive login from a non TTY device")
}
isDefaultRegistry := serverAddress == registry.IndexServer
defaultUsername = strings.TrimSpace(defaultUsername)

View File

@ -39,8 +39,8 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "login [OPTIONS] [SERVER]",
Short: "Log in to a registry",
Long: "Log in to a registry.\nIf no server is specified, the default is defined by the daemon.",
Short: "Authenticate to a registry",
Long: "Authenticate to a registry.\nDefaults to Docker Hub if no server is specified.",
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
@ -111,9 +111,7 @@ func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) err
serverAddress string
response *registrytypes.AuthenticateOKBody
)
if opts.serverAddress != "" &&
opts.serverAddress != registry.DefaultNamespace &&
opts.serverAddress != registry.DefaultRegistryHost {
if opts.serverAddress != "" && opts.serverAddress != registry.DefaultNamespace {
serverAddress = opts.serverAddress
} else {
serverAddress = registry.IndexServer
@ -129,7 +127,7 @@ func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) err
// if we failed to authenticate with stored credentials (or didn't have stored credentials),
// prompt the user for new credentials
if err != nil || authConfig.Username == "" || authConfig.Password == "" {
response, err = loginUser(ctx, dockerCli, opts, authConfig.Username, serverAddress)
response, err = loginUser(ctx, dockerCli, opts, authConfig.Username, authConfig.ServerAddress)
if err != nil {
return err
}
@ -178,6 +176,17 @@ func isOauthLoginDisabled() bool {
}
func loginUser(ctx context.Context, dockerCli command.Cli, opts loginOptions, defaultUsername, serverAddress string) (*registrytypes.AuthenticateOKBody, error) {
// Some links documenting this:
// - https://code.google.com/archive/p/mintty/issues/56
// - https://github.com/docker/docker/issues/15272
// - https://mintty.github.io/ (compatibility)
// Linux will hit this if you attempt `cat | docker login`, and Windows
// will hit this if you attempt docker login from mintty where stdin
// is a pipe, not a character based console.
if (opts.user == "" || opts.password == "") && !dockerCli.In().IsTerminal() {
return nil, errors.Errorf("Error: Cannot perform an interactive login from a non TTY device")
}
// If we're logging into the index server and the user didn't provide a username or password, use the device flow
if serverAddress == registry.IndexServer && opts.user == "" && opts.password == "" && !isOauthLoginDisabled() {
response, err := loginWithDeviceCodeFlow(ctx, dockerCli)

View File

@ -16,6 +16,7 @@ import (
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/system"
"github.com/docker/docker/client"
"github.com/docker/docker/registry"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
@ -83,88 +84,207 @@ func TestLoginWithCredStoreCreds(t *testing.T) {
}
func TestRunLogin(t *testing.T) {
const (
storedServerAddress = "reg1"
validUsername = "u1"
validPassword = "p1"
validPassword2 = "p2"
)
validAuthConfig := configtypes.AuthConfig{
ServerAddress: storedServerAddress,
Username: validUsername,
Password: validPassword,
}
expiredAuthConfig := configtypes.AuthConfig{
ServerAddress: storedServerAddress,
Username: validUsername,
Password: expiredPassword,
}
validIdentityToken := configtypes.AuthConfig{
ServerAddress: storedServerAddress,
Username: validUsername,
IdentityToken: useToken,
}
testCases := []struct {
doc string
inputLoginOption loginOptions
inputStoredCred *configtypes.AuthConfig
expectedErr string
expectedSavedCred configtypes.AuthConfig
doc string
priorCredentials map[string]configtypes.AuthConfig
input loginOptions
expectedCredentials map[string]configtypes.AuthConfig
expectedErr string
}{
{
doc: "valid auth from store",
inputLoginOption: loginOptions{
serverAddress: storedServerAddress,
priorCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: "a-password",
ServerAddress: "reg1",
},
},
inputStoredCred: &validAuthConfig,
expectedSavedCred: validAuthConfig,
},
{
doc: "expired auth",
inputLoginOption: loginOptions{
serverAddress: storedServerAddress,
input: loginOptions{
serverAddress: "reg1",
},
inputStoredCred: &expiredAuthConfig,
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
{
doc: "valid username and password",
inputLoginOption: loginOptions{
serverAddress: storedServerAddress,
user: validUsername,
password: validPassword2,
},
inputStoredCred: &validAuthConfig,
expectedSavedCred: configtypes.AuthConfig{
ServerAddress: storedServerAddress,
Username: validUsername,
Password: validPassword2,
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: "a-password",
ServerAddress: "reg1",
},
},
},
{
doc: "unknown user",
inputLoginOption: loginOptions{
serverAddress: storedServerAddress,
doc: "expired auth from store",
priorCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: expiredPassword,
ServerAddress: "reg1",
},
},
input: loginOptions{
serverAddress: "reg1",
},
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
{
doc: "store valid username and password",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
password: "p2",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: "p2",
ServerAddress: "reg1",
},
},
},
{
doc: "unknown user w/ prior credentials",
priorCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: "a-password",
ServerAddress: "reg1",
},
},
input: loginOptions{
serverAddress: "reg1",
user: unknownUser,
password: validPassword,
password: "a-password",
},
expectedErr: errUnknownUser,
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "a-password",
Password: "a-password",
ServerAddress: "reg1",
},
},
inputStoredCred: &validAuthConfig,
expectedErr: errUnknownUser,
},
{
doc: "valid token",
inputLoginOption: loginOptions{
serverAddress: storedServerAddress,
user: validUsername,
doc: "unknown user w/o prior credentials",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "reg1",
user: unknownUser,
password: "a-password",
},
expectedErr: errUnknownUser,
expectedCredentials: map[string]configtypes.AuthConfig{},
},
{
doc: "store valid token",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
password: useToken,
},
inputStoredCred: &validIdentityToken,
expectedSavedCred: validIdentityToken,
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
IdentityToken: useToken,
ServerAddress: "reg1",
},
},
},
{
doc: "valid token from store",
priorCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: useToken,
ServerAddress: "reg1",
},
},
input: loginOptions{
serverAddress: "reg1",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
IdentityToken: useToken,
ServerAddress: "reg1",
},
},
},
{
doc: "no registry specified defaults to index server",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
user: "my-username",
password: "my-password",
},
expectedCredentials: map[string]configtypes.AuthConfig{
registry.IndexServer: {
Username: "my-username",
Password: "my-password",
ServerAddress: registry.IndexServer,
},
},
},
{
doc: "registry-1.docker.io",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "registry-1.docker.io",
user: "my-username",
password: "my-password",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"registry-1.docker.io": {
Username: "my-username",
Password: "my-password",
ServerAddress: "registry-1.docker.io",
},
},
},
// Regression test for https://github.com/docker/cli/issues/5382
{
doc: "sanitizes server address to remove repo",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "registry-1.docker.io/bork/test",
user: "my-username",
password: "a-password",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"registry-1.docker.io": {
Username: "my-username",
Password: "a-password",
ServerAddress: "registry-1.docker.io",
},
},
},
// Regression test for https://github.com/docker/cli/issues/5382
{
doc: "updates credential if server address includes repo",
priorCredentials: map[string]configtypes.AuthConfig{
"registry-1.docker.io": {
Username: "my-username",
Password: "a-password",
ServerAddress: "registry-1.docker.io",
},
},
input: loginOptions{
serverAddress: "registry-1.docker.io/bork/test",
user: "my-username",
password: "new-password",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"registry-1.docker.io": {
Username: "my-username",
Password: "new-password",
ServerAddress: "registry-1.docker.io",
},
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
tmpFile := fs.NewFile(t, "test-run-login")
defer tmpFile.Remove()
@ -172,23 +292,166 @@ func TestRunLogin(t *testing.T) {
configfile := cli.ConfigFile()
configfile.Filename = tmpFile.Path()
if tc.inputStoredCred != nil {
cred := *tc.inputStoredCred
assert.NilError(t, configfile.GetCredentialsStore(cred.ServerAddress).Store(cred))
for _, priorCred := range tc.priorCredentials {
assert.NilError(t, configfile.GetCredentialsStore(priorCred.ServerAddress).Store(priorCred))
}
loginErr := runLogin(context.Background(), cli, tc.inputLoginOption)
storedCreds, err := configfile.GetAllCredentials()
assert.NilError(t, err)
assert.DeepEqual(t, storedCreds, tc.priorCredentials)
loginErr := runLogin(context.Background(), cli, tc.input)
if tc.expectedErr != "" {
assert.Error(t, loginErr, tc.expectedErr)
return
}
assert.NilError(t, loginErr)
savedCred, credStoreErr := configfile.GetCredentialsStore(tc.inputStoredCred.ServerAddress).Get(tc.inputStoredCred.ServerAddress)
assert.Check(t, credStoreErr)
assert.DeepEqual(t, tc.expectedSavedCred, savedCred)
outputCreds, err := configfile.GetAllCredentials()
assert.Check(t, err)
assert.DeepEqual(t, outputCreds, tc.expectedCredentials)
})
}
}
func TestLoginNonInteractive(t *testing.T) {
t.Run("no prior credentials", func(t *testing.T) {
testCases := []struct {
doc string
username bool
password bool
expectedErr string
}{
{
doc: "success - w/ user w/ password",
username: true,
password: true,
},
{
doc: "error - w/o user w/o pass ",
username: false,
password: false,
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
{
doc: "error - w/ user w/o pass",
username: true,
password: false,
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
{
doc: "error - w/o user w/ pass",
username: false,
password: true,
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
}
// "" meaning default registry
registries := []string{"", "my-registry.com"}
for _, registry := range registries {
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
tmpFile := fs.NewFile(t, "test-run-login")
defer tmpFile.Remove()
cli := test.NewFakeCli(&fakeClient{})
configfile := cli.ConfigFile()
configfile.Filename = tmpFile.Path()
options := loginOptions{
serverAddress: registry,
}
if tc.username {
options.user = "my-username"
}
if tc.password {
options.password = "my-password"
}
loginErr := runLogin(context.Background(), cli, options)
if tc.expectedErr != "" {
assert.Error(t, loginErr, tc.expectedErr)
return
}
assert.NilError(t, loginErr)
})
}
}
})
t.Run("w/ prior credentials", func(t *testing.T) {
testCases := []struct {
doc string
username bool
password bool
expectedErr string
}{
{
doc: "success - w/ user w/ password",
username: true,
password: true,
},
{
doc: "success - w/o user w/o pass ",
username: false,
password: false,
},
{
doc: "error - w/ user w/o pass",
username: true,
password: false,
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
{
doc: "error - w/o user w/ pass",
username: false,
password: true,
expectedErr: "Error: Cannot perform an interactive login from a non TTY device",
},
}
// "" meaning default registry
registries := []string{"", "my-registry.com"}
for _, registry := range registries {
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
tmpFile := fs.NewFile(t, "test-run-login")
defer tmpFile.Remove()
cli := test.NewFakeCli(&fakeClient{})
configfile := cli.ConfigFile()
configfile.Filename = tmpFile.Path()
serverAddress := registry
if serverAddress == "" {
serverAddress = "https://index.docker.io/v1/"
}
assert.NilError(t, configfile.GetCredentialsStore(serverAddress).Store(configtypes.AuthConfig{
Username: "my-username",
Password: "my-password",
ServerAddress: serverAddress,
}))
options := loginOptions{
serverAddress: registry,
}
if tc.username {
options.user = "my-username"
}
if tc.password {
options.password = "my-password"
}
loginErr := runLogin(context.Background(), cli, options)
if tc.expectedErr != "" {
assert.Error(t, loginErr, tc.expectedErr)
return
}
assert.NilError(t, loginErr)
})
}
}
})
}
func TestLoginTermination(t *testing.T) {
p, tty, err := pty.Open()
assert.NilError(t, err)

View File

@ -45,14 +45,14 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper
if err != nil {
return nil, errors.Wrap(err, "ssh host connection is not valid")
}
sshFlags = addSSHTimeout(sshFlags)
sshFlags = disablePseudoTerminalAllocation(sshFlags)
return &ConnectionHelper{
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
args := []string{"docker"}
if sp.Path != "" {
args = append(args, "--host", "unix://"+sp.Path)
}
sshFlags = addSSHTimeout(sshFlags)
sshFlags = disablePseudoTerminalAllocation(sshFlags)
args = append(args, "system", "dial-stdio")
return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args(args...)...)...)
},

View File

@ -96,18 +96,32 @@ func tryDecodeOAuthError(resp *http.Response) error {
// authenticated or we have reached the time limit for authenticating (based on
// the response from GetDeviceCode).
func (a API) WaitForDeviceToken(ctx context.Context, state State) (TokenResponse, error) {
ticker := time.NewTicker(state.IntervalDuration())
// Ticker for polling tenant for login based on the interval
// specified by the tenant response.
ticker := time.NewTimer(state.IntervalDuration())
defer ticker.Stop()
timeout := time.After(state.ExpiryDuration())
// The tenant tells us for as long as we can poll it for credentials
// while the user logs in through their browser. Timeout if we don't get
// credentials within this period.
timeout := time.NewTimer(state.ExpiryDuration())
defer timeout.Stop()
for {
resetTimer(ticker, state.IntervalDuration())
select {
case <-ctx.Done():
// user canceled login
return TokenResponse{}, ctx.Err()
case <-ticker.C:
// tick, check for user login
res, err := a.getDeviceToken(ctx, state)
if err != nil {
return res, err
if errors.Is(err, context.Canceled) {
// if the caller canceled the context, continue
// and let the select hit the ctx.Done() branch
continue
}
return TokenResponse{}, err
}
if res.Error != nil {
@ -119,14 +133,33 @@ func (a API) WaitForDeviceToken(ctx context.Context, state State) (TokenResponse
}
return res, nil
case <-timeout:
case <-timeout.C:
// login timed out
return TokenResponse{}, ErrTimeout
}
}
}
// resetTimer is a helper function thatstops, drains and resets the timer.
// This is necessary in go versions <1.23, since the timer isn't stopped +
// the timer's channel isn't drained on timer.Reset.
// See: https://go-review.googlesource.com/c/go/+/568341
// FIXME: remove/simplify this after we update to go1.23
func resetTimer(t *time.Timer, d time.Duration) {
if !t.Stop() {
select {
case <-t.C:
default:
}
}
t.Reset(d)
}
// getToken calls the token endpoint of Auth0 and returns the response.
func (a API) getDeviceToken(ctx context.Context, state State) (TokenResponse, error) {
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel()
data := url.Values{
"client_id": {a.ClientID},
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},

View File

@ -198,7 +198,7 @@ func TestWaitForDeviceToken(t *testing.T) {
state := State{
DeviceCode: "aDeviceCode",
UserCode: "aUserCode",
Interval: 1,
Interval: 5,
ExpiresIn: 1,
}

View File

@ -92,7 +92,7 @@ func (m *OAuthManager) LoginDevice(ctx context.Context, w io.Writer) (*types.Aut
return nil, ErrDeviceLoginStartFail
}
_, _ = fmt.Fprintln(w, aec.Bold.Apply("\nUSING WEB BASED LOGIN"))
_, _ = fmt.Fprintln(w, aec.Bold.Apply("\nUSING WEB-BASED LOGIN"))
_, _ = fmt.Fprintln(w, "To sign in with credentials on the command line, use 'docker login -u <username>'")
_, _ = fmt.Fprintf(w, "\nYour one-time device confirmation code is: "+aec.Bold.Apply("%s\n"), state.UserCode)
_, _ = fmt.Fprintf(w, aec.Bold.Apply("Press ENTER")+" to open your browser or submit your device code here: "+aec.Underline.Apply("%s\n"), strings.Split(state.VerificationURI, "?")[0])

View File

@ -1,5 +1,5 @@
variable "GO_VERSION" {
default = "1.21.13"
default = "1.22.7"
}
variable "VERSION" {
default = ""

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.13
ARG GO_VERSION=1.22.7
ARG ALPINE_VERSION=3.20
ARG BUILDX_VERSION=0.16.1

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.13
ARG GO_VERSION=1.22.7
ARG ALPINE_VERSION=3.20
ARG GOLANGCI_LINT_VERSION=v1.59.1

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.13
ARG GO_VERSION=1.22.7
ARG ALPINE_VERSION=3.20
ARG MODOUTDATED_VERSION=v0.8.0

View File

@ -29,7 +29,7 @@ The base command for the Docker CLI.
| [`inspect`](inspect.md) | Return low-level information on Docker objects |
| [`kill`](kill.md) | Kill one or more running containers |
| [`load`](load.md) | Load an image from a tar archive or STDIN |
| [`login`](login.md) | Log in to a registry |
| [`login`](login.md) | Authenticate to a registry |
| [`logout`](logout.md) | Log out from a registry |
| [`logs`](logs.md) | Fetch the logs of a container |
| [`manifest`](manifest.md) | Manage Docker image manifests and manifest lists |

View File

@ -1,60 +1,51 @@
# login
<!---MARKER_GEN_START-->
Log in to a registry.
If no server is specified, the default is defined by the daemon.
Authenticate to a registry.
Defaults to Docker Hub if no server is specified.
### Options
| Name | Type | Default | Description |
|:--------------------------------------|:---------|:--------|:-----------------------------|
| `-p`, `--password` | `string` | | Password |
| [`--password-stdin`](#password-stdin) | `bool` | | Take the password from stdin |
| `-u`, `--username` | `string` | | Username |
| Name | Type | Default | Description |
|:---------------------------------------------|:---------|:--------|:-----------------------------|
| `-p`, `--password` | `string` | | Password |
| [`--password-stdin`](#password-stdin) | `bool` | | Take the password from stdin |
| [`-u`](#username), [`--username`](#username) | `string` | | Username |
<!---MARKER_GEN_END-->
## Description
Log in to a registry.
Authenticate to a registry.
## Examples
You can authenticate to any public or private registry for which you have
credentials. Authentication may be required for pulling and pushing images.
Other commands, such as `docker scout` and `docker build`, may also require
authentication to access subscription-only features or data related to your
Docker organization.
### Login to a self-hosted registry
Authentication credentials are stored in the configured [credential
store](#credential-stores). If you use Docker Desktop, credentials are
automatically saved to the native keychain of your operating system. If you're
not using Docker Desktop, you can configure the credential store in the Docker
configuration file, which is located at `$HOME/.docker/config.json` on Linux or
`%USERPROFILE%/.docker/config.json` on Windows. If you don't configure a
credential store, Docker stores credentials in the `config.json` file in a
base64-encoded format. This method is less secure than configuring and using a
credential store.
If you want to log in to a self-hosted registry you can specify this by
adding the server name.
`docker login` also supports [credential helpers](#credential-helpers) to help
you handle credentials for specific registries.
```console
$ docker login localhost:8080
```
### Authentication methods
### <a name="password-stdin"></a> Provide a password using STDIN (--password-stdin)
To run the `docker login` command non-interactively, you can set the
`--password-stdin` flag to provide a password through `STDIN`. Using
`STDIN` prevents the password from ending up in the shell's history,
or log-files.
The following example reads a password from a file, and passes it to the
`docker login` command using `STDIN`:
```console
$ cat ~/my_password.txt | docker login --username foo --password-stdin
```
### Privileged user requirement
`docker login` requires you to use `sudo` or be `root`, except when:
- Connecting to a remote daemon, such as a `docker-machine` provisioned `docker engine`.
- The user is added to the `docker` group. This will impact the security of your system; the `docker` group is `root` equivalent. See [Docker Daemon Attack Surface](https://docs.docker.com/engine/security/#docker-daemon-attack-surface) for details.
You can log in to any public or private repository for which you have
credentials. When you log in, the command stores credentials in
`$HOME/.docker/config.json` on Linux or `%USERPROFILE%/.docker/config.json` on
Windows, via the procedure described below.
You can authenticate to a registry using a username and access token or
password. Docker Hub also supports a web-based sign-in flow, which signs you in
to your Docker account without entering your password. For Docker Hub, the
`docker login` command uses a device code flow by default, unless the
`--username` flag is specified. The device code flow is a secure way to sign
in. See [Authenticate to Docker Hub using device code](#authenticate-to-docker-hub-using-device-code).
### Credential stores
@ -75,6 +66,10 @@ Helpers are available for the following credential stores:
- Microsoft Windows Credential Manager
- [pass](https://www.passwordstore.org/)
With Docker Desktop, the credential store is already installed and configured
for you. Unless you want to change the credential store used by Docker Desktop,
you can skip the following steps.
#### Configure the credential store
You need to specify the credential store in `$HOME/.docker/config.json`
@ -94,22 +89,22 @@ the credentials from the file and run `docker login` again.
#### Default behavior
By default, Docker looks for the native binary on each of the platforms, i.e.
"osxkeychain" on macOS, "wincred" on windows, and "pass" on Linux. A special
case is that on Linux, Docker will fall back to the "secretservice" binary if
it cannot find the "pass" binary. If none of these binaries are present, it
stores the credentials (i.e. password) in base64 encoding in the config files
described above.
`osxkeychain` on macOS, `wincred` on Windows, and `pass` on Linux. A special
case is that on Linux, Docker will fall back to the `secretservice` binary if
it cannot find the `pass` binary. If none of these binaries are present, it
stores the base64-encoded credentials in the `config.json` configuration file.
#### Credential helper protocol
Credential helpers can be any program or script that follows a very simple protocol.
This protocol is heavily inspired by Git, but it differs in the information shared.
Credential helpers can be any program or script that implements the credential
helper protocol. This protocol is inspired by Git, but differs in the
information shared.
The helpers always use the first argument in the command to identify the action.
There are only three possible values for that argument: `store`, `get`, and `erase`.
The `store` command takes a JSON payload from the standard input. That payload carries
the server address, to identify the credential, the user name, and either a password
the server address, to identify the credential, the username, and either a password
or an identity token.
```json
@ -149,10 +144,10 @@ will show if there was an issue.
### Credential helpers
Credential helpers are similar to the credential store above, but act as the
designated programs to handle credentials for specific registries. The default
credential store (`credsStore` or the config file itself) will not be used for
operations concerning credentials of the specified registries.
Credential helpers are similar to [credential stores](#credential-stores), but
act as the designated programs to handle credentials for specific registries.
The default credential store will not be used for operations concerning
credentials of the specified registries.
#### Configure credential helpers
@ -162,19 +157,93 @@ the credentials from the default store.
Credential helpers are specified in a similar way to `credsStore`, but
allow for multiple helpers to be configured at a time. Keys specify the
registry domain, and values specify the suffix of the program to use
(i.e. everything after `docker-credential-`).
For example:
(i.e. everything after `docker-credential-`). For example:
```json
{
"credHelpers": {
"registry.example.com": "registryhelper",
"awesomereg.example.org": "hip-star",
"unicorn.example.io": "vcbait"
"myregistry.example.com": "secretservice",
"docker.internal.example": "pass",
}
}
```
## Examples
### Authenticate to Docker Hub with web-based login
By default, the `docker login` command authenticates to Docker Hub, using a
device code flow. This flow lets you authenticate to Docker Hub without
entering your password. Instead, you visit a URL in your web browser, enter a
code, and authenticate.
```console
$ docker login
USING WEB-BASED LOGIN
To sign in with credentials on the command line, use 'docker login -u <username>'
Your one-time device confirmation code is: LNFR-PGCJ
Press ENTER to open your browser or submit your device code here: https://login.docker.com/activate
Waiting for authentication in the browser…
```
After entering the code in your browser, you are authenticated to Docker Hub
using the account you're currently signed in with on the Docker Hub website or
in Docker Desktop. If you aren't signed in, you are prompted to sign in after
entering the device code.
### Authenticate to a self-hosted registry
If you want to authenticate to a self-hosted registry you can specify this by
adding the server name.
```console
$ docker login registry.example.com
```
By default, the `docker login` command assumes that the registry listens on
port 443 or 80. If the registry listens on a different port, you can specify it
by adding the port number to the server name.
```console
$ docker login registry.example.com:1337
```
> [!NOTE]
> Registry addresses should not include URL path components, only the hostname
> and (optionally) the port. Registry addresses with URL path components may
> result in an error. For example, `docker login registry.example.com/foo/`
> is incorrect, while `docker login registry.example.com` is correct.
>
> The exception to this rule is the Docker Hub registry, which may use the
> `/v1/` path component in the address for historical reasons.
### <a name="username"></a> Authenticate to a registry with a username and password
To authenticate to a registry with a username and password, you can use the
`--username` or `-u` flag. The following example authenticates to Docker Hub
with the username `moby`. The password is entered interactively.
```console
$ docker login -u moby
```
### <a name="password-stdin"></a> Provide a password using STDIN (--password-stdin)
To run the `docker login` command non-interactively, you can set the
`--password-stdin` flag to provide a password through `STDIN`. Using
`STDIN` prevents the password from ending up in the shell's history,
or log-files.
The following example reads a password from a file, and passes it to the
`docker login` command using `STDIN`:
```console
$ cat ~/my_password.txt | docker login --username foo --password-stdin
```
## Related commands
* [logout](logout.md)

View File

@ -30,7 +30,7 @@ func TestOauthLogin(t *testing.T) {
assert.NilError(t, err)
output, _ := io.ReadAll(p)
assert.Check(t, strings.Contains(string(output), "USING WEB BASED LOGIN"), string(output))
assert.Check(t, strings.Contains(string(output), "USING WEB-BASED LOGIN"), string(output))
}
func TestLoginWithEscapeHatch(t *testing.T) {

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.13
ARG GO_VERSION=1.22.7
FROM golang:${GO_VERSION}-alpine AS generated
ENV GOTOOLCHAIN=local

View File

@ -5,6 +5,12 @@
set -eu -o pipefail
# Disable CGO - we don't need it for these plugins.
#
# Important: this must be done before sourcing "./scripts/build/.variables",
# because some other variables are conditionally set whether CGO is enabled.
export CGO_ENABLED=0
source ./scripts/build/.variables
for p in cli-plugins/examples/* "$@" ; do
@ -15,5 +21,5 @@ for p in cli-plugins/examples/* "$@" ; do
mkdir -p "$(dirname "${TARGET_PLUGIN}")"
echo "Building $GO_LINKMODE $(basename "${TARGET_PLUGIN}")"
(set -x ; CGO_ENABLED=0 GO111MODULE=auto go build -o "${TARGET_PLUGIN}" -tags "${GO_BUILDTAGS}" -ldflags "${GO_LDFLAGS}" ${GO_BUILDMODE} "github.com/docker/cli/${p}")
(set -x ; GO111MODULE=auto go build -o "${TARGET_PLUGIN}" -tags "${GO_BUILDTAGS}" -ldflags "${GO_LDFLAGS}" ${GO_BUILDMODE} "github.com/docker/cli/${p}")
done

View File

@ -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.2.0-rc.1.0.20240827140014-3ab5c7d0036c+incompatible // 27.x branch (v27.2.0-dev)
github.com/docker/docker v27.2.1-0.20240906095740-8b539b8df240+incompatible
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
@ -32,6 +32,7 @@ require (
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
@ -80,7 +81,6 @@ require (
github.com/moby/sys/symlink v0.2.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect

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.2.0-rc.1.0.20240827140014-3ab5c7d0036c+incompatible h1:EuHKrI999zfPX8J2mF6AcHVAMQvSTkawrSGh81s5ncU=
github.com/docker/docker v27.2.0-rc.1.0.20240827140014-3ab5c7d0036c+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.2.1-0.20240906095740-8b539b8df240+incompatible h1:Ge62WWQ9qg3GHZltYdb0zH1Uq/LVUi8b2/zZ3L1uaAE=
github.com/docker/docker v27.2.1-0.20240906095740-8b539b8df240+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

@ -5347,7 +5347,7 @@ definitions:
The version Go used to compile the daemon, and the version of the Go
runtime in use.
type: "string"
example: "go1.21.13"
example: "go1.22.7"
Os:
description: |
The operating system that the daemon is running on ("linux" or "windows")

View File

@ -1,6 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
import (
"errors"
"fmt"
"strings"
@ -325,12 +326,12 @@ func ValidateRestartPolicy(policy RestartPolicy) error {
if policy.MaximumRetryCount < 0 {
msg += " and cannot be negative"
}
return &errInvalidParameter{fmt.Errorf(msg)}
return &errInvalidParameter{errors.New(msg)}
}
return nil
case RestartPolicyOnFailure:
if policy.MaximumRetryCount < 0 {
return &errInvalidParameter{fmt.Errorf("invalid restart policy: maximum retry count cannot be negative")}
return &errInvalidParameter{errors.New("invalid restart policy: maximum retry count cannot be negative")}
}
return nil
case "":

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.2.0-rc.1.0.20240827140014-3ab5c7d0036c+incompatible
# github.com/docker/docker v27.2.1-0.20240906095740-8b539b8df240+incompatible
## explicit
github.com/docker/docker/api
github.com/docker/docker/api/types