Skip to content

Commit b3aa171

Browse files
author
Jean-Christophe Sirot
committed
Make default context behaves like a real context:
- when using "--context default" parameter - when printing the list of contexts - when exporting the default context to a tarball Signed-off-by: Jean-Christophe Sirot <jean-christophe.sirot@docker.com> (+1 squashed commit) Squashed commits: [2067049] Fix CLI initialization for the `docker stack deploy --help` command and ensure that the dockerCli.CurrentContext() always returns a non empty context name (default as a fallback) Remove now obsolete code handling empty string context name Minor code cleanup Signed-off-by: Jean-Christophe Sirot <jean-christophe.sirot@docker.com>
1 parent 86a5a48 commit b3aa171

File tree

18 files changed

+575
-106
lines changed

18 files changed

+575
-106
lines changed

cli/command/cli.go

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package command
33
import (
44
"context"
55
"io"
6+
"io/ioutil"
67
"os"
78
"path/filepath"
89
"runtime"
@@ -209,12 +210,18 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
209210

210211
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)
211212

212-
cli.contextStore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
213+
baseContextSore := store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
214+
cli.contextStore = &ContextStoreWithDefault{
215+
Store: baseContextSore,
216+
Resolver: func() (*DefaultContext, error) {
217+
return resolveDefaultContext(opts.Common, cli.ConfigFile(), cli.Err())
218+
},
219+
}
213220
cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
214221
if err != nil {
215222
return err
216223
}
217-
cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext, opts.Common)
224+
cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext)
218225
if err != nil {
219226
return errors.Wrap(err, "unable to resolve docker endpoint")
220227
}
@@ -252,12 +259,17 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
252259

253260
// NewAPIClientFromFlags creates a new APIClient from command line flags
254261
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
255-
store := store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig())
262+
store := &ContextStoreWithDefault{
263+
Store: store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig()),
264+
Resolver: func() (*DefaultContext, error) {
265+
return resolveDefaultContext(opts, configFile, ioutil.Discard)
266+
},
267+
}
256268
contextName, err := resolveContextName(opts, configFile, store)
257269
if err != nil {
258270
return nil, err
259271
}
260-
endpoint, err := resolveDockerEndpoint(store, contextName, opts)
272+
endpoint, err := resolveDockerEndpoint(store, contextName)
261273
if err != nil {
262274
return nil, errors.Wrap(err, "unable to resolve docker endpoint")
263275
}
@@ -278,18 +290,20 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
278290
return client.NewClientWithOpts(clientOpts...)
279291
}
280292

281-
func resolveDockerEndpoint(s store.Store, contextName string, opts *cliflags.CommonOptions) (docker.Endpoint, error) {
282-
if contextName != "" {
283-
ctxMeta, err := s.GetContextMetadata(contextName)
284-
if err != nil {
285-
return docker.Endpoint{}, err
286-
}
287-
epMeta, err := docker.EndpointFromContext(ctxMeta)
288-
if err != nil {
289-
return docker.Endpoint{}, err
290-
}
291-
return docker.WithTLSData(s, contextName, epMeta)
293+
func resolveDockerEndpoint(s store.Store, contextName string) (docker.Endpoint, error) {
294+
ctxMeta, err := s.GetContextMetadata(contextName)
295+
if err != nil {
296+
return docker.Endpoint{}, err
292297
}
298+
epMeta, err := docker.EndpointFromContext(ctxMeta)
299+
if err != nil {
300+
return docker.Endpoint{}, err
301+
}
302+
return docker.WithTLSData(s, contextName, epMeta)
303+
}
304+
305+
// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
306+
func resolveDefaultDockerEndpoint(opts *cliflags.CommonOptions) (docker.Endpoint, error) {
293307
host, err := getServerHost(opts.Hosts, opts.TLSOptions)
294308
if err != nil {
295309
return docker.Endpoint{}, err
@@ -384,38 +398,21 @@ func (cli *DockerCli) CurrentContext() string {
384398

385399
// StackOrchestrator resolves which stack orchestrator is in use
386400
func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error) {
387-
var ctxOrchestrator string
388-
389-
configFile := cli.configFile
390-
if configFile == nil {
391-
configFile = cliconfig.LoadDefaultConfigFile(cli.Err())
392-
}
393-
394401
currentContext := cli.CurrentContext()
395-
if currentContext == "" {
396-
currentContext = configFile.CurrentContext
402+
ctxRaw, err := cli.ContextStore().GetContextMetadata(currentContext)
403+
if store.IsErrContextDoesNotExist(err) {
404+
// case where the currentContext has been removed (CLI behavior is to fallback to using DOCKER_HOST based resolution)
405+
return GetStackOrchestrator(flagValue, "", cli.ConfigFile().StackOrchestrator, cli.Err())
397406
}
398-
if currentContext != "" {
399-
contextstore := cli.contextStore
400-
if contextstore == nil {
401-
contextstore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
402-
}
403-
ctxRaw, err := contextstore.GetContextMetadata(currentContext)
404-
if store.IsErrContextDoesNotExist(err) {
405-
// case where the currentContext has been removed (CLI behavior is to fallback to using DOCKER_HOST based resolution)
406-
return GetStackOrchestrator(flagValue, "", configFile.StackOrchestrator, cli.Err())
407-
}
408-
if err != nil {
409-
return "", err
410-
}
411-
ctxMeta, err := GetDockerContext(ctxRaw)
412-
if err != nil {
413-
return "", err
414-
}
415-
ctxOrchestrator = string(ctxMeta.StackOrchestrator)
407+
if err != nil {
408+
return "", err
416409
}
417-
418-
return GetStackOrchestrator(flagValue, ctxOrchestrator, configFile.StackOrchestrator, cli.Err())
410+
ctxMeta, err := GetDockerContext(ctxRaw)
411+
if err != nil {
412+
return "", err
413+
}
414+
ctxOrchestrator := string(ctxMeta.StackOrchestrator)
415+
return GetStackOrchestrator(flagValue, ctxOrchestrator, cli.ConfigFile().StackOrchestrator, cli.Err())
419416
}
420417

421418
// DockerEndpoint returns the current docker endpoint
@@ -511,10 +508,10 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
511508
return opts.Context, nil
512509
}
513510
if len(opts.Hosts) > 0 {
514-
return "", nil
511+
return DefaultContextName, nil
515512
}
516513
if _, present := os.LookupEnv("DOCKER_HOST"); present {
517-
return "", nil
514+
return DefaultContextName, nil
518515
}
519516
if ctxName, ok := os.LookupEnv("DOCKER_CONTEXT"); ok {
520517
return ctxName, nil
@@ -526,7 +523,7 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
526523
}
527524
return config.CurrentContext, err
528525
}
529-
return "", nil
526+
return DefaultContextName, nil
530527
}
531528

532529
func defaultContextStoreConfig() store.Config {

cli/command/context/create_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,26 @@ func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) (*test.FakeCli, func
2323
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
2424
store.EndpointTypeGetter(kubernetes.KubernetesEndpoint, func() interface{} { return &kubernetes.EndpointMeta{} }),
2525
)
26-
store := store.New(dir, storeConfig)
26+
store := &command.ContextStoreWithDefault{
27+
Store: store.New(dir, storeConfig),
28+
Resolver: func() (*command.DefaultContext, error) {
29+
return &command.DefaultContext{
30+
Meta: store.ContextMetadata{
31+
Endpoints: map[string]interface{}{
32+
docker.DockerEndpoint: docker.EndpointMeta{
33+
Host: "unix:///var/run/docker.sock",
34+
},
35+
},
36+
Metadata: command.DockerContext{
37+
Description: "",
38+
StackOrchestrator: command.OrchestratorSwarm,
39+
},
40+
Name: command.DefaultContextName,
41+
},
42+
TLS: store.ContextTLSData{},
43+
}, nil
44+
},
45+
}
2746
cleanup := func() {
2847
os.RemoveAll(dir)
2948
}
@@ -52,6 +71,12 @@ func TestCreateInvalids(t *testing.T) {
5271
{
5372
expecterErr: `context name cannot be empty`,
5473
},
74+
{
75+
options: CreateOptions{
76+
Name: "default",
77+
},
78+
expecterErr: `"default" is a reserved context name`,
79+
},
5580
{
5681
options: CreateOptions{
5782
Name: " ",

cli/command/context/export.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error {
7777

7878
// RunExport exports a Docker context
7979
func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
80-
if err := validateContextName(opts.ContextName); err != nil {
80+
if err := validateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName {
8181
return err
8282
}
8383
ctxMeta, err := dockerCli.ContextStore().GetContextMetadata(opts.ContextName)

cli/command/context/inspect.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
4040

4141
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
4242
getRefFunc := func(ref string) (interface{}, []byte, error) {
43-
if ref == "default" {
44-
return nil, nil, errors.New(`context "default" cannot be inspected`)
45-
}
4643
c, err := dockerCli.ContextStore().GetContextMetadata(ref)
4744
if err != nil {
4845
return nil, nil, err

cli/command/context/list.go

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/docker/cli/cli/command/formatter"
1010
"github.com/docker/cli/cli/context/docker"
1111
kubecontext "github.com/docker/cli/cli/context/kubernetes"
12-
"github.com/docker/cli/kubernetes"
1312
"github.com/spf13/cobra"
1413
"vbom.ml/util/sortorder"
1514
)
@@ -61,6 +60,9 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
6160
if kubernetesEndpoint != nil {
6261
kubEndpointText = fmt.Sprintf("%s (%s)", kubernetesEndpoint.Host, kubernetesEndpoint.DefaultNamespace)
6362
}
63+
if rawMeta.Name == command.DefaultContextName {
64+
meta.Description = "Current DOCKER_HOST based configuration"
65+
}
6466
desc := formatter.ClientContext{
6567
Name: rawMeta.Name,
6668
Current: rawMeta.Name == curContext,
@@ -71,29 +73,6 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
7173
}
7274
contexts = append(contexts, &desc)
7375
}
74-
if !opts.quiet {
75-
desc := &formatter.ClientContext{
76-
Name: "default",
77-
Description: "Current DOCKER_HOST based configuration",
78-
}
79-
if dockerCli.CurrentContext() == "" {
80-
orchestrator, _ := dockerCli.StackOrchestrator("")
81-
kubEndpointText := ""
82-
kubeconfig := kubernetes.NewKubernetesConfig("")
83-
if cfg, err := kubeconfig.ClientConfig(); err == nil {
84-
ns, _, _ := kubeconfig.Namespace()
85-
if ns == "" {
86-
ns = "default"
87-
}
88-
kubEndpointText = fmt.Sprintf("%s (%s)", cfg.Host, ns)
89-
}
90-
desc.Current = true
91-
desc.StackOrchestrator = string(orchestrator)
92-
desc.DockerEndpoint = dockerCli.DockerEndpoint().Host
93-
desc.KubernetesEndpoint = kubEndpointText
94-
}
95-
contexts = append(contexts, desc)
96-
}
9776
sort.Slice(contexts, func(i, j int) bool {
9877
return sortorder.NaturalLess(contexts[i].Name, contexts[j].Name)
9978
})

cli/command/context/list_test.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"testing"
55

66
"github.com/docker/cli/cli/command"
7-
"github.com/docker/cli/cli/context/docker"
87
"gotest.tools/assert"
98
"gotest.tools/env"
109
"gotest.tools/golden"
@@ -36,20 +35,6 @@ func TestList(t *testing.T) {
3635
golden.Assert(t, cli.OutBuffer().String(), "list.golden")
3736
}
3837

39-
func TestListNoContext(t *testing.T) {
40-
cli, cleanup := makeFakeCli(t)
41-
defer cleanup()
42-
defer env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")()
43-
cli.SetDockerEndpoint(docker.Endpoint{
44-
EndpointMeta: docker.EndpointMeta{
45-
Host: "https://someswarmserver",
46-
},
47-
})
48-
cli.OutBuffer().Reset()
49-
assert.NilError(t, runList(cli, &listOptions{}))
50-
golden.Assert(t, cli.OutBuffer().String(), "list.no-context.golden")
51-
}
52-
5338
func TestListQuiet(t *testing.T) {
5439
cli, cleanup := makeFakeCli(t)
5540
defer cleanup()

cli/command/context/remove_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,12 @@ func TestRemoveCurrentForce(t *testing.T) {
6262
assert.NilError(t, err)
6363
assert.Equal(t, "", reloadedConfig.CurrentContext)
6464
}
65+
66+
func TestRemoveDefault(t *testing.T) {
67+
cli, cleanup := makeFakeCli(t)
68+
defer cleanup()
69+
createTestContextWithKubeAndSwarm(t, cli, "other", "all")
70+
cli.SetCurrentContext("current")
71+
err := RunRemove(cli, RemoveOptions{}, []string{"default"})
72+
assert.ErrorContains(t, err, `default: context "default" cannot be removed`)
73+
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
2-
current * description of current https://someswarmserver https://someserver (default) all
3-
default Current DOCKER_HOST based configuration
4-
other description of other https://someswarmserver https://someserver (default) all
5-
unset description of unset https://someswarmserver https://someserver (default)
1+
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
2+
current * description of current https://someswarmserver https://someserver (default) all
3+
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
4+
other description of other https://someswarmserver https://someserver (default) all
5+
unset description of unset https://someswarmserver https://someserver (default)

cli/command/context/testdata/list.no-context.golden

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
current
2+
default
23
other

0 commit comments

Comments
 (0)