Merge pull request #3878 from thaJeztah/current_context_improvements

context: don't validate context when looking up name
This commit is contained in:
Sebastiaan van Stijn 2022-11-22 17:06:20 +01:00 committed by GitHub
commit 2080390f5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 46 deletions

View File

@ -28,7 +28,6 @@ import (
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -212,6 +211,9 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
if opts.Debug { if opts.Debug {
debug.Enable() debug.Enable()
} }
if opts.Context != "" && len(opts.Hosts) > 0 {
return errors.New("conflicting options: either specify --host or --context, not both")
}
cli.loadConfigFile() cli.loadConfigFile()
@ -222,10 +224,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
return ResolveDefaultContext(opts, cli.contextStoreConfig) return ResolveDefaultContext(opts, cli.contextStoreConfig)
}, },
} }
cli.currentContext, err = resolveContextName(opts, cli.configFile, cli.contextStore) cli.currentContext = resolveContextName(opts, cli.configFile)
if err != nil {
return err
}
cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext) cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext)
if err != nil { if err != nil {
return errors.Wrap(err, "unable to resolve docker endpoint") return errors.Wrap(err, "unable to resolve docker endpoint")
@ -243,6 +242,10 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
// NewAPIClientFromFlags creates a new APIClient from command line flags // NewAPIClientFromFlags creates a new APIClient from command line flags
func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
if opts.Context != "" && len(opts.Hosts) > 0 {
return nil, errors.New("conflicting options: either specify --host or --context, not both")
}
storeConfig := DefaultContextStoreConfig() storeConfig := DefaultContextStoreConfig()
contextStore := &ContextStoreWithDefault{ contextStore := &ContextStoreWithDefault{
Store: store.New(config.ContextStoreDir(), storeConfig), Store: store.New(config.ContextStoreDir(), storeConfig),
@ -250,11 +253,7 @@ func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.
return ResolveDefaultContext(opts, storeConfig) return ResolveDefaultContext(opts, storeConfig)
}, },
} }
contextName, err := resolveContextName(opts, configFile, contextStore) endpoint, err := resolveDockerEndpoint(contextStore, resolveContextName(opts, configFile))
if err != nil {
return nil, err
}
endpoint, err := resolveDockerEndpoint(contextStore, contextName)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to resolve docker endpoint") return nil, errors.Wrap(err, "unable to resolve docker endpoint")
} }
@ -363,11 +362,63 @@ func (cli *DockerCli) ContextStore() store.Store {
return cli.contextStore return cli.contextStore
} }
// CurrentContext returns the current context name // CurrentContext returns the current context name, based on flags,
// environment variables and the cli configuration file, in the following
// order of preference:
//
// 1. The "--context" command-line option.
// 2. The "DOCKER_CONTEXT" environment variable.
// 3. The current context as configured through the in "currentContext"
// field in the CLI configuration file ("~/.docker/config.json").
// 4. If no context is configured, use the "default" context.
//
// # Fallbacks for backward-compatibility
//
// To preserve backward-compatibility with the "pre-contexts" behavior,
// the "default" context is used if:
//
// - The "--host" option is set
// - The "DOCKER_HOST" ([DefaultContextName]) environment variable is set
// to a non-empty value.
//
// In these cases, the default context is used, which uses the host as
// specified in "DOCKER_HOST", and TLS config from flags/env vars.
//
// Setting both the "--context" and "--host" flags is ambiguous and results
// in an error when the cli is started.
//
// CurrentContext does not validate if the given context exists or if it's
// valid; errors may occur when trying to use it.
func (cli *DockerCli) CurrentContext() string { func (cli *DockerCli) CurrentContext() string {
return cli.currentContext return cli.currentContext
} }
// CurrentContext returns the current context name, based on flags,
// environment variables and the cli configuration file. It does not
// validate if the given context exists or if it's valid; errors may
// occur when trying to use it.
//
// Refer to [DockerCli.CurrentContext] above for further details.
func resolveContextName(opts *cliflags.ClientOptions, config *configfile.ConfigFile) string {
if opts != nil && opts.Context != "" {
return opts.Context
}
if opts != nil && len(opts.Hosts) > 0 {
return DefaultContextName
}
if os.Getenv(client.EnvOverrideHost) != "" {
return DefaultContextName
}
if ctxName := os.Getenv("DOCKER_CONTEXT"); ctxName != "" {
return ctxName
}
if config != nil && config.CurrentContext != "" {
// We don't validate if this context exists: errors may occur when trying to use it.
return config.CurrentContext
}
return DefaultContextName
}
// DockerEndpoint returns the current docker endpoint // DockerEndpoint returns the current docker endpoint
func (cli *DockerCli) DockerEndpoint() docker.Endpoint { func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
return cli.dockerEndpoint return cli.dockerEndpoint
@ -437,40 +488,6 @@ func UserAgent() string {
return "Docker-Client/" + version.Version + " (" + runtime.GOOS + ")" return "Docker-Client/" + version.Version + " (" + runtime.GOOS + ")"
} }
// resolveContextName resolves the current context name with the following rules:
// - setting both --context and --host flags is ambiguous
// - if --context is set, use this value
// - if --host flag or DOCKER_HOST (client.EnvOverrideHost) is set, fallbacks to use the same logic as before context-store was added
// for backward compatibility with existing scripts
// - if DOCKER_CONTEXT is set, use this value
// - if Config file has a globally set "CurrentContext", use this value
// - fallbacks to default HOST, uses TLS config from flags/env vars
func resolveContextName(opts *cliflags.ClientOptions, config *configfile.ConfigFile, contextstore store.Reader) (string, error) {
if opts.Context != "" && len(opts.Hosts) > 0 {
return "", errors.New("Conflicting options: either specify --host or --context, not both")
}
if opts.Context != "" {
return opts.Context, nil
}
if len(opts.Hosts) > 0 {
return DefaultContextName, nil
}
if os.Getenv(client.EnvOverrideHost) != "" {
return DefaultContextName, nil
}
if ctxName := os.Getenv("DOCKER_CONTEXT"); ctxName != "" {
return ctxName, nil
}
if config != nil && config.CurrentContext != "" {
_, err := contextstore.GetMetadata(config.CurrentContext)
if errdefs.IsNotFound(err) {
return "", errors.Errorf("current context %q is not found on the file system, please check your config file at %s", config.CurrentContext, config.Filename)
}
return config.CurrentContext, err
}
return DefaultContextName, nil
}
var defaultStoreEndpoints = []store.NamedTypeGetter{ var defaultStoreEndpoints = []store.NamedTypeGetter{
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }), store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
} }

View File

@ -200,7 +200,8 @@ func TestInitializeFromClientHangs(t *testing.T) {
go func() { go func() {
cli := &DockerCli{client: apiClient, initTimeout: time.Millisecond} cli := &DockerCli{client: apiClient, initTimeout: time.Millisecond}
cli.Initialize(flags.NewClientOptions()) err := cli.Initialize(flags.NewClientOptions())
assert.Check(t, err)
close(initializedCh) close(initializedCh)
}() }()