Skip to content

Commit a5c8d39

Browse files
authored
Merge pull request #3790 from thaJeztah/context_cleanup
context: various cleanups and improvements
2 parents a496a7d + cd7c493 commit a5c8d39

16 files changed

+186
-220
lines changed

cli/command/cli.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/docker/docker/api/types/registry"
2929
"github.com/docker/docker/api/types/swarm"
3030
"github.com/docker/docker/client"
31+
"github.com/docker/docker/errdefs"
3132
"github.com/docker/go-connections/tlsconfig"
3233
"github.com/pkg/errors"
3334
"github.com/spf13/cobra"
@@ -462,8 +463,8 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
462463
}
463464
if config != nil && config.CurrentContext != "" {
464465
_, err := contextstore.GetMetadata(config.CurrentContext)
465-
if store.IsErrContextDoesNotExist(err) {
466-
return "", errors.Errorf("Current context %q is not found on the file system, please check your config file at %s", config.CurrentContext, config.Filename)
466+
if errdefs.IsNotFound(err) {
467+
return "", errors.Errorf("current context %q is not found on the file system, please check your config file at %s", config.CurrentContext, config.Filename)
467468
}
468469
return config.CurrentContext, err
469470
}

cli/command/context/create.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/docker/cli/cli/command/formatter/tabwriter"
1111
"github.com/docker/cli/cli/context/docker"
1212
"github.com/docker/cli/cli/context/store"
13+
"github.com/docker/docker/errdefs"
1314
"github.com/pkg/errors"
1415
"github.com/spf13/cobra"
1516
)
@@ -127,7 +128,7 @@ func checkContextNameForCreation(s store.Reader, name string) error {
127128
if err := store.ValidateContextName(name); err != nil {
128129
return err
129130
}
130-
if _, err := s.GetMetadata(name); !store.IsErrContextDoesNotExist(err) {
131+
if _, err := s.GetMetadata(name); !errdefs.IsNotFound(err) {
131132
if err != nil {
132133
return errors.Wrap(err, "error while getting existing contexts")
133134
}

cli/command/context/remove.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error
4040
if name == "default" {
4141
errs = append(errs, `default: context "default" cannot be removed`)
4242
} else if err := doRemove(dockerCli, name, name == currentCtx, opts.Force); err != nil {
43-
errs = append(errs, fmt.Sprintf("%s: %s", name, err))
43+
errs = append(errs, err.Error())
4444
} else {
4545
fmt.Fprintln(dockerCli.Out(), name)
4646
}
@@ -54,7 +54,7 @@ func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error
5454
func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error {
5555
if isCurrent {
5656
if !force {
57-
return errors.New("context is in use, set -f flag to force remove")
57+
return errors.Errorf("context %q is in use, set -f flag to force remove", name)
5858
}
5959
// fallback to DOCKER_HOST
6060
cfg := dockerCli.ConfigFile()

cli/command/context/remove_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import (
66

77
"github.com/docker/cli/cli/config"
88
"github.com/docker/cli/cli/config/configfile"
9-
"github.com/docker/cli/cli/context/store"
9+
"github.com/docker/docker/errdefs"
1010
"gotest.tools/v3/assert"
11+
is "gotest.tools/v3/assert/cmp"
1112
)
1213

1314
func TestRemove(t *testing.T) {
@@ -18,7 +19,7 @@ func TestRemove(t *testing.T) {
1819
_, err := cli.ContextStore().GetMetadata("current")
1920
assert.NilError(t, err)
2021
_, err = cli.ContextStore().GetMetadata("other")
21-
assert.Check(t, store.IsErrContextDoesNotExist(err))
22+
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
2223
}
2324

2425
func TestRemoveNotAContext(t *testing.T) {
@@ -38,7 +39,7 @@ func TestRemoveCurrent(t *testing.T) {
3839
createTestContext(t, cli, "other")
3940
cli.SetCurrentContext("current")
4041
err := RunRemove(cli, RemoveOptions{}, []string{"current"})
41-
assert.ErrorContains(t, err, "current: context is in use, set -f flag to force remove")
42+
assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`)
4243
}
4344

4445
func TestRemoveCurrentForce(t *testing.T) {

cli/command/context/use_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import (
1111
"github.com/docker/cli/cli/command"
1212
"github.com/docker/cli/cli/config"
1313
"github.com/docker/cli/cli/config/configfile"
14-
"github.com/docker/cli/cli/context/store"
1514
"github.com/docker/cli/cli/flags"
15+
"github.com/docker/docker/errdefs"
1616
"github.com/docker/docker/pkg/homedir"
1717
"gotest.tools/v3/assert"
1818
is "gotest.tools/v3/assert/cmp"
@@ -47,7 +47,7 @@ func TestUse(t *testing.T) {
4747
func TestUseNoExist(t *testing.T) {
4848
cli := makeFakeCli(t)
4949
err := newUseCommand(cli).RunE(nil, []string{"test"})
50-
assert.Check(t, store.IsErrContextDoesNotExist(err))
50+
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
5151
}
5252

5353
// TestUseDefaultWithoutConfigFile verifies that the CLI does not create

cli/command/defaultcontextstore.go

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package command
22

33
import (
4-
"fmt"
5-
64
"github.com/docker/cli/cli/context/docker"
75
"github.com/docker/cli/cli/context/store"
86
cliflags "github.com/docker/cli/cli/flags"
7+
"github.com/docker/docker/errdefs"
98
"github.com/pkg/errors"
109
)
1110

@@ -107,15 +106,15 @@ func (s *ContextStoreWithDefault) List() ([]store.Metadata, error) {
107106
// CreateOrUpdate is not allowed for the default context and fails
108107
func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
109108
if meta.Name == DefaultContextName {
110-
return errors.New("default context cannot be created nor updated")
109+
return errdefs.InvalidParameter(errors.New("default context cannot be created nor updated"))
111110
}
112111
return s.Store.CreateOrUpdate(meta)
113112
}
114113

115114
// Remove is not allowed for the default context and fails
116115
func (s *ContextStoreWithDefault) Remove(name string) error {
117116
if name == DefaultContextName {
118-
return errors.New("default context cannot be removed")
117+
return errdefs.InvalidParameter(errors.New("default context cannot be removed"))
119118
}
120119
return s.Store.Remove(name)
121120
}
@@ -135,15 +134,15 @@ func (s *ContextStoreWithDefault) GetMetadata(name string) (store.Metadata, erro
135134
// ResetTLSMaterial is not implemented for default context and fails
136135
func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.ContextTLSData) error {
137136
if name == DefaultContextName {
138-
return errors.New("The default context store does not support ResetTLSMaterial")
137+
return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
139138
}
140139
return s.Store.ResetTLSMaterial(name, data)
141140
}
142141

143142
// ResetEndpointTLSMaterial is not implemented for default context and fails
144143
func (s *ContextStoreWithDefault) ResetEndpointTLSMaterial(contextName string, endpointName string, data *store.EndpointTLSData) error {
145144
if contextName == DefaultContextName {
146-
return errors.New("The default context store does not support ResetEndpointTLSMaterial")
145+
return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
147146
}
148147
return s.Store.ResetEndpointTLSMaterial(contextName, endpointName, data)
149148
}
@@ -176,29 +175,13 @@ func (s *ContextStoreWithDefault) GetTLSData(contextName, endpointName, fileName
176175
return nil, err
177176
}
178177
if defaultContext.TLS.Endpoints[endpointName].Files[fileName] == nil {
179-
return nil, &noDefaultTLSDataError{endpointName: endpointName, fileName: fileName}
178+
return nil, errdefs.NotFound(errors.Errorf("TLS data for %s/%s/%s does not exist", DefaultContextName, endpointName, fileName))
180179
}
181180
return defaultContext.TLS.Endpoints[endpointName].Files[fileName], nil
182-
183181
}
184182
return s.Store.GetTLSData(contextName, endpointName, fileName)
185183
}
186184

187-
type noDefaultTLSDataError struct {
188-
endpointName string
189-
fileName string
190-
}
191-
192-
func (e *noDefaultTLSDataError) Error() string {
193-
return fmt.Sprintf("tls data for %s/%s/%s does not exist", DefaultContextName, e.endpointName, e.fileName)
194-
}
195-
196-
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
197-
func (e *noDefaultTLSDataError) NotFound() {}
198-
199-
// IsTLSDataDoesNotExist satisfies github.com/docker/cli/cli/context/store.tlsDataDoesNotExist
200-
func (e *noDefaultTLSDataError) IsTLSDataDoesNotExist() {}
201-
202185
// GetStorageInfo implements store.Store's GetStorageInfo
203186
func (s *ContextStoreWithDefault) GetStorageInfo(contextName string) store.StorageInfo {
204187
if contextName == DefaultContextName {

cli/command/defaultcontextstore_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"github.com/docker/cli/cli/context/docker"
99
"github.com/docker/cli/cli/context/store"
1010
cliflags "github.com/docker/cli/cli/flags"
11+
"github.com/docker/docker/errdefs"
1112
"github.com/docker/go-connections/tlsconfig"
1213
"gotest.tools/v3/assert"
14+
is "gotest.tools/v3/assert/cmp"
1315
"gotest.tools/v3/golden"
1416
)
1517

@@ -153,19 +155,21 @@ func TestErrCreateDefault(t *testing.T) {
153155
Metadata: testContext{Bar: "baz"},
154156
Name: "default",
155157
})
158+
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
156159
assert.Error(t, err, "default context cannot be created nor updated")
157160
}
158161

159162
func TestErrRemoveDefault(t *testing.T) {
160163
meta := testDefaultMetadata()
161164
s := testStore(t, meta, store.ContextTLSData{})
162165
err := s.Remove("default")
166+
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
163167
assert.Error(t, err, "default context cannot be removed")
164168
}
165169

166170
func TestErrTLSDataError(t *testing.T) {
167171
meta := testDefaultMetadata()
168172
s := testStore(t, meta, store.ContextTLSData{})
169173
_, err := s.GetTLSData("default", "noop", "noop")
170-
assert.Check(t, store.IsErrTLSDataDoesNotExist(err))
174+
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
171175
}

cli/context/store/metadata_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"path/filepath"
66
"testing"
77

8+
"github.com/docker/docker/errdefs"
89
"gotest.tools/v3/assert"
910
"gotest.tools/v3/assert/cmp"
1011
)
@@ -22,7 +23,7 @@ func testMetadata(name string) Metadata {
2223
func TestMetadataGetNotExisting(t *testing.T) {
2324
testee := metadataStore{root: t.TempDir(), config: testCfg}
2425
_, err := testee.get("noexist")
25-
assert.Assert(t, IsErrContextDoesNotExist(err))
26+
assert.ErrorType(t, err, errdefs.IsNotFound)
2627
}
2728

2829
func TestMetadataCreateGetRemove(t *testing.T) {
@@ -41,22 +42,22 @@ func TestMetadataCreateGetRemove(t *testing.T) {
4142
assert.NilError(t, err)
4243
// create a new instance to check it does not depend on some sort of state
4344
testee = metadataStore{root: testDir, config: testCfg}
44-
meta, err := testee.get(contextdirOf("test-context"))
45+
meta, err := testee.get("test-context")
4546
assert.NilError(t, err)
4647
assert.DeepEqual(t, meta, testMeta)
4748

4849
// update
4950

5051
err = testee.createOrUpdate(expected2)
5152
assert.NilError(t, err)
52-
meta, err = testee.get(contextdirOf("test-context"))
53+
meta, err = testee.get("test-context")
5354
assert.NilError(t, err)
5455
assert.DeepEqual(t, meta, expected2)
5556

56-
assert.NilError(t, testee.remove(contextdirOf("test-context")))
57-
assert.NilError(t, testee.remove(contextdirOf("test-context"))) // support duplicate remove
58-
_, err = testee.get(contextdirOf("test-context"))
59-
assert.Assert(t, IsErrContextDoesNotExist(err))
57+
assert.NilError(t, testee.remove("test-context"))
58+
assert.NilError(t, testee.remove("test-context")) // support duplicate remove
59+
_, err = testee.get("test-context")
60+
assert.ErrorType(t, err, errdefs.IsNotFound)
6061
}
6162

6263
func TestMetadataRespectJsonAnnotation(t *testing.T) {
@@ -121,7 +122,7 @@ func TestWithEmbedding(t *testing.T) {
121122
},
122123
}
123124
assert.NilError(t, testee.createOrUpdate(Metadata{Metadata: testCtxMeta, Name: "test"}))
124-
res, err := testee.get(contextdirOf("test"))
125+
res, err := testee.get("test")
125126
assert.NilError(t, err)
126127
assert.Equal(t, testCtxMeta, res.Metadata)
127128
}

cli/context/store/metadatastore.go

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package store
22

33
import (
44
"encoding/json"
5-
"fmt"
65
"os"
76
"path/filepath"
87
"reflect"
98
"sort"
109

10+
"github.com/docker/docker/errdefs"
1111
"github.com/fvbommel/sortorder"
12+
"github.com/pkg/errors"
1213
)
1314

1415
const (
@@ -55,11 +56,21 @@ func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
5556
return reflect.ValueOf(typed).Elem().Interface(), nil
5657
}
5758

58-
func (s *metadataStore) get(id contextdir) (Metadata, error) {
59-
contextDir := s.contextDir(id)
60-
bytes, err := os.ReadFile(filepath.Join(contextDir, metaFile))
59+
func (s *metadataStore) get(name string) (Metadata, error) {
60+
m, err := s.getByID(contextdirOf(name))
6161
if err != nil {
62-
return Metadata{}, convertContextDoesNotExist(err)
62+
return m, errors.Wrapf(err, "load context %q", name)
63+
}
64+
return m, nil
65+
}
66+
67+
func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
68+
bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile))
69+
if err != nil {
70+
if errors.Is(err, os.ErrNotExist) {
71+
return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context does not exist"))
72+
}
73+
return Metadata{}, err
6374
}
6475
var untyped untypedContextMetadata
6576
r := Metadata{
@@ -80,9 +91,11 @@ func (s *metadataStore) get(id contextdir) (Metadata, error) {
8091
return r, err
8192
}
8293

83-
func (s *metadataStore) remove(id contextdir) error {
84-
contextDir := s.contextDir(id)
85-
return os.RemoveAll(contextDir)
94+
func (s *metadataStore) remove(name string) error {
95+
if err := os.RemoveAll(s.contextDir(contextdirOf(name))); err != nil {
96+
return errors.Wrapf(err, "failed to remove metadata")
97+
}
98+
return nil
8699
}
87100

88101
func (s *metadataStore) list() ([]Metadata, error) {
@@ -95,9 +108,12 @@ func (s *metadataStore) list() ([]Metadata, error) {
95108
}
96109
var res []Metadata
97110
for _, dir := range ctxDirs {
98-
c, err := s.get(contextdir(dir))
111+
c, err := s.getByID(contextdir(dir))
99112
if err != nil {
100-
return nil, err
113+
if os.IsNotExist(err) {
114+
continue
115+
}
116+
return nil, errors.Wrap(err, "failed to read metadata")
101117
}
102118
res = append(res, c)
103119
}
@@ -131,20 +147,13 @@ func listRecursivelyMetadataDirs(root string) ([]string, error) {
131147
return nil, err
132148
}
133149
for _, s := range subs {
134-
result = append(result, fmt.Sprintf("%s/%s", fi.Name(), s))
150+
result = append(result, filepath.Join(fi.Name(), s))
135151
}
136152
}
137153
}
138154
return result, nil
139155
}
140156

141-
func convertContextDoesNotExist(err error) error {
142-
if os.IsNotExist(err) {
143-
return &contextDoesNotExistError{}
144-
}
145-
return err
146-
}
147-
148157
type untypedContextMetadata struct {
149158
Metadata json.RawMessage `json:"metadata,omitempty"`
150159
Endpoints map[string]json.RawMessage `json:"endpoints,omitempty"`

0 commit comments

Comments
 (0)