cli/command/context: context ls: always show current context

if a context is set (e.g. through DOCKER_CONTEXT or the CLI config file), but
wasn't found, then a "stub" context is added, including an error message that
the context doesn't exist.

    DOCKER_CONTEXT=nosuchcontext docker context ls
    NAME              DESCRIPTION                               DOCKER ENDPOINT               ERROR
    default           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
    nosuchcontext *                                                                           context "nosuchcontext": context not found: …

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-11-04 15:48:30 +01:00
parent ed4b0a67be
commit 2c9dff1436
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
6 changed files with 40 additions and 8 deletions

View File

@ -50,10 +50,14 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
} }
var ( var (
curContext = dockerCli.CurrentContext() curContext = dockerCli.CurrentContext()
curFound bool
contexts []*formatter.ClientContext contexts []*formatter.ClientContext
) )
for _, rawMeta := range contextMap { for _, rawMeta := range contextMap {
isCurrent := rawMeta.Name == curContext isCurrent := rawMeta.Name == curContext
if isCurrent {
curFound = true
}
meta, err := command.GetDockerContext(rawMeta) meta, err := command.GetDockerContext(rawMeta)
if err != nil { if err != nil {
// Add a stub-entry to the list, including the error-message // Add a stub-entry to the list, including the error-message
@ -79,6 +83,21 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
} }
contexts = append(contexts, &desc) contexts = append(contexts, &desc)
} }
if !curFound {
// The currently specified context wasn't found. We add a stub-entry
// to the list, including the error-message indicating that the context
// wasn't found.
var errMsg string
_, err := dockerCli.ContextStore().GetMetadata(curContext)
if err != nil {
errMsg = err.Error()
}
contexts = append(contexts, &formatter.ClientContext{
Name: curContext,
Current: true,
Error: errMsg,
})
}
sort.Slice(contexts, func(i, j int) bool { sort.Slice(contexts, func(i, j int) bool {
return sortorder.NaturalLess(contexts[i].Name, contexts[j].Name) return sortorder.NaturalLess(contexts[i].Name, contexts[j].Name)
}) })

View File

@ -39,3 +39,11 @@ func TestListQuiet(t *testing.T) {
assert.NilError(t, runList(cli, &listOptions{quiet: true})) assert.NilError(t, runList(cli, &listOptions{quiet: true}))
golden.Assert(t, cli.OutBuffer().String(), "quiet-list.golden") golden.Assert(t, cli.OutBuffer().String(), "quiet-list.golden")
} }
func TestListError(t *testing.T) {
cli := makeFakeCli(t)
cli.SetCurrentContext("nosuchcontext")
cli.OutBuffer().Reset()
assert.NilError(t, runList(cli, &listOptions{}))
golden.Assert(t, cli.OutBuffer().String(), "list-with-error.golden")
}

View File

@ -0,0 +1,3 @@
NAME DESCRIPTION DOCKER ENDPOINT ERROR
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
nosuchcontext * context "nosuchcontext": context not found: …

View File

@ -1,5 +1,5 @@
NAME DESCRIPTION DOCKER ENDPOINT NAME DESCRIPTION DOCKER ENDPOINT ERROR
current * description of current https://someswarmserver.example.com current * description of current https://someswarmserver.example.com
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
other description of other https://someswarmserver.example.com other description of other https://someswarmserver.example.com
unset description of unset https://someswarmserver.example.com unset description of unset https://someswarmserver.example.com

View File

@ -6,6 +6,8 @@ const (
dockerEndpointHeader = "DOCKER ENDPOINT" dockerEndpointHeader = "DOCKER ENDPOINT"
quietContextFormat = "{{.Name}}" quietContextFormat = "{{.Name}}"
maxErrLength = 45
) )
// NewClientContextFormat returns a Format for rendering using a Context // NewClientContextFormat returns a Format for rendering using a Context
@ -80,7 +82,7 @@ func (c *clientContextContext) DockerEndpoint() string {
// Error returns the truncated error (if any) that occurred when loading the context. // Error returns the truncated error (if any) that occurred when loading the context.
func (c *clientContextContext) Error() string { func (c *clientContextContext) Error() string {
// TODO(thaJeztah) add "--no-trunc" option to context ls and set default to 30 cols to match "docker service ps" // TODO(thaJeztah) add "--no-trunc" option to context ls and set default to 30 cols to match "docker service ps"
return Ellipsis(c.c.Error, 45) return Ellipsis(c.c.Error, maxErrLength)
} }
// KubernetesEndpoint returns the kubernetes endpoint. // KubernetesEndpoint returns the kubernetes endpoint.

View File

@ -59,7 +59,7 @@ func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
func (s *metadataStore) get(name string) (Metadata, error) { func (s *metadataStore) get(name string) (Metadata, error) {
m, err := s.getByID(contextdirOf(name)) m, err := s.getByID(contextdirOf(name))
if err != nil { if err != nil {
return m, errors.Wrapf(err, "load context %q", name) return m, errors.Wrapf(err, "context %q", name)
} }
return m, nil return m, nil
} }
@ -68,7 +68,7 @@ func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile)) bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile))
if err != nil { if err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context does not exist")) return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context not found"))
} }
return Metadata{}, err return Metadata{}, err
} }