From 981f897b5ff039509a6ff176e5e98358fa02cb12 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 25 Jul 2025 16:36:39 +0200 Subject: [PATCH] extract config into a dedicated module Signed-off-by: Nicolas De Loof --- cli-plugins/manager/cobra.go | 2 +- cli-plugins/manager/hooks.go | 4 +- cli-plugins/manager/manager.go | 4 +- cli-plugins/manager/manager_test.go | 4 +- cli/command/cli.go | 4 +- cli/command/cli_test.go | 4 +- cli/command/config/ls_test.go | 2 +- cli/command/container/auth_config_utils.go | 6 +- cli/command/container/create.go | 4 +- cli/command/container/create_test.go | 2 +- cli/command/container/exec.go | 2 +- cli/command/container/exec_test.go | 2 +- cli/command/container/list_test.go | 2 +- cli/command/context/create_test.go | 2 +- cli/command/context/remove_test.go | 4 +- cli/command/context/use_test.go | 4 +- cli/command/defaultcontextstore_test.go | 2 +- cli/command/image/list_test.go | 2 +- cli/command/manifest/annotate.go | 2 +- cli/command/node/list_test.go | 2 +- cli/command/registry.go | 6 +- cli/command/registry/login.go | 4 +- cli/command/registry/login_test.go | 2 +- cli/command/registry/logout.go | 2 +- cli/command/registry/warning.go | 2 +- cli/command/registry_test.go | 4 +- cli/command/secret/ls_test.go | 2 +- cli/command/stack/ps_test.go | 2 +- cli/command/stack/services_test.go | 2 +- cli/command/system/prune_test.go | 2 +- cli/command/task/print.go | 2 +- cli/command/trust/key_generate_test.go | 2 +- cli/command/trust/key_load_test.go | 2 +- cli/command/trust/sign_test.go | 2 +- cli/command/trust/signer_add_test.go | 2 +- cli/command/utils.go | 2 +- cli/command/volume/list_test.go | 2 +- cli/flags/options.go | 2 +- cli/flags/options_test.go | 2 +- cli/trust/trust.go | 2 +- {cli/config => config}/config.go | 6 +- {cli/config => config}/config_test.go | 4 +- {cli/config => config}/configfile/file.go | 6 +- .../config => config}/configfile/file_test.go | 4 +- .../config => config}/configfile/file_unix.go | 0 .../configfile/file_windows.go | 0 .../testdata/plugin-config-2.golden | 0 .../configfile/testdata/plugin-config.golden | 0 .../credentials/credentials.go | 2 +- .../credentials/default_store.go | 0 .../credentials/default_store_darwin.go | 0 .../credentials/default_store_linux.go | 0 .../credentials/default_store_unsupported.go | 0 .../credentials/default_store_windows.go | 0 .../credentials/file_store.go | 2 +- .../credentials/file_store_test.go | 2 +- .../credentials/native_store.go | 2 +- .../credentials/native_store_test.go | 2 +- config/go.mod | 17 + config/go.sum | 24 + {cli/config => config}/memorystore/store.go | 4 +- .../memorystore/store_test.go | 2 +- {cli/config => config}/types/authconfig.go | 0 e2e/cli-plugins/config_test.go | 2 +- e2e/cli-plugins/util_test.go | 4 +- e2e/context/context_test.go | 2 +- e2e/internal/fixtures/fixtures.go | 2 +- internal/oauth/manager/manager.go | 4 +- internal/oauth/manager/manager_test.go | 4 +- internal/oauth/manager/util.go | 2 +- internal/test/cli.go | 2 +- internal/test/store.go | 4 +- vendor.mod | 9 +- vendor/github.com/docker/cli/config/config.go | 177 +++++++ .../docker/cli/config/configfile/file.go | 444 ++++++++++++++++++ .../docker/cli/config/configfile/file_unix.go | 35 ++ .../cli/config/configfile/file_windows.go | 5 + .../cli/config/credentials/credentials.go | 17 + .../cli/config/credentials/default_store.go | 22 + .../credentials/default_store_darwin.go | 5 + .../config/credentials/default_store_linux.go | 13 + .../credentials/default_store_unsupported.go | 7 + .../credentials/default_store_windows.go | 5 + .../cli/config/credentials/file_store.go | 118 +++++ .../cli/config/credentials/native_store.go | 147 ++++++ .../docker/cli/config/memorystore/store.go | 126 +++++ .../docker/cli/config/types/authconfig.go | 22 + vendor/modules.txt | 8 + 88 files changed, 1284 insertions(+), 87 deletions(-) rename {cli/config => config}/config.go (97%) rename {cli/config => config}/config_test.go (99%) rename {cli/config => config}/configfile/file.go (99%) rename {cli/config => config}/configfile/file_test.go (99%) rename {cli/config => config}/configfile/file_unix.go (100%) rename {cli/config => config}/configfile/file_windows.go (100%) rename {cli/config => config}/configfile/testdata/plugin-config-2.golden (100%) rename {cli/config => config}/configfile/testdata/plugin-config.golden (100%) rename {cli/config => config}/credentials/credentials.go (92%) rename {cli/config => config}/credentials/default_store.go (100%) rename {cli/config => config}/credentials/default_store_darwin.go (100%) rename {cli/config => config}/credentials/default_store_linux.go (100%) rename {cli/config => config}/credentials/default_store_unsupported.go (100%) rename {cli/config => config}/credentials/default_store_windows.go (100%) rename {cli/config => config}/credentials/file_store.go (98%) rename {cli/config => config}/credentials/file_store_test.go (99%) rename {cli/config => config}/credentials/native_store.go (99%) rename {cli/config => config}/credentials/native_store_test.go (99%) create mode 100644 config/go.mod create mode 100644 config/go.sum rename {cli/config => config}/memorystore/store.go (96%) rename {cli/config => config}/memorystore/store_test.go (99%) rename {cli/config => config}/types/authconfig.go (100%) create mode 100644 vendor/github.com/docker/cli/config/config.go create mode 100644 vendor/github.com/docker/cli/config/configfile/file.go create mode 100644 vendor/github.com/docker/cli/config/configfile/file_unix.go create mode 100644 vendor/github.com/docker/cli/config/configfile/file_windows.go create mode 100644 vendor/github.com/docker/cli/config/credentials/credentials.go create mode 100644 vendor/github.com/docker/cli/config/credentials/default_store.go create mode 100644 vendor/github.com/docker/cli/config/credentials/default_store_darwin.go create mode 100644 vendor/github.com/docker/cli/config/credentials/default_store_linux.go create mode 100644 vendor/github.com/docker/cli/config/credentials/default_store_unsupported.go create mode 100644 vendor/github.com/docker/cli/config/credentials/default_store_windows.go create mode 100644 vendor/github.com/docker/cli/config/credentials/file_store.go create mode 100644 vendor/github.com/docker/cli/config/credentials/native_store.go create mode 100644 vendor/github.com/docker/cli/config/memorystore/store.go create mode 100644 vendor/github.com/docker/cli/config/types/authconfig.go diff --git a/cli-plugins/manager/cobra.go b/cli-plugins/manager/cobra.go index ddf067be1dbd..ad4250e1f9a9 100644 --- a/cli-plugins/manager/cobra.go +++ b/cli-plugins/manager/cobra.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/docker/cli/cli-plugins/metadata" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/spf13/cobra" ) diff --git a/cli-plugins/manager/hooks.go b/cli-plugins/manager/hooks.go index bee212702da4..fa3bcce016ac 100644 --- a/cli-plugins/manager/hooks.go +++ b/cli-plugins/manager/hooks.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/docker/cli/cli-plugins/hooks" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index 25515bccb868..249336193e87 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.go @@ -10,8 +10,8 @@ import ( "sync" "github.com/docker/cli/cli-plugins/metadata" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "github.com/fvbommel/sortorder" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" diff --git a/cli-plugins/manager/manager_test.go b/cli-plugins/manager/manager_test.go index 64609fbbad39..00fefd62def5 100644 --- a/cli-plugins/manager/manager_test.go +++ b/cli-plugins/manager/manager_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/spf13/cobra" "gotest.tools/v3/assert" diff --git a/cli/command/cli.go b/cli/command/cli.go index 083ce318ec7b..32413471f394 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -13,8 +13,6 @@ import ( "sync" "time" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" dcontext "github.com/docker/cli/cli/context" "github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/store" @@ -22,6 +20,8 @@ import ( cliflags "github.com/docker/cli/cli/flags" "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/version" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" dopts "github.com/docker/cli/opts" "github.com/moby/moby/api/types/build" "github.com/moby/moby/api/types/swarm" diff --git a/cli/command/cli_test.go b/cli/command/cli_test.go index 4dd0dfbde918..d1b9cded5672 100644 --- a/cli/command/cli_test.go +++ b/cli/command/cli_test.go @@ -16,9 +16,9 @@ import ( "testing" "time" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/flags" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "github.com/moby/moby/api/types" "github.com/moby/moby/client" "gotest.tools/v3/assert" diff --git a/cli/command/config/ls_test.go b/cli/command/config/ls_test.go index 7e3b4b263b94..20a285983a1d 100644 --- a/cli/command/config/ls_test.go +++ b/cli/command/config/ls_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/swarm" diff --git a/cli/command/container/auth_config_utils.go b/cli/command/container/auth_config_utils.go index f908bd84982f..ceea7337b787 100644 --- a/cli/command/container/auth_config_utils.go +++ b/cli/command/container/auth_config_utils.go @@ -5,9 +5,9 @@ import ( "os" "strings" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" + "github.com/docker/cli/config/types" ) // readCredentials resolves auth-config from the current environment to be diff --git a/cli/command/container/create.go b/cli/command/container/create.go index f2d834e54e04..c1553d0a5eeb 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -18,10 +18,10 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/image" - "github.com/docker/cli/cli/config/configfile" - "github.com/docker/cli/cli/config/types" "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" + "github.com/docker/cli/config/configfile" + "github.com/docker/cli/config/types" "github.com/docker/cli/internal/jsonstream" "github.com/docker/cli/opts" "github.com/moby/moby/api/types/container" diff --git a/cli/command/container/create_test.go b/cli/command/container/create_test.go index 5065f3678813..647924556ae5 100644 --- a/cli/command/container/create_test.go +++ b/cli/command/container/create_test.go @@ -11,7 +11,7 @@ import ( "testing" "github.com/docker/cli/cli" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/notary" "github.com/google/go-cmp/cmp" diff --git a/cli/command/container/exec.go b/cli/command/container/exec.go index 678fd0b34c3a..fd915385c931 100644 --- a/cli/command/container/exec.go +++ b/cli/command/container/exec.go @@ -8,7 +8,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/opts" "github.com/moby/moby/api/types/container" "github.com/moby/moby/client" diff --git a/cli/command/container/exec_test.go b/cli/command/container/exec_test.go index 0356c1ef913b..2d3afa797be5 100644 --- a/cli/command/container/exec_test.go +++ b/cli/command/container/exec_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/docker/cli/cli" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/opts" "github.com/moby/moby/api/types/container" diff --git a/cli/command/container/list_test.go b/cli/command/container/list_test.go index a6659377302c..65637fb86cde 100644 --- a/cli/command/container/list_test.go +++ b/cli/command/container/list_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/docker/cli/opts" diff --git a/cli/command/context/create_test.go b/cli/command/context/create_test.go index 794f95de04d5..273ee7035715 100644 --- a/cli/command/context/create_test.go +++ b/cli/command/context/create_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/store" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "gotest.tools/v3/assert" ) diff --git a/cli/command/context/remove_test.go b/cli/command/context/remove_test.go index bbcddec6d16b..c92f03d6be7d 100644 --- a/cli/command/context/remove_test.go +++ b/cli/command/context/remove_test.go @@ -5,8 +5,8 @@ import ( "testing" cerrdefs "github.com/containerd/errdefs" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) diff --git a/cli/command/context/use_test.go b/cli/command/context/use_test.go index 8c7265dac829..f5d9a3e3fde0 100644 --- a/cli/command/context/use_test.go +++ b/cli/command/context/use_test.go @@ -11,9 +11,9 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/flags" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) diff --git a/cli/command/defaultcontextstore_test.go b/cli/command/defaultcontextstore_test.go index d0c8d09b758a..3cd1cffc4e77 100644 --- a/cli/command/defaultcontextstore_test.go +++ b/cli/command/defaultcontextstore_test.go @@ -8,10 +8,10 @@ import ( "testing" cerrdefs "github.com/containerd/errdefs" - "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/store" cliflags "github.com/docker/cli/cli/flags" + "github.com/docker/cli/config/configfile" "github.com/docker/go-connections/tlsconfig" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" diff --git a/cli/command/image/list_test.go b/cli/command/image/list_test.go index df0bb975f374..51e0fde102ec 100644 --- a/cli/command/image/list_test.go +++ b/cli/command/image/list_test.go @@ -6,7 +6,7 @@ import ( "io" "testing" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/moby/moby/api/types/image" "gotest.tools/v3/assert" diff --git a/cli/command/manifest/annotate.go b/cli/command/manifest/annotate.go index 4ad38206201e..a7262dc24f1b 100644 --- a/cli/command/manifest/annotate.go +++ b/cli/command/manifest/annotate.go @@ -7,9 +7,9 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" + "github.com/docker/cli/config" "github.com/moby/moby/api/types/registry" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" diff --git a/cli/command/node/list_test.go b/cli/command/node/list_test.go index 37f0687b644f..30911071ed22 100644 --- a/cli/command/node/list_test.go +++ b/cli/command/node/list_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/swarm" diff --git a/cli/command/registry.go b/cli/command/registry.go index ee4b0c803970..55788fd318db 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -8,11 +8,11 @@ import ( "strings" "github.com/distribution/reference" - "github.com/docker/cli/cli/config/configfile" - "github.com/docker/cli/cli/config/credentials" - configtypes "github.com/docker/cli/cli/config/types" "github.com/docker/cli/cli/hints" "github.com/docker/cli/cli/streams" + "github.com/docker/cli/config/configfile" + "github.com/docker/cli/config/credentials" + configtypes "github.com/docker/cli/config/types" "github.com/docker/cli/internal/prompt" "github.com/docker/cli/internal/tui" registrytypes "github.com/moby/moby/api/types/registry" diff --git a/cli/command/registry/login.go b/cli/command/registry/login.go index 7083aaf932cf..dd9a2470f1f0 100644 --- a/cli/command/registry/login.go +++ b/cli/command/registry/login.go @@ -12,8 +12,8 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" - "github.com/docker/cli/cli/config/configfile" - configtypes "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/configfile" + configtypes "github.com/docker/cli/config/types" "github.com/docker/cli/internal/oauth/manager" "github.com/docker/cli/internal/registry" "github.com/docker/cli/internal/tui" diff --git a/cli/command/registry/login_test.go b/cli/command/registry/login_test.go index 20ca3d929901..0b37fa0a2b30 100644 --- a/cli/command/registry/login_test.go +++ b/cli/command/registry/login_test.go @@ -10,8 +10,8 @@ import ( "time" "github.com/creack/pty" - configtypes "github.com/docker/cli/cli/config/types" "github.com/docker/cli/cli/streams" + configtypes "github.com/docker/cli/config/types" "github.com/docker/cli/internal/prompt" "github.com/docker/cli/internal/registry" "github.com/docker/cli/internal/test" diff --git a/cli/command/registry/logout.go b/cli/command/registry/logout.go index 34498871a557..6c2f4301a804 100644 --- a/cli/command/registry/logout.go +++ b/cli/command/registry/logout.go @@ -6,7 +6,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config/credentials" + "github.com/docker/cli/config/credentials" "github.com/docker/cli/internal/oauth/manager" "github.com/docker/cli/internal/registry" "github.com/spf13/cobra" diff --git a/cli/command/registry/warning.go b/cli/command/registry/warning.go index 22a8c8655be6..cd7f28021023 100644 --- a/cli/command/registry/warning.go +++ b/cli/command/registry/warning.go @@ -4,7 +4,7 @@ import ( "os" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/tui" ) diff --git a/cli/command/registry_test.go b/cli/command/registry_test.go index 20bb234db2d4..4bc27aa1028d 100644 --- a/cli/command/registry_test.go +++ b/cli/command/registry_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config/configfile" - configtypes "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/configfile" + configtypes "github.com/docker/cli/config/types" "github.com/moby/moby/api/types/registry" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" diff --git a/cli/command/secret/ls_test.go b/cli/command/secret/ls_test.go index 3f60a9ce033e..11ed1f9d51ed 100644 --- a/cli/command/secret/ls_test.go +++ b/cli/command/secret/ls_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/swarm" diff --git a/cli/command/stack/ps_test.go b/cli/command/stack/ps_test.go index e3dfd044dfb3..39f729334801 100644 --- a/cli/command/stack/ps_test.go +++ b/cli/command/stack/ps_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/swarm" diff --git a/cli/command/stack/services_test.go b/cli/command/stack/services_test.go index 237c601261ff..aa7eea0254c6 100644 --- a/cli/command/stack/services_test.go +++ b/cli/command/stack/services_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/swarm" diff --git a/cli/command/system/prune_test.go b/cli/command/system/prune_test.go index ad00a19581ed..98e00297a3c1 100644 --- a/cli/command/system/prune_test.go +++ b/cli/command/system/prune_test.go @@ -6,7 +6,7 @@ import ( "io" "testing" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/filters" diff --git a/cli/command/task/print.go b/cli/command/task/print.go index d9d015767b2a..2054aaf75045 100644 --- a/cli/command/task/print.go +++ b/cli/command/task/print.go @@ -8,7 +8,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/idresolver" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/fvbommel/sortorder" "github.com/moby/moby/api/types/swarm" ) diff --git a/cli/command/trust/key_generate_test.go b/cli/command/trust/key_generate_test.go index 1efd1d31214c..a6941977b228 100644 --- a/cli/command/trust/key_generate_test.go +++ b/cli/command/trust/key_generate_test.go @@ -8,7 +8,7 @@ import ( "path/filepath" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/docker/cli/internal/test" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/trustmanager" diff --git a/cli/command/trust/key_load_test.go b/cli/command/trust/key_load_test.go index 2c004d38849d..974ab91bdaa2 100644 --- a/cli/command/trust/key_load_test.go +++ b/cli/command/trust/key_load_test.go @@ -9,7 +9,7 @@ import ( "runtime" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/docker/cli/internal/test" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/storage" diff --git a/cli/command/trust/sign_test.go b/cli/command/trust/sign_test.go index e24ffbba73cb..09c69682fa59 100644 --- a/cli/command/trust/sign_test.go +++ b/cli/command/trust/sign_test.go @@ -7,8 +7,8 @@ import ( "runtime" "testing" - "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/trust" + "github.com/docker/cli/config" "github.com/docker/cli/internal/test" notaryfake "github.com/docker/cli/internal/test/notary" "github.com/theupdateframework/notary" diff --git a/cli/command/trust/signer_add_test.go b/cli/command/trust/signer_add_test.go index 31fd98361176..9d8784dddbf4 100644 --- a/cli/command/trust/signer_add_test.go +++ b/cli/command/trust/signer_add_test.go @@ -8,7 +8,7 @@ import ( "runtime" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/docker/cli/internal/test" notaryfake "github.com/docker/cli/internal/test/notary" "github.com/theupdateframework/notary" diff --git a/cli/command/utils.go b/cli/command/utils.go index ccb05c02ae23..c356912df75c 100644 --- a/cli/command/utils.go +++ b/cli/command/utils.go @@ -10,8 +10,8 @@ import ( "path/filepath" "strings" - "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/streams" + "github.com/docker/cli/config" "github.com/docker/cli/internal/prompt" "github.com/moby/moby/api/types/filters" "github.com/moby/sys/atomicwriter" diff --git a/cli/command/volume/list_test.go b/cli/command/volume/list_test.go index a90d96e8e457..1ff02daf00ea 100644 --- a/cli/command/volume/list_test.go +++ b/cli/command/volume/list_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/filters" diff --git a/cli/flags/options.go b/cli/flags/options.go index a782986f097e..95890a011829 100644 --- a/cli/flags/options.go +++ b/cli/flags/options.go @@ -5,7 +5,7 @@ import ( "os" "path/filepath" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/docker/cli/opts" "github.com/docker/go-connections/tlsconfig" "github.com/moby/moby/client" diff --git a/cli/flags/options_test.go b/cli/flags/options_test.go index 1fef741757be..0f1045c1bc93 100644 --- a/cli/flags/options_test.go +++ b/cli/flags/options_test.go @@ -4,7 +4,7 @@ import ( "path/filepath" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/spf13/pflag" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" diff --git a/cli/trust/trust.go b/cli/trust/trust.go index b8525f051325..ad0363b1a166 100644 --- a/cli/trust/trust.go +++ b/cli/trust/trust.go @@ -13,7 +13,7 @@ import ( "time" "github.com/distribution/reference" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "github.com/docker/cli/internal/registry" "github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth/challenge" diff --git a/cli/config/config.go b/config/config.go similarity index 97% rename from cli/config/config.go rename to config/config.go index cbb34486a6c8..02e02116b4da 100644 --- a/cli/config/config.go +++ b/config/config.go @@ -10,9 +10,9 @@ import ( "strings" "sync" - "github.com/docker/cli/cli/config/configfile" - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/configfile" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" "github.com/pkg/errors" ) diff --git a/cli/config/config_test.go b/config/config_test.go similarity index 99% rename from cli/config/config_test.go rename to config/config_test.go index 922641a726ad..c46c4b6f05e8 100644 --- a/cli/config/config_test.go +++ b/config/config_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/docker/cli/cli/config/configfile" - "github.com/docker/cli/cli/config/credentials" + "github.com/docker/cli/config/configfile" + "github.com/docker/cli/config/credentials" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/skip" diff --git a/cli/config/configfile/file.go b/config/configfile/file.go similarity index 99% rename from cli/config/configfile/file.go rename to config/configfile/file.go index 530c5228561f..fe71fdaa7e20 100644 --- a/cli/config/configfile/file.go +++ b/config/configfile/file.go @@ -9,9 +9,9 @@ import ( "path/filepath" "strings" - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/memorystore" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/memorystore" + "github.com/docker/cli/config/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/cli/config/configfile/file_test.go b/config/configfile/file_test.go similarity index 99% rename from cli/config/configfile/file_test.go rename to config/configfile/file_test.go index 92df02c74352..cc2c9f2227f4 100644 --- a/cli/config/configfile/file_test.go +++ b/config/configfile/file_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/fs" diff --git a/cli/config/configfile/file_unix.go b/config/configfile/file_unix.go similarity index 100% rename from cli/config/configfile/file_unix.go rename to config/configfile/file_unix.go diff --git a/cli/config/configfile/file_windows.go b/config/configfile/file_windows.go similarity index 100% rename from cli/config/configfile/file_windows.go rename to config/configfile/file_windows.go diff --git a/cli/config/configfile/testdata/plugin-config-2.golden b/config/configfile/testdata/plugin-config-2.golden similarity index 100% rename from cli/config/configfile/testdata/plugin-config-2.golden rename to config/configfile/testdata/plugin-config-2.golden diff --git a/cli/config/configfile/testdata/plugin-config.golden b/config/configfile/testdata/plugin-config.golden similarity index 100% rename from cli/config/configfile/testdata/plugin-config.golden rename to config/configfile/testdata/plugin-config.golden diff --git a/cli/config/credentials/credentials.go b/config/credentials/credentials.go similarity index 92% rename from cli/config/credentials/credentials.go rename to config/credentials/credentials.go index 28d58ec48d7d..399b84a42b71 100644 --- a/cli/config/credentials/credentials.go +++ b/config/credentials/credentials.go @@ -1,7 +1,7 @@ package credentials import ( - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/types" ) // Store is the interface that any credentials store must implement. diff --git a/cli/config/credentials/default_store.go b/config/credentials/default_store.go similarity index 100% rename from cli/config/credentials/default_store.go rename to config/credentials/default_store.go diff --git a/cli/config/credentials/default_store_darwin.go b/config/credentials/default_store_darwin.go similarity index 100% rename from cli/config/credentials/default_store_darwin.go rename to config/credentials/default_store_darwin.go diff --git a/cli/config/credentials/default_store_linux.go b/config/credentials/default_store_linux.go similarity index 100% rename from cli/config/credentials/default_store_linux.go rename to config/credentials/default_store_linux.go diff --git a/cli/config/credentials/default_store_unsupported.go b/config/credentials/default_store_unsupported.go similarity index 100% rename from cli/config/credentials/default_store_unsupported.go rename to config/credentials/default_store_unsupported.go diff --git a/cli/config/credentials/default_store_windows.go b/config/credentials/default_store_windows.go similarity index 100% rename from cli/config/credentials/default_store_windows.go rename to config/credentials/default_store_windows.go diff --git a/cli/config/credentials/file_store.go b/config/credentials/file_store.go similarity index 98% rename from cli/config/credentials/file_store.go rename to config/credentials/file_store.go index c69312b01490..e0ff09dbbe82 100644 --- a/cli/config/credentials/file_store.go +++ b/config/credentials/file_store.go @@ -8,7 +8,7 @@ import ( "strings" "sync/atomic" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/types" ) type store interface { diff --git a/cli/config/credentials/file_store_test.go b/config/credentials/file_store_test.go similarity index 99% rename from cli/config/credentials/file_store_test.go rename to config/credentials/file_store_test.go index e4a43e11fb14..0421288879c3 100644 --- a/cli/config/credentials/file_store_test.go +++ b/config/credentials/file_store_test.go @@ -3,7 +3,7 @@ package credentials import ( "testing" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/types" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) diff --git a/cli/config/credentials/native_store.go b/config/credentials/native_store.go similarity index 99% rename from cli/config/credentials/native_store.go rename to config/credentials/native_store.go index b9af145b9dcb..9516cb7684fd 100644 --- a/cli/config/credentials/native_store.go +++ b/config/credentials/native_store.go @@ -1,7 +1,7 @@ package credentials import ( - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/types" "github.com/docker/docker-credential-helpers/client" "github.com/docker/docker-credential-helpers/credentials" ) diff --git a/cli/config/credentials/native_store_test.go b/config/credentials/native_store_test.go similarity index 99% rename from cli/config/credentials/native_store_test.go rename to config/credentials/native_store_test.go index c2f843bfbb95..65d56a9172bd 100644 --- a/cli/config/credentials/native_store_test.go +++ b/config/credentials/native_store_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/types" "github.com/docker/docker-credential-helpers/client" "github.com/docker/docker-credential-helpers/credentials" "gotest.tools/v3/assert" diff --git a/config/go.mod b/config/go.mod new file mode 100644 index 000000000000..228ee263f60d --- /dev/null +++ b/config/go.mod @@ -0,0 +1,17 @@ +module github.com/docker/cli/config + +go 1.24 + +toolchain go1.24.2 + +require ( + github.com/docker/docker-credential-helpers v0.9.3 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.9.3 + gotest.tools/v3 v3.5.2 +) + +require ( + github.com/google/go-cmp v0.5.9 // indirect + golang.org/x/sys v0.20.0 // indirect +) diff --git a/config/go.sum b/config/go.sum new file mode 100644 index 000000000000..3b258105906c --- /dev/null +++ b/config/go.sum @@ -0,0 +1,24 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= +github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/cli/config/memorystore/store.go b/config/memorystore/store.go similarity index 96% rename from cli/config/memorystore/store.go rename to config/memorystore/store.go index 199083464ed8..e2510a6180a6 100644 --- a/cli/config/memorystore/store.go +++ b/config/memorystore/store.go @@ -9,8 +9,8 @@ import ( "os" "sync" - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" ) var errValueNotFound = errors.New("value not found") diff --git a/cli/config/memorystore/store_test.go b/config/memorystore/store_test.go similarity index 99% rename from cli/config/memorystore/store_test.go rename to config/memorystore/store_test.go index a92a170a3b6b..d2981a43a9af 100644 --- a/cli/config/memorystore/store_test.go +++ b/config/memorystore/store_test.go @@ -3,7 +3,7 @@ package memorystore import ( "testing" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/types" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) diff --git a/cli/config/types/authconfig.go b/config/types/authconfig.go similarity index 100% rename from cli/config/types/authconfig.go rename to config/types/authconfig.go diff --git a/e2e/cli-plugins/config_test.go b/e2e/cli-plugins/config_test.go index 1cac1dd7b1e8..4f0cb049ac1b 100644 --- a/e2e/cli-plugins/config_test.go +++ b/e2e/cli-plugins/config_test.go @@ -4,7 +4,7 @@ import ( "path/filepath" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "gotest.tools/v3/assert" "gotest.tools/v3/icmd" ) diff --git a/e2e/cli-plugins/util_test.go b/e2e/cli-plugins/util_test.go index 2f8e0f34e7e9..ee54a4cb4b12 100644 --- a/e2e/cli-plugins/util_test.go +++ b/e2e/cli-plugins/util_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/config" + "github.com/docker/cli/config/configfile" "gotest.tools/v3/assert" "gotest.tools/v3/fs" "gotest.tools/v3/icmd" diff --git a/e2e/context/context_test.go b/e2e/context/context_test.go index 9fba58326e9e..f7b694110831 100644 --- a/e2e/context/context_test.go +++ b/e2e/context/context_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "gotest.tools/v3/assert" "gotest.tools/v3/golden" "gotest.tools/v3/icmd" diff --git a/e2e/internal/fixtures/fixtures.go b/e2e/internal/fixtures/fixtures.go index f6c49f50c794..8b8a68e56b8a 100644 --- a/e2e/internal/fixtures/fixtures.go +++ b/e2e/internal/fixtures/fixtures.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/docker/cli/cli/config" + "github.com/docker/cli/config" "gotest.tools/v3/fs" "gotest.tools/v3/icmd" ) diff --git a/internal/oauth/manager/manager.go b/internal/oauth/manager/manager.go index 96deb61b1325..87f01ecf97f0 100644 --- a/internal/oauth/manager/manager.go +++ b/internal/oauth/manager/manager.go @@ -9,9 +9,9 @@ import ( "os" "strings" - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/types" "github.com/docker/cli/cli/streams" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" "github.com/docker/cli/internal/oauth" "github.com/docker/cli/internal/oauth/api" "github.com/docker/cli/internal/registry" diff --git a/internal/oauth/manager/manager_test.go b/internal/oauth/manager/manager_test.go index 44a744999b8b..47f9edfbc308 100644 --- a/internal/oauth/manager/manager_test.go +++ b/internal/oauth/manager/manager_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" "github.com/docker/cli/internal/oauth/api" "gotest.tools/v3/assert" ) diff --git a/internal/oauth/manager/util.go b/internal/oauth/manager/util.go index c5e268d21433..a5c36f676657 100644 --- a/internal/oauth/manager/util.go +++ b/internal/oauth/manager/util.go @@ -5,8 +5,8 @@ import ( "runtime" "strings" - "github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/cli/version" + "github.com/docker/cli/config/credentials" ) const ( diff --git a/internal/test/cli.go b/internal/test/cli.go index b7c9228f4349..6bb015b05a48 100644 --- a/internal/test/cli.go +++ b/internal/test/cli.go @@ -7,13 +7,13 @@ import ( "strings" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/store" manifeststore "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" + "github.com/docker/cli/config/configfile" "github.com/moby/moby/client" notaryclient "github.com/theupdateframework/notary/client" ) diff --git a/internal/test/store.go b/internal/test/store.go index 802ea6a026a6..e789b6e0fcd1 100644 --- a/internal/test/store.go +++ b/internal/test/store.go @@ -1,8 +1,8 @@ package test import ( - "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/cli/config/types" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" ) // FakeStore implements a credentials.Store that only acts as an in memory map diff --git a/vendor.mod b/vendor.mod index 5a1e7cbeb359..8a4420924450 100644 --- a/vendor.mod +++ b/vendor.mod @@ -4,7 +4,11 @@ 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.23.0 +go 1.24 + +toolchain go1.24.2 + +replace github.com/docker/cli/config => ./config replace ( // FIXME(thaJeztah): temporarily need to pin on commits, otherwise go modules won't resolve until these are tagged. @@ -21,9 +25,10 @@ require ( github.com/creack/pty v1.1.24 github.com/distribution/reference v0.6.0 github.com/docker/cli-docs-tool v0.10.0 + github.com/docker/cli/config v0.0.0 github.com/docker/distribution v2.8.3+incompatible github.com/docker/docker v28.2.3-0.20250724140036-49306c607b72+incompatible // master (v29.0-dev) - github.com/docker/docker-credential-helpers v0.9.3 + github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 github.com/fvbommel/sortorder v1.1.0 diff --git a/vendor/github.com/docker/cli/config/config.go b/vendor/github.com/docker/cli/config/config.go new file mode 100644 index 000000000000..02e02116b4da --- /dev/null +++ b/vendor/github.com/docker/cli/config/config.go @@ -0,0 +1,177 @@ +package config + +import ( + "fmt" + "io" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + "sync" + + "github.com/docker/cli/config/configfile" + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" + "github.com/pkg/errors" +) + +const ( + // EnvOverrideConfigDir is the name of the environment variable that can be + // used to override the location of the client configuration files (~/.docker). + // + // It takes priority over the default, but can be overridden by the "--config" + // command line option. + EnvOverrideConfigDir = "DOCKER_CONFIG" + + // ConfigFileName is the name of the client configuration file inside the + // config-directory. + ConfigFileName = "config.json" + configFileDir = ".docker" + contextsDir = "contexts" +) + +var ( + initConfigDir = new(sync.Once) + configDir string +) + +// resetConfigDir is used in testing to reset the "configDir" package variable +// and its sync.Once to force re-lookup between tests. +func resetConfigDir() { + configDir = "" + initConfigDir = new(sync.Once) +} + +// getHomeDir returns the home directory of the current user with the help of +// environment variables depending on the target operating system. +// Returned path should be used with "path/filepath" to form new paths. +// +// On non-Windows platforms, it falls back to nss lookups, if the home +// directory cannot be obtained from environment-variables. +// +// If linking statically with cgo enabled against glibc, ensure the +// osusergo build tag is used. +// +// If needing to do nss lookups, do not disable cgo or set osusergo. +// +// getHomeDir is a copy of [pkg/homedir.Get] to prevent adding docker/docker +// as dependency for consumers that only need to read the config-file. +// +// [pkg/homedir.Get]: https://pkg.go.dev/github.com/docker/docker@v28.0.3+incompatible/pkg/homedir#Get +func getHomeDir() string { + home, _ := os.UserHomeDir() + if home == "" && runtime.GOOS != "windows" { + if u, err := user.Current(); err == nil { + return u.HomeDir + } + } + return home +} + +// Provider defines an interface for providing the CLI config. +type Provider interface { + ConfigFile() *configfile.ConfigFile +} + +// Dir returns the directory the configuration file is stored in +func Dir() string { + initConfigDir.Do(func() { + configDir = os.Getenv(EnvOverrideConfigDir) + if configDir == "" { + configDir = filepath.Join(getHomeDir(), configFileDir) + } + }) + return configDir +} + +// ContextStoreDir returns the directory the docker contexts are stored in +func ContextStoreDir() string { + return filepath.Join(Dir(), contextsDir) +} + +// SetDir sets the directory the configuration file is stored in +func SetDir(dir string) { + // trigger the sync.Once to synchronise with Dir() + initConfigDir.Do(func() {}) + configDir = filepath.Clean(dir) +} + +// Path returns the path to a file relative to the config dir +func Path(p ...string) (string, error) { + path := filepath.Join(append([]string{Dir()}, p...)...) + if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) { + return "", errors.Errorf("path %q is outside of root config directory %q", path, Dir()) + } + return path, nil +} + +// LoadFromReader is a convenience function that creates a ConfigFile object from +// a reader. It returns an error if configData is malformed. +func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { + configFile := configfile.ConfigFile{ + AuthConfigs: make(map[string]types.AuthConfig), + } + err := configFile.LoadFromReader(configData) + return &configFile, err +} + +// Load reads the configuration file ([ConfigFileName]) from the given directory. +// If no directory is given, it uses the default [Dir]. A [*configfile.ConfigFile] +// is returned containing the contents of the configuration file, or a default +// struct if no configfile exists in the given location. +// +// Load returns an error if a configuration file exists in the given location, +// but cannot be read, or is malformed. Consumers must handle errors to prevent +// overwriting an existing configuration file. +func Load(configDir string) (*configfile.ConfigFile, error) { + if configDir == "" { + configDir = Dir() + } + return load(configDir) +} + +func load(configDir string) (*configfile.ConfigFile, error) { + filename := filepath.Join(configDir, ConfigFileName) + configFile := configfile.New(filename) + + file, err := os.Open(filename) + if err != nil { + if os.IsNotExist(err) { + // It is OK for no configuration file to be present, in which + // case we return a default struct. + return configFile, nil + } + // Any other error happening when failing to read the file must be returned. + return configFile, errors.Wrap(err, "loading config file") + } + defer file.Close() + err = configFile.LoadFromReader(file) + if err != nil { + err = errors.Wrapf(err, "parsing config file (%s)", filename) + } + return configFile, err +} + +// LoadDefaultConfigFile attempts to load the default config file and returns +// a reference to the ConfigFile struct. If none is found or when failing to load +// the configuration file, it initializes a default ConfigFile struct. If no +// credentials-store is set in the configuration file, it attempts to discover +// the default store to use for the current platform. +// +// Important: LoadDefaultConfigFile prints a warning to stderr when failing to +// load the configuration file, but otherwise ignores errors. Consumers should +// consider using [Load] (and [credentials.DetectDefaultStore]) to detect errors +// when updating the configuration file, to prevent discarding a (malformed) +// configuration file. +func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile { + configFile, err := load(Dir()) + if err != nil { + // FIXME(thaJeztah): we should not proceed here to prevent overwriting existing (but malformed) config files; see https://github.com/docker/cli/issues/5075 + _, _ = fmt.Fprintln(stderr, "WARNING: Error", err) + } + if !configFile.ContainsAuth() { + configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore) + } + return configFile +} diff --git a/vendor/github.com/docker/cli/config/configfile/file.go b/vendor/github.com/docker/cli/config/configfile/file.go new file mode 100644 index 000000000000..fe71fdaa7e20 --- /dev/null +++ b/vendor/github.com/docker/cli/config/configfile/file.go @@ -0,0 +1,444 @@ +package configfile + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/memorystore" + "github.com/docker/cli/config/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// ConfigFile ~/.docker/config.json file info +type ConfigFile struct { + AuthConfigs map[string]types.AuthConfig `json:"auths"` + HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` + PsFormat string `json:"psFormat,omitempty"` + ImagesFormat string `json:"imagesFormat,omitempty"` + NetworksFormat string `json:"networksFormat,omitempty"` + PluginsFormat string `json:"pluginsFormat,omitempty"` + VolumesFormat string `json:"volumesFormat,omitempty"` + StatsFormat string `json:"statsFormat,omitempty"` + DetachKeys string `json:"detachKeys,omitempty"` + CredentialsStore string `json:"credsStore,omitempty"` + CredentialHelpers map[string]string `json:"credHelpers,omitempty"` + Filename string `json:"-"` // Note: for internal use only + ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"` + ServicesFormat string `json:"servicesFormat,omitempty"` + TasksFormat string `json:"tasksFormat,omitempty"` + SecretFormat string `json:"secretFormat,omitempty"` + ConfigFormat string `json:"configFormat,omitempty"` + NodesFormat string `json:"nodesFormat,omitempty"` + PruneFilters []string `json:"pruneFilters,omitempty"` + Proxies map[string]ProxyConfig `json:"proxies,omitempty"` + CurrentContext string `json:"currentContext,omitempty"` + CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"` + Plugins map[string]map[string]string `json:"plugins,omitempty"` + Aliases map[string]string `json:"aliases,omitempty"` + Features map[string]string `json:"features,omitempty"` + + // Deprecated: experimental CLI features are always enabled and this field is no longer used. Use [Features] instead for optional features. This field will be removed in a future release. + Experimental string `json:"experimental,omitempty"` +} + +type configEnvAuth struct { + Auth string `json:"auth"` +} + +type configEnv struct { + AuthConfigs map[string]configEnvAuth `json:"auths"` +} + +// DockerEnvConfigKey is an environment variable that contains a JSON encoded +// credential config. It only supports storing the credentials as a base64 +// encoded string in the format base64("username:pat"). +// +// Adding additional fields will produce a parsing error. +// +// Example: +// +// { +// "auths": { +// "example.test": { +// "auth": base64-encoded-username-pat +// } +// } +// } +const DockerEnvConfigKey = "DOCKER_AUTH_CONFIG" + +// ProxyConfig contains proxy configuration settings +type ProxyConfig struct { + HTTPProxy string `json:"httpProxy,omitempty"` + HTTPSProxy string `json:"httpsProxy,omitempty"` + NoProxy string `json:"noProxy,omitempty"` + FTPProxy string `json:"ftpProxy,omitempty"` + AllProxy string `json:"allProxy,omitempty"` +} + +// New initializes an empty configuration file for the given filename 'fn' +func New(fn string) *ConfigFile { + return &ConfigFile{ + AuthConfigs: make(map[string]types.AuthConfig), + HTTPHeaders: make(map[string]string), + Filename: fn, + Plugins: make(map[string]map[string]string), + Aliases: make(map[string]string), + } +} + +// 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) { + return err + } + var err error + for addr, ac := range configFile.AuthConfigs { + if ac.Auth != "" { + ac.Username, ac.Password, err = decodeAuth(ac.Auth) + if err != nil { + return err + } + } + ac.Auth = "" + ac.ServerAddress = addr + configFile.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 +} + +// 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) + } + return configFile.AuthConfigs +} + +// SaveToWriter encodes and writes out all the authorization information to +// the given writer +func (configFile *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 { + authCopy := authConfig + // encode and save the authstring, while blanking out the original fields + authCopy.Auth = encodeAuth(&authCopy) + authCopy.Username = "" + authCopy.Password = "" + authCopy.ServerAddress = "" + tmpAuthConfigs[k] = authCopy + } + + saveAuthConfigs := configFile.AuthConfigs + configFile.AuthConfigs = tmpAuthConfigs + defer func() { configFile.AuthConfigs = saveAuthConfigs }() + + // User-Agent header is automatically set, and should not be stored in the configuration + for v := range configFile.HTTPHeaders { + if strings.EqualFold(v, "User-Agent") { + delete(configFile.HTTPHeaders, v) + } + } + + data, err := json.MarshalIndent(configFile, "", "\t") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Save encodes and writes out all the authorization information +func (configFile *ConfigFile) Save() (retErr error) { + if configFile.Filename == "" { + return errors.Errorf("Can't save config with empty filename") + } + + dir := filepath.Dir(configFile.Filename) + if err := os.MkdirAll(dir, 0o700); err != nil { + return err + } + temp, err := os.CreateTemp(dir, filepath.Base(configFile.Filename)) + if err != nil { + return err + } + defer func() { + // ignore error as the file may already be closed when we reach this. + _ = temp.Close() + if retErr != nil { + if err := os.Remove(temp.Name()); err != nil { + logrus.WithError(err).WithField("file", temp.Name()).Debug("Error cleaning up temp file") + } + } + }() + + err = configFile.SaveToWriter(temp) + if err != nil { + return err + } + + if err := temp.Close(); err != nil { + return errors.Wrap(err, "error closing temp file") + } + + // Handle situation where the configfile is a symlink, and allow for dangling symlinks + cfgFile := configFile.Filename + if f, err := filepath.EvalSymlinks(cfgFile); err == nil { + cfgFile = f + } else if os.IsNotExist(err) { + // extract the path from the error if the configfile does not exist or is a dangling symlink + var pathError *os.PathError + if errors.As(err, &pathError) { + cfgFile = pathError.Path + } + } + + // Try copying the current config file (if any) ownership and permissions + copyFilePermissions(cfgFile, temp.Name()) + return os.Rename(temp.Name(), cfgFile) +} + +// 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 { + var cfgKey string + + if _, ok := configFile.Proxies[host]; !ok { + cfgKey = "default" + } else { + cfgKey = host + } + + config := configFile.Proxies[cfgKey] + permitted := map[string]*string{ + "HTTP_PROXY": &config.HTTPProxy, + "HTTPS_PROXY": &config.HTTPSProxy, + "NO_PROXY": &config.NoProxy, + "FTP_PROXY": &config.FTPProxy, + "ALL_PROXY": &config.AllProxy, + } + m := runOpts + if m == nil { + m = make(map[string]*string) + } + for k := range permitted { + if *permitted[k] == "" { + continue + } + if _, ok := m[k]; !ok { + m[k] = permitted[k] + } + if _, ok := m[strings.ToLower(k)]; !ok { + m[strings.ToLower(k)] = permitted[k] + } + } + return m +} + +// encodeAuth creates a base64 encoded string to containing authorization information +func encodeAuth(authConfig *types.AuthConfig) string { + if authConfig.Username == "" && authConfig.Password == "" { + return "" + } + + authStr := authConfig.Username + ":" + authConfig.Password + msg := []byte(authStr) + encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg))) + base64.StdEncoding.Encode(encoded, msg) + return string(encoded) +} + +// decodeAuth decodes a base64 encoded string and returns username and password +func decodeAuth(authStr string) (string, string, error) { + if authStr == "" { + return "", "", nil + } + + decLen := base64.StdEncoding.DecodedLen(len(authStr)) + decoded := make([]byte, decLen) + authByte := []byte(authStr) + n, err := base64.StdEncoding.Decode(decoded, authByte) + if err != nil { + return "", "", err + } + if n > decLen { + return "", "", errors.Errorf("Something went wrong decoding auth config") + } + userName, password, ok := strings.Cut(string(decoded), ":") + if !ok || userName == "" { + return "", "", errors.Errorf("Invalid auth configuration file") + } + return userName, strings.Trim(password, "\x00"), nil +} + +// 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) + + if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" { + store = newNativeStore(configFile, helper) + } + + envConfig := os.Getenv(DockerEnvConfigKey) + if envConfig == "" { + return store + } + + authConfig, err := parseEnvConfig(envConfig) + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, "Failed to create credential store from DOCKER_AUTH_CONFIG: ", err) + return store + } + + // use DOCKER_AUTH_CONFIG if set + // it uses the native or file store as a fallback to fetch and store credentials + envStore, err := memorystore.New( + memorystore.WithAuthConfig(authConfig), + memorystore.WithFallbackStore(store), + ) + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, "Failed to create credential store from DOCKER_AUTH_CONFIG: ", err) + return store + } + + return envStore +} + +func parseEnvConfig(v string) (map[string]types.AuthConfig, error) { + envConfig := &configEnv{} + decoder := json.NewDecoder(strings.NewReader(v)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(envConfig); err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + if decoder.More() { + return nil, errors.New("DOCKER_AUTH_CONFIG does not support more than one JSON object") + } + + authConfigs := make(map[string]types.AuthConfig) + for addr, envAuth := range envConfig.AuthConfigs { + if envAuth.Auth == "" { + return nil, fmt.Errorf("DOCKER_AUTH_CONFIG environment variable is missing key `auth` for %s", addr) + } + username, password, err := decodeAuth(envAuth.Auth) + if err != nil { + return nil, err + } + authConfigs[addr] = types.AuthConfig{ + Username: username, + Password: password, + ServerAddress: addr, + } + } + return authConfigs, nil +} + +// var for unit testing. +var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + return credentials.NewNativeStore(configFile, helperSuffix) +} + +// GetAuthConfig for a repository from the credential store +func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) { + return configFile.GetCredentialsStore(registryHostname).Get(registryHostname) +} + +// getConfiguredCredentialStore returns the credential helper configured for the +// given registry, the default credsStore, or the empty string if neither are +// configured. +func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string { + if c.CredentialHelpers != nil && registryHostname != "" { + if helper, exists := c.CredentialHelpers[registryHostname]; exists { + return helper + } + } + return c.CredentialsStore +} + +// GetAllCredentials returns all of the credentials stored in all of the +// configured credential stores. +func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) { + auths := make(map[string]types.AuthConfig) + addAll := func(from map[string]types.AuthConfig) { + for reg, ac := range from { + auths[reg] = ac + } + } + + defaultStore := configFile.GetCredentialsStore("") + newAuths, err := defaultStore.GetAll() + if err != nil { + return nil, err + } + 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) + 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) + continue + } + auths[registryHostname] = newAuth + } + return auths, nil +} + +// GetFilename returns the file name that this config file is based on. +func (configFile *ConfigFile) GetFilename() string { + return configFile.Filename +} + +// PluginConfig retrieves the requested option for the given plugin. +func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) { + if configFile.Plugins == nil { + return "", false + } + pluginConfig, ok := configFile.Plugins[pluginname] + if !ok { + return "", false + } + value, ok := pluginConfig[option] + return value, ok +} + +// SetPluginConfig sets the option to the given value for the given +// 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) + } + pluginConfig, ok := configFile.Plugins[pluginname] + if !ok { + pluginConfig = make(map[string]string) + configFile.Plugins[pluginname] = pluginConfig + } + if value != "" { + pluginConfig[option] = value + } else { + delete(pluginConfig, option) + } + if len(pluginConfig) == 0 { + delete(configFile.Plugins, pluginname) + } +} diff --git a/vendor/github.com/docker/cli/config/configfile/file_unix.go b/vendor/github.com/docker/cli/config/configfile/file_unix.go new file mode 100644 index 000000000000..06b811e7d5fd --- /dev/null +++ b/vendor/github.com/docker/cli/config/configfile/file_unix.go @@ -0,0 +1,35 @@ +//go:build !windows + +package configfile + +import ( + "os" + "syscall" +) + +// copyFilePermissions copies file ownership and permissions from "src" to "dst", +// ignoring any error during the process. +func copyFilePermissions(src, dst string) { + var ( + mode os.FileMode = 0o600 + uid, gid int + ) + + fi, err := os.Stat(src) + if err != nil { + return + } + if fi.Mode().IsRegular() { + mode = fi.Mode() + } + if err := os.Chmod(dst, mode); err != nil { + return + } + + uid = int(fi.Sys().(*syscall.Stat_t).Uid) + gid = int(fi.Sys().(*syscall.Stat_t).Gid) + + if uid > 0 && gid > 0 { + _ = os.Chown(dst, uid, gid) + } +} diff --git a/vendor/github.com/docker/cli/config/configfile/file_windows.go b/vendor/github.com/docker/cli/config/configfile/file_windows.go new file mode 100644 index 000000000000..42fffc39ad2e --- /dev/null +++ b/vendor/github.com/docker/cli/config/configfile/file_windows.go @@ -0,0 +1,5 @@ +package configfile + +func copyFilePermissions(src, dst string) { + // TODO implement for Windows +} diff --git a/vendor/github.com/docker/cli/config/credentials/credentials.go b/vendor/github.com/docker/cli/config/credentials/credentials.go new file mode 100644 index 000000000000..399b84a42b71 --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/credentials.go @@ -0,0 +1,17 @@ +package credentials + +import ( + "github.com/docker/cli/config/types" +) + +// Store is the interface that any credentials store must implement. +type Store interface { + // Erase removes credentials from the store for a given server. + Erase(serverAddress string) error + // Get retrieves credentials from the store for a given server. + Get(serverAddress string) (types.AuthConfig, error) + // GetAll retrieves all the credentials from the store. + GetAll() (map[string]types.AuthConfig, error) + // Store saves credentials in the store. + Store(authConfig types.AuthConfig) error +} diff --git a/vendor/github.com/docker/cli/config/credentials/default_store.go b/vendor/github.com/docker/cli/config/credentials/default_store.go new file mode 100644 index 000000000000..a36afc41f4f5 --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/default_store.go @@ -0,0 +1,22 @@ +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 != "" { + // use user-defined + return store + } + + platformDefault := defaultCredentialsStore() + if platformDefault == "" { + return "" + } + + if _, err := exec.LookPath(remoteCredentialsPrefix + platformDefault); err != nil { + return "" + } + return platformDefault +} diff --git a/vendor/github.com/docker/cli/config/credentials/default_store_darwin.go b/vendor/github.com/docker/cli/config/credentials/default_store_darwin.go new file mode 100644 index 000000000000..5d42dec62240 --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/default_store_darwin.go @@ -0,0 +1,5 @@ +package credentials + +func defaultCredentialsStore() string { + return "osxkeychain" +} diff --git a/vendor/github.com/docker/cli/config/credentials/default_store_linux.go b/vendor/github.com/docker/cli/config/credentials/default_store_linux.go new file mode 100644 index 000000000000..a9012c6d4a8f --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/default_store_linux.go @@ -0,0 +1,13 @@ +package credentials + +import ( + "os/exec" +) + +func defaultCredentialsStore() string { + if _, err := exec.LookPath("pass"); err == nil { + return "pass" + } + + return "secretservice" +} diff --git a/vendor/github.com/docker/cli/config/credentials/default_store_unsupported.go b/vendor/github.com/docker/cli/config/credentials/default_store_unsupported.go new file mode 100644 index 000000000000..40c16eb837d9 --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/default_store_unsupported.go @@ -0,0 +1,7 @@ +//go:build !windows && !darwin && !linux + +package credentials + +func defaultCredentialsStore() string { + return "" +} diff --git a/vendor/github.com/docker/cli/config/credentials/default_store_windows.go b/vendor/github.com/docker/cli/config/credentials/default_store_windows.go new file mode 100644 index 000000000000..bb799ca61b79 --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/default_store_windows.go @@ -0,0 +1,5 @@ +package credentials + +func defaultCredentialsStore() string { + return "wincred" +} diff --git a/vendor/github.com/docker/cli/config/credentials/file_store.go b/vendor/github.com/docker/cli/config/credentials/file_store.go new file mode 100644 index 000000000000..e0ff09dbbe82 --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/file_store.go @@ -0,0 +1,118 @@ +package credentials + +import ( + "fmt" + "net" + "net/url" + "os" + "strings" + "sync/atomic" + + "github.com/docker/cli/config/types" +) + +type store interface { + Save() error + GetAuthConfigs() map[string]types.AuthConfig + GetFilename() string +} + +// fileStore implements a credentials store using +// the docker configuration file to keep the credentials in plain text. +type fileStore struct { + file store +} + +// NewFileStore creates a new file credentials store. +func NewFileStore(file store) Store { + return &fileStore{file: file} +} + +// Erase removes the given credentials from the file store.This function is +// idempotent and does not update the file if credentials did not change. +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() +} + +// Get retrieves credentials for a specific server from the file store. +func (c *fileStore) Get(serverAddress string) (types.AuthConfig, error) { + authConfig, ok := c.file.GetAuthConfigs()[serverAddress] + if !ok { + // Maybe they have a legacy config file, we will iterate the keys converting + // them to the new format and testing + for r, ac := range c.file.GetAuthConfigs() { + if serverAddress == ConvertToHostname(r) { + return ac, nil + } + } + + authConfig = types.AuthConfig{} + } + return authConfig, nil +} + +func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) { + return c.file.GetAuthConfigs(), nil +} + +// unencryptedWarning warns the user when using an insecure credential storage. +// After a deprecation period, user will get prompted if stdin and stderr are a terminal. +// Otherwise, we'll assume they want it (sadly), because people may have been scripting +// insecure logins and we don't want to break them. Maybe they'll see the warning in their +// logs and fix things. +const unencryptedWarning = ` +WARNING! Your credentials are stored unencrypted in '%s'. +Configure a credential helper to remove this warning. See +https://docs.docker.com/go/credential-store/ +` + +// alreadyPrinted ensures that we only print the unencryptedWarning once per +// 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. 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 + } + + if !alreadyPrinted.Load() && authConfig.Password != "" { + // Display a warning if we're storing the users password (not a token). + // + // FIXME(thaJeztah): make output configurable instead of hardcoding to os.Stderr + _, _ = fmt.Fprintln(os.Stderr, fmt.Sprintf(unencryptedWarning, c.file.GetFilename())) + alreadyPrinted.Store(true) + } + + return nil +} + +// ConvertToHostname converts a registry url which has http|https prepended +// to just an hostname. +// Copied from github.com/docker/docker/registry.ConvertToHostname to reduce dependencies. +func ConvertToHostname(maybeURL string) string { + stripped := maybeURL + if strings.Contains(stripped, "://") { + u, err := url.Parse(stripped) + if err == nil && u.Hostname() != "" { + if u.Port() == "" { + return u.Hostname() + } + return net.JoinHostPort(u.Hostname(), u.Port()) + } + } + hostName, _, _ := strings.Cut(stripped, "/") + return hostName +} diff --git a/vendor/github.com/docker/cli/config/credentials/native_store.go b/vendor/github.com/docker/cli/config/credentials/native_store.go new file mode 100644 index 000000000000..9516cb7684fd --- /dev/null +++ b/vendor/github.com/docker/cli/config/credentials/native_store.go @@ -0,0 +1,147 @@ +package credentials + +import ( + "github.com/docker/cli/config/types" + "github.com/docker/docker-credential-helpers/client" + "github.com/docker/docker-credential-helpers/credentials" +) + +const ( + remoteCredentialsPrefix = "docker-credential-" //nolint:gosec // ignore G101: Potential hardcoded credentials + tokenUsername = "" +) + +// nativeStore implements a credentials store +// using native keychain to keep credentials secure. +// It piggybacks into a file store to keep users' emails. +type nativeStore struct { + programFunc client.ProgramFunc + fileStore Store +} + +// NewNativeStore creates a new native store that +// uses a remote helper program to manage credentials. +func NewNativeStore(file store, helperSuffix string) Store { + name := remoteCredentialsPrefix + helperSuffix + return &nativeStore{ + programFunc: client.NewShellProgramFunc(name), + fileStore: NewFileStore(file), + } +} + +// Erase removes the given credentials from the native store. +func (c *nativeStore) Erase(serverAddress string) error { + if err := client.Erase(c.programFunc, serverAddress); err != nil { + return err + } + + // Fallback to plain text store to remove email + return c.fileStore.Erase(serverAddress) +} + +// Get retrieves credentials for a specific server from the native store. +func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) { + // load user email if it exist or an empty auth config. + auth, _ := c.fileStore.Get(serverAddress) + + creds, err := c.getCredentialsFromStore(serverAddress) + if err != nil { + return auth, err + } + auth.Username = creds.Username + auth.IdentityToken = creds.IdentityToken + auth.Password = creds.Password + auth.ServerAddress = creds.ServerAddress + + return auth, nil +} + +// GetAll retrieves all the credentials from the native store. +func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) { + auths, err := c.listCredentialsInStore() + if err != nil { + return nil, err + } + + // Emails are only stored in the file store. + // This call can be safely eliminated when emails are removed. + fileConfigs, _ := c.fileStore.GetAll() + + authConfigs := make(map[string]types.AuthConfig) + for registry := range auths { + creds, err := c.getCredentialsFromStore(registry) + if err != nil { + return nil, err + } + ac := fileConfigs[registry] // might contain Email + ac.Username = creds.Username + ac.Password = creds.Password + ac.IdentityToken = creds.IdentityToken + if ac.ServerAddress == "" { + ac.ServerAddress = creds.ServerAddress + } + authConfigs[registry] = ac + } + + return authConfigs, nil +} + +// Store saves the given credentials in the file store. +func (c *nativeStore) Store(authConfig types.AuthConfig) error { + if err := c.storeCredentialsInStore(authConfig); err != nil { + return err + } + authConfig.Username = "" + authConfig.Password = "" + authConfig.IdentityToken = "" + + // Fallback to old credential in plain text to save only the email + return c.fileStore.Store(authConfig) +} + +// storeCredentialsInStore executes the command to store the credentials in the native store. +func (c *nativeStore) storeCredentialsInStore(config types.AuthConfig) error { + creds := &credentials.Credentials{ + ServerURL: config.ServerAddress, + Username: config.Username, + Secret: config.Password, + } + + if config.IdentityToken != "" { + creds.Username = tokenUsername + creds.Secret = config.IdentityToken + } + + return client.Store(c.programFunc, creds) +} + +// getCredentialsFromStore executes the command to get the credentials from the native store. +func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthConfig, error) { + var ret types.AuthConfig + + creds, err := client.Get(c.programFunc, serverAddress) + if err != nil { + if credentials.IsErrCredentialsNotFound(err) { + // do not return an error if the credentials are not + // in the keychain. Let docker ask for new credentials. + return ret, nil + } + return ret, err + } + + if creds.Username == tokenUsername { + ret.IdentityToken = creds.Secret + } else { + ret.Password = creds.Secret + ret.Username = creds.Username + } + + ret.ServerAddress = serverAddress + return ret, nil +} + +// listCredentialsInStore returns a listing of stored credentials as a map of +// URL -> username. +func (c *nativeStore) listCredentialsInStore() (map[string]string, error) { + return client.List(c.programFunc) +} diff --git a/vendor/github.com/docker/cli/config/memorystore/store.go b/vendor/github.com/docker/cli/config/memorystore/store.go new file mode 100644 index 000000000000..e2510a6180a6 --- /dev/null +++ b/vendor/github.com/docker/cli/config/memorystore/store.go @@ -0,0 +1,126 @@ +//go:build go1.23 + +package memorystore + +import ( + "errors" + "fmt" + "maps" + "os" + "sync" + + "github.com/docker/cli/config/credentials" + "github.com/docker/cli/config/types" +) + +var errValueNotFound = errors.New("value not found") + +func IsErrValueNotFound(err error) bool { + return errors.Is(err, errValueNotFound) +} + +type Config struct { + lock sync.RWMutex + memoryCredentials map[string]types.AuthConfig + fallbackStore credentials.Store +} + +func (e *Config) Erase(serverAddress string) error { + e.lock.Lock() + defer e.lock.Unlock() + delete(e.memoryCredentials, serverAddress) + + if e.fallbackStore != nil { + err := e.fallbackStore.Erase(serverAddress) + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, "memorystore: ", err) + } + } + + return nil +} + +func (e *Config) Get(serverAddress string) (types.AuthConfig, error) { + e.lock.RLock() + defer e.lock.RUnlock() + authConfig, ok := e.memoryCredentials[serverAddress] + if !ok { + if e.fallbackStore != nil { + return e.fallbackStore.Get(serverAddress) + } + return types.AuthConfig{}, errValueNotFound + } + return authConfig, nil +} + +func (e *Config) GetAll() (map[string]types.AuthConfig, error) { + e.lock.RLock() + defer e.lock.RUnlock() + creds := make(map[string]types.AuthConfig) + + if e.fallbackStore != nil { + fileCredentials, err := e.fallbackStore.GetAll() + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, "memorystore: ", err) + } else { + creds = fileCredentials + } + } + + maps.Copy(creds, e.memoryCredentials) + return creds, nil +} + +func (e *Config) Store(authConfig types.AuthConfig) error { + e.lock.Lock() + defer e.lock.Unlock() + e.memoryCredentials[authConfig.ServerAddress] = authConfig + + if e.fallbackStore != nil { + return e.fallbackStore.Store(authConfig) + } + return nil +} + +// WithFallbackStore sets a fallback store. +// +// Write operations will be performed on both the memory store and the +// fallback store. +// +// Read operations will first check the memory store, and if the credential +// is not found, it will then check the fallback store. +// +// Retrieving all credentials will return from both the memory store and the +// fallback store, merging the results from both stores into a single map. +// +// Data stored in the memory store will take precedence over data in the +// fallback store. +func WithFallbackStore(store credentials.Store) Options { + return func(s *Config) error { + s.fallbackStore = store + return nil + } +} + +// WithAuthConfig allows to set the initial credentials in the memory store. +func WithAuthConfig(config map[string]types.AuthConfig) Options { + return func(s *Config) error { + s.memoryCredentials = config + return nil + } +} + +type Options func(*Config) error + +// New creates a new in memory credential store +func New(opts ...Options) (credentials.Store, error) { + m := &Config{ + memoryCredentials: make(map[string]types.AuthConfig), + } + for _, opt := range opts { + if err := opt(m); err != nil { + return nil, err + } + } + return m, nil +} diff --git a/vendor/github.com/docker/cli/config/types/authconfig.go b/vendor/github.com/docker/cli/config/types/authconfig.go new file mode 100644 index 000000000000..056af6b84259 --- /dev/null +++ b/vendor/github.com/docker/cli/config/types/authconfig.go @@ -0,0 +1,22 @@ +package types + +// AuthConfig contains authorization information for connecting to a Registry +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // Email is an optional value associated with the username. + // This field is deprecated and will be removed in a later + // version of docker. + Email string `json:"email,omitempty"` + + ServerAddress string `json:"serveraddress,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry + RegistryToken string `json:"registrytoken,omitempty"` +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f4115c073f7d..5bf0ce5428a5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -48,6 +48,13 @@ github.com/distribution/reference ## explicit; go 1.23.0 github.com/docker/cli-docs-tool github.com/docker/cli-docs-tool/annotation +# github.com/docker/cli/config v0.0.0 => ./config +## explicit; go 1.24 +github.com/docker/cli/config +github.com/docker/cli/config/configfile +github.com/docker/cli/config/credentials +github.com/docker/cli/config/memorystore +github.com/docker/cli/config/types # github.com/docker/distribution v2.8.3+incompatible ## explicit github.com/docker/distribution @@ -571,5 +578,6 @@ gotest.tools/v3/skip # tags.cncf.io/container-device-interface v0.8.0 ## explicit; go 1.20 tags.cncf.io/container-device-interface/pkg/parser +# github.com/docker/cli/config => ./config # github.com/moby/moby/api => github.com/moby/moby/api v0.0.0-20250724140036-49306c607b72 # github.com/moby/moby/client => github.com/moby/moby/client v0.0.0-20250724140036-49306c607b72