vendor: github.com/docker/cli v29.5.1

full diff: https://github.com/docker/cli/compare/v29.4.3...v29.5.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2026-05-18 20:49:19 +02:00
parent 4361a61408
commit 587111d392
9 changed files with 164 additions and 100 deletions
+2 -2
View File
@@ -17,7 +17,7 @@ require (
github.com/creack/pty v1.1.24
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/distribution/reference v0.6.0
github.com/docker/cli v29.4.3+incompatible
github.com/docker/cli v29.5.1+incompatible
github.com/docker/cli-docs-tool v0.11.0
github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-units v0.5.0
@@ -112,7 +112,7 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/docker/docker-credential-helpers v0.9.6 // indirect
github.com/docker/docker-credential-helpers v0.9.7 // indirect
github.com/docker/go-connections v0.7.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
+4 -4
View File
@@ -179,16 +179,16 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v29.4.3+incompatible h1:u+UliYm2J/rYrIh2FqHQg32neRG8GjbvNuwQRTzGspU=
github.com/docker/cli v29.4.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v29.5.1+incompatible h1:NiufLAJoRcPauFoBNYthfuM4REFwM8H2h9xnLABNHGs=
github.com/docker/cli v29.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE=
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
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 v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.6 h1:cT2PbRPSlnMmNTfT2TDMXRyQ1KMWHG7xoTLBcn1ZNv0=
github.com/docker/docker-credential-helpers v0.9.6/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/docker-credential-helpers v0.9.7 h1:jaPIxEIDz5bQeghNAdzz0ETwMMnM4vzjZlxz3pWP4JA=
github.com/docker/docker-credential-helpers v0.9.7/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
+38 -7
View File
@@ -21,13 +21,14 @@ import (
const (
defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
namesHeader = "NAMES"
commandHeader = "COMMAND"
runningForHeader = "CREATED"
mountsHeader = "MOUNTS"
localVolumes = "LOCAL VOLUMES"
networksHeader = "NETWORKS"
platformHeader = "PLATFORM"
namesHeader = "NAMES"
commandHeader = "COMMAND"
runningForHeader = "CREATED"
mountsHeader = "MOUNTS"
localVolumes = "LOCAL VOLUMES"
networksHeader = "NETWORKS"
platformHeader = "PLATFORM"
healthStatusHeader = "HEALTH STATUS"
)
// Platform wraps a [ocispec.Platform] to implement the stringer interface.
@@ -121,6 +122,7 @@ func NewContainerContext() *ContainerContext {
"LocalVolumes": localVolumes,
"Networks": networksHeader,
"Platform": platformHeader,
"HealthStatus": healthStatusHeader,
}
return &containerCtx
}
@@ -352,6 +354,35 @@ func (c *ContainerContext) Networks() string {
return strings.Join(networks, ",")
}
// HealthStatus returns the container's health status (for example, "healthy","unhealthy", or "starting").
// If no healthcheck is configured, an empty
// string is returned.
func (c *ContainerContext) HealthStatus() string {
if c.c.Health != nil && c.c.Health.Status != "" {
return string(c.c.Health.Status)
}
// Fallback for API versions before v1.52, which include health only in Status text;
// see https://github.com/moby/moby/pull/50281
// see https://github.com/moby/moby/blob/docker-v29.4.3/daemon/container/health.go#L18-L43
_, health, ok := strings.Cut(c.c.Status, "(")
if !ok || !strings.HasSuffix(health, ")") {
return ""
}
health = strings.TrimSuffix(health, ")")
health = strings.TrimPrefix(health, "health: ")
switch container.HealthStatus(health) {
case container.Healthy, container.Unhealthy, container.Starting:
return health
case container.NoHealthcheck:
return ""
default:
return ""
}
}
// DisplayablePorts returns formatted string representing open ports of container
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
// it's used by command 'docker ps'
+5
View File
@@ -1,7 +1,11 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package formatter
import (
"fmt"
"slices"
"strconv"
"strings"
@@ -104,6 +108,7 @@ func (c *volumeContext) Labels() string {
for k, v := range c.v.Labels {
joinLabels = append(joinLabels, k+"="+v)
}
slices.Sort(joinLabels)
return strings.Join(joinLabels, ",")
}
+1 -17
View File
@@ -194,8 +194,7 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
if err != nil {
return "", err
}
configKey := getAuthConfigKey(reference.Domain(registryRef))
authConfig, err := cfg.GetAuthConfig(configKey)
authConfig, err := cfg.GetAuthConfig(reference.Domain(registryRef))
if err != nil {
return "", err
}
@@ -211,18 +210,3 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
RegistryToken: authConfig.RegistryToken,
})
}
// getAuthConfigKey special-cases using the full index address of the official
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
//
// It is similar to [registry.GetAuthConfigKey], but does not require on
// [registrytypes.IndexInfo] as intermediate.
//
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#GetAuthConfigKey
// [registrytypes.IndexInfo]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/api/types/registry#IndexInfo
func getAuthConfigKey(domainName string) string {
if domainName == "docker.io" || domainName == "index.docker.io" {
return authConfigKey
}
return domainName
}
+80 -51
View File
@@ -20,6 +20,34 @@ import (
"github.com/sirupsen/logrus"
)
// authConfigKey is the key used to store credentials for Docker Hub. It is
// a copy of [registry.IndexServer].
//
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.5.1+incompatible/registry#IndexServer
const authConfigKey = "https://index.docker.io/v1/"
// getAuthConfigKey returns the canonical key used to look up stored
// registry credentials for the given registry domain.
//
// For the official Docker Hub registry ("docker.io"), credentials are stored
// under the historical full index address ("https://index.docker.io/v1/").
//
// For all other registries, the input is domainName to already be a normalized
// hostname (optionally including ":port") and is returned unchanged.
//
// This function performs key normalization only; it does not validate or parse
// the input.
//
// It is similar to [registry.GetAuthConfigKey] in the daemon.
//
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.5.1+incompatible/registry#GetAuthConfigKey
func getAuthConfigKey(domainName string) string {
if domainName == "docker.io" || domainName == "index.docker.io" {
return authConfigKey
}
return domainName
}
// ConfigFile ~/.docker/config.json file info
type ConfigFile struct {
AuthConfigs map[string]types.AuthConfig `json:"auths"`
@@ -96,12 +124,12 @@ func New(fn string) *ConfigFile {
// LoadFromReader reads the configuration data given and sets up the auth config
// information with given directory and populates the receiver object
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
if err := json.NewDecoder(configData).Decode(configFile); err != nil && !errors.Is(err, io.EOF) {
func (c *ConfigFile) LoadFromReader(configData io.Reader) error {
if err := json.NewDecoder(configData).Decode(c); err != nil && !errors.Is(err, io.EOF) {
return err
}
var err error
for addr, ac := range configFile.AuthConfigs {
for addr, ac := range c.AuthConfigs {
if ac.Auth != "" {
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
if err != nil {
@@ -110,33 +138,33 @@ func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
}
ac.Auth = ""
ac.ServerAddress = addr
configFile.AuthConfigs[addr] = ac
c.AuthConfigs[addr] = ac
}
return nil
}
// ContainsAuth returns whether there is authentication configured
// in this file or not.
func (configFile *ConfigFile) ContainsAuth() bool {
return configFile.CredentialsStore != "" ||
len(configFile.CredentialHelpers) > 0 ||
len(configFile.AuthConfigs) > 0
func (c *ConfigFile) ContainsAuth() bool {
return c.CredentialsStore != "" ||
len(c.CredentialHelpers) > 0 ||
len(c.AuthConfigs) > 0
}
// GetAuthConfigs returns the mapping of repo to auth configuration
func (configFile *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
if configFile.AuthConfigs == nil {
configFile.AuthConfigs = make(map[string]types.AuthConfig)
func (c *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
if c.AuthConfigs == nil {
c.AuthConfigs = make(map[string]types.AuthConfig)
}
return configFile.AuthConfigs
return c.AuthConfigs
}
// SaveToWriter encodes and writes out all the authorization information to
// the given writer
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
func (c *ConfigFile) SaveToWriter(writer io.Writer) error {
// Encode sensitive data into a new/temp struct
tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
for k, authConfig := range configFile.AuthConfigs {
tmpAuthConfigs := make(map[string]types.AuthConfig, len(c.AuthConfigs))
for k, authConfig := range c.AuthConfigs {
authCopy := authConfig
// encode and save the authstring, while blanking out the original fields
authCopy.Auth = encodeAuth(&authCopy)
@@ -146,18 +174,18 @@ func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
tmpAuthConfigs[k] = authCopy
}
saveAuthConfigs := configFile.AuthConfigs
configFile.AuthConfigs = tmpAuthConfigs
defer func() { configFile.AuthConfigs = saveAuthConfigs }()
saveAuthConfigs := c.AuthConfigs
c.AuthConfigs = tmpAuthConfigs
defer func() { c.AuthConfigs = saveAuthConfigs }()
// User-Agent header is automatically set, and should not be stored in the configuration
for v := range configFile.HTTPHeaders {
for v := range c.HTTPHeaders {
if strings.EqualFold(v, "User-Agent") {
delete(configFile.HTTPHeaders, v)
delete(c.HTTPHeaders, v)
}
}
data, err := json.MarshalIndent(configFile, "", "\t")
data, err := json.MarshalIndent(c, "", "\t")
if err != nil {
return err
}
@@ -166,16 +194,16 @@ func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
}
// Save encodes and writes out all the authorization information
func (configFile *ConfigFile) Save() (retErr error) {
if configFile.Filename == "" {
func (c *ConfigFile) Save() (retErr error) {
if c.Filename == "" {
return errors.New("can't save config with empty filename")
}
dir := filepath.Dir(configFile.Filename)
dir := filepath.Dir(c.Filename)
if err := os.MkdirAll(dir, 0o700); err != nil {
return err
}
temp, err := os.CreateTemp(dir, filepath.Base(configFile.Filename))
temp, err := os.CreateTemp(dir, filepath.Base(c.Filename))
if err != nil {
return err
}
@@ -189,7 +217,7 @@ func (configFile *ConfigFile) Save() (retErr error) {
}
}()
err = configFile.SaveToWriter(temp)
err = c.SaveToWriter(temp)
if err != nil {
return err
}
@@ -199,7 +227,7 @@ func (configFile *ConfigFile) Save() (retErr error) {
}
// Handle situation where the configfile is a symlink, and allow for dangling symlinks
cfgFile := configFile.Filename
cfgFile := c.Filename
if f, err := filepath.EvalSymlinks(cfgFile); err == nil {
cfgFile = f
} else if os.IsNotExist(err) {
@@ -217,16 +245,16 @@ func (configFile *ConfigFile) Save() (retErr error) {
// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and
// then checking this against any environment variables provided to the container
func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string {
func (c *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string {
var cfgKey string
if _, ok := configFile.Proxies[host]; !ok {
if _, ok := c.Proxies[host]; !ok {
cfgKey = "default"
} else {
cfgKey = host
}
config := configFile.Proxies[cfgKey]
config := c.Proxies[cfgKey]
permitted := map[string]*string{
"HTTP_PROXY": &config.HTTPProxy,
"HTTPS_PROXY": &config.HTTPSProxy,
@@ -290,11 +318,11 @@ func decodeAuth(authStr string) (string, string, error) {
// GetCredentialsStore returns a new credentials store from the settings in the
// configuration file
func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store {
store := credentials.NewFileStore(configFile)
func (c *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store {
store := credentials.NewFileStore(c)
if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" {
store = newNativeStore(configFile, helper)
if helper := getConfiguredCredentialStore(c, getAuthConfigKey(registryHostname)); helper != "" {
store = newNativeStore(c, helper)
}
envConfig := os.Getenv(DockerEnvConfigKey)
@@ -357,8 +385,9 @@ var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentia
}
// GetAuthConfig for a repository from the credential store
func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) {
return configFile.GetCredentialsStore(registryHostname).Get(registryHostname)
func (c *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) {
acKey := getAuthConfigKey(registryHostname)
return c.GetCredentialsStore(acKey).Get(acKey)
}
// getConfiguredCredentialStore returns the credential helper configured for the
@@ -375,13 +404,13 @@ func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string
// GetAllCredentials returns all of the credentials stored in all of the
// configured credential stores.
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
func (c *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
auths := make(map[string]types.AuthConfig)
addAll := func(from map[string]types.AuthConfig) {
maps.Copy(auths, from)
}
defaultStore := configFile.GetCredentialsStore("")
defaultStore := c.GetCredentialsStore("")
newAuths, err := defaultStore.GetAll()
if err != nil {
return nil, err
@@ -389,8 +418,8 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
addAll(newAuths)
// Auth configs from a registry-specific helper should override those from the default store.
for registryHostname := range configFile.CredentialHelpers {
newAuth, err := configFile.GetAuthConfig(registryHostname)
for registryHostname := range c.CredentialHelpers {
newAuth, err := c.GetAuthConfig(registryHostname)
if err != nil {
// TODO(thaJeztah): use context-logger, so that this output can be suppressed (in tests).
logrus.WithError(err).Warnf("Failed to get credentials for registry: %s", registryHostname)
@@ -402,16 +431,16 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
}
// GetFilename returns the file name that this config file is based on.
func (configFile *ConfigFile) GetFilename() string {
return configFile.Filename
func (c *ConfigFile) GetFilename() string {
return c.Filename
}
// PluginConfig retrieves the requested option for the given plugin.
func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) {
if configFile.Plugins == nil {
func (c *ConfigFile) PluginConfig(pluginname, option string) (string, bool) {
if c.Plugins == nil {
return "", false
}
pluginConfig, ok := configFile.Plugins[pluginname]
pluginConfig, ok := c.Plugins[pluginname]
if !ok {
return "", false
}
@@ -423,14 +452,14 @@ func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, b
// plugin. Passing a value of "" will remove the option. If removing
// the final config item for a given plugin then also cleans up the
// overall plugin entry.
func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string) {
if configFile.Plugins == nil {
configFile.Plugins = make(map[string]map[string]string)
func (c *ConfigFile) SetPluginConfig(pluginname, option, value string) {
if c.Plugins == nil {
c.Plugins = make(map[string]map[string]string)
}
pluginConfig, ok := configFile.Plugins[pluginname]
pluginConfig, ok := c.Plugins[pluginname]
if !ok {
pluginConfig = make(map[string]string)
configFile.Plugins[pluginname] = pluginConfig
c.Plugins[pluginname] = pluginConfig
}
if value != "" {
pluginConfig[option] = value
@@ -438,6 +467,6 @@ func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string)
delete(pluginConfig, option)
}
if len(pluginConfig) == 0 {
delete(configFile.Plugins, pluginname)
delete(c.Plugins, pluginname)
}
}
+12 -5
View File
@@ -2,12 +2,19 @@ package credentials
import "os/exec"
// DetectDefaultStore return the default credentials store for the platform if
// no user-defined store is passed, and the store executable is available.
func DetectDefaultStore(store string) string {
if store != "" {
// DetectDefaultStore returns the credentials store to use if no user-defined
// custom helper is passed.
//
// Some platforms define a preferred helper, in which case it attempts to look
// up the helper binary before falling back to the platform's default.
//
// If no user-defined helper is passed, and no helper is found, it returns an
// empty string, which means credentials are stored unencrypted in the CLI's
// config-file without the use of a credentials store.
func DetectDefaultStore(customStore string) string {
if customStore != "" {
// use user-defined
return store
return customStore
}
platformDefault := defaultCredentialsStore()
+20 -12
View File
@@ -70,28 +70,36 @@ func ConvertKVStringsToMapWithNil(values []string) map[string]*string {
return result
}
// ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect
// ParseRestartPolicy parses a restart policy string ("name[:max-retries]")
// into a [container.RestartPolicy].
//
// Parsing is syntactic only; semantic validation is deferred to the daemon/API.
// An empty input returns a zero-value policy for backward compatibility. The
// retry count, if set, must be an integer (negative values are allowed here
// but may be rejected by the daemon).
func ParseRestartPolicy(policy string) (container.RestartPolicy, error) {
if policy == "" {
// for backward-compatibility, we don't set the default ("no")
// policy here, because older versions of the engine may not
// support it.
// For backward compatibility, do not set an explicit default ("no"),
// as older daemons may not support it.
return container.RestartPolicy{}, nil
}
p := container.RestartPolicy{}
k, v, ok := strings.Cut(policy, ":")
if ok && k == "" {
name, count, ok := strings.Cut(policy, ":")
if ok && name == "" {
return container.RestartPolicy{}, errors.New("invalid restart policy format: no policy provided before colon")
}
if v != "" {
count, err := strconv.Atoi(v)
var retryCount int
if count != "" {
c, err := strconv.Atoi(count)
if err != nil {
return container.RestartPolicy{}, errors.New("invalid restart policy format: maximum retry count must be an integer")
}
p.MaximumRetryCount = count
retryCount = c
}
p.Name = container.RestartPolicyMode(k)
return p, nil
return container.RestartPolicy{
Name: container.RestartPolicyMode(name),
MaximumRetryCount: retryCount,
}, nil
}
+2 -2
View File
@@ -298,7 +298,7 @@ github.com/digitorus/timestamp
# github.com/distribution/reference v0.6.0
## explicit; go 1.20
github.com/distribution/reference
# github.com/docker/cli v29.4.3+incompatible
# github.com/docker/cli v29.5.1+incompatible
## explicit
github.com/docker/cli/cli
github.com/docker/cli/cli-plugins/metadata
@@ -337,7 +337,7 @@ github.com/docker/cli-docs-tool/annotation
# github.com/docker/docker v28.5.2+incompatible
## explicit
github.com/docker/docker/pkg/namesgenerator
# github.com/docker/docker-credential-helpers v0.9.6
# github.com/docker/docker-credential-helpers v0.9.7
## explicit; go 1.21
github.com/docker/docker-credential-helpers/client
github.com/docker/docker-credential-helpers/credentials