mirror of https://github.com/docker/cli.git
Merge pull request #3790 from thaJeztah/context_cleanup
context: various cleanups and improvements
This commit is contained in:
commit
a5c8d39a15
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -462,8 +463,8 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
|
|||
}
|
||||
if config != nil && config.CurrentContext != "" {
|
||||
_, err := contextstore.GetMetadata(config.CurrentContext)
|
||||
if store.IsErrContextDoesNotExist(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)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/docker/cli/cli/command/formatter/tabwriter"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -127,7 +128,7 @@ func checkContextNameForCreation(s store.Reader, name string) error {
|
|||
if err := store.ValidateContextName(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.GetMetadata(name); !store.IsErrContextDoesNotExist(err) {
|
||||
if _, err := s.GetMetadata(name); !errdefs.IsNotFound(err) {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error while getting existing contexts")
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error
|
|||
if name == "default" {
|
||||
errs = append(errs, `default: context "default" cannot be removed`)
|
||||
} else if err := doRemove(dockerCli, name, name == currentCtx, opts.Force); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %s", name, err))
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error
|
|||
func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error {
|
||||
if isCurrent {
|
||||
if !force {
|
||||
return errors.New("context is in use, set -f flag to force remove")
|
||||
return errors.Errorf("context %q is in use, set -f flag to force remove", name)
|
||||
}
|
||||
// fallback to DOCKER_HOST
|
||||
cfg := dockerCli.ConfigFile()
|
||||
|
|
|
@ -6,8 +6,9 @@ import (
|
|||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
|
@ -18,7 +19,7 @@ func TestRemove(t *testing.T) {
|
|||
_, err := cli.ContextStore().GetMetadata("current")
|
||||
assert.NilError(t, err)
|
||||
_, err = cli.ContextStore().GetMetadata("other")
|
||||
assert.Check(t, store.IsErrContextDoesNotExist(err))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
||||
func TestRemoveNotAContext(t *testing.T) {
|
||||
|
@ -38,7 +39,7 @@ func TestRemoveCurrent(t *testing.T) {
|
|||
createTestContext(t, cli, "other")
|
||||
cli.SetCurrentContext("current")
|
||||
err := RunRemove(cli, RemoveOptions{}, []string{"current"})
|
||||
assert.ErrorContains(t, err, "current: context is in use, set -f flag to force remove")
|
||||
assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`)
|
||||
}
|
||||
|
||||
func TestRemoveCurrentForce(t *testing.T) {
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
|
@ -47,7 +47,7 @@ func TestUse(t *testing.T) {
|
|||
func TestUseNoExist(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
err := newUseCommand(cli).RunE(nil, []string{"test"})
|
||||
assert.Check(t, store.IsErrContextDoesNotExist(err))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
||||
// TestUseDefaultWithoutConfigFile verifies that the CLI does not create
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -107,7 +106,7 @@ func (s *ContextStoreWithDefault) List() ([]store.Metadata, error) {
|
|||
// CreateOrUpdate is not allowed for the default context and fails
|
||||
func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
|
||||
if meta.Name == DefaultContextName {
|
||||
return errors.New("default context cannot be created nor updated")
|
||||
return errdefs.InvalidParameter(errors.New("default context cannot be created nor updated"))
|
||||
}
|
||||
return s.Store.CreateOrUpdate(meta)
|
||||
}
|
||||
|
@ -115,7 +114,7 @@ func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
|
|||
// Remove is not allowed for the default context and fails
|
||||
func (s *ContextStoreWithDefault) Remove(name string) error {
|
||||
if name == DefaultContextName {
|
||||
return errors.New("default context cannot be removed")
|
||||
return errdefs.InvalidParameter(errors.New("default context cannot be removed"))
|
||||
}
|
||||
return s.Store.Remove(name)
|
||||
}
|
||||
|
@ -135,7 +134,7 @@ func (s *ContextStoreWithDefault) GetMetadata(name string) (store.Metadata, erro
|
|||
// ResetTLSMaterial is not implemented for default context and fails
|
||||
func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.ContextTLSData) error {
|
||||
if name == DefaultContextName {
|
||||
return errors.New("The default context store does not support ResetTLSMaterial")
|
||||
return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
|
||||
}
|
||||
return s.Store.ResetTLSMaterial(name, data)
|
||||
}
|
||||
|
@ -143,7 +142,7 @@ func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.Cont
|
|||
// ResetEndpointTLSMaterial is not implemented for default context and fails
|
||||
func (s *ContextStoreWithDefault) ResetEndpointTLSMaterial(contextName string, endpointName string, data *store.EndpointTLSData) error {
|
||||
if contextName == DefaultContextName {
|
||||
return errors.New("The default context store does not support ResetEndpointTLSMaterial")
|
||||
return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
|
||||
}
|
||||
return s.Store.ResetEndpointTLSMaterial(contextName, endpointName, data)
|
||||
}
|
||||
|
@ -176,29 +175,13 @@ func (s *ContextStoreWithDefault) GetTLSData(contextName, endpointName, fileName
|
|||
return nil, err
|
||||
}
|
||||
if defaultContext.TLS.Endpoints[endpointName].Files[fileName] == nil {
|
||||
return nil, &noDefaultTLSDataError{endpointName: endpointName, fileName: fileName}
|
||||
return nil, errdefs.NotFound(errors.Errorf("TLS data for %s/%s/%s does not exist", DefaultContextName, endpointName, fileName))
|
||||
}
|
||||
return defaultContext.TLS.Endpoints[endpointName].Files[fileName], nil
|
||||
|
||||
}
|
||||
return s.Store.GetTLSData(contextName, endpointName, fileName)
|
||||
}
|
||||
|
||||
type noDefaultTLSDataError struct {
|
||||
endpointName string
|
||||
fileName string
|
||||
}
|
||||
|
||||
func (e *noDefaultTLSDataError) Error() string {
|
||||
return fmt.Sprintf("tls data for %s/%s/%s does not exist", DefaultContextName, e.endpointName, e.fileName)
|
||||
}
|
||||
|
||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||
func (e *noDefaultTLSDataError) NotFound() {}
|
||||
|
||||
// IsTLSDataDoesNotExist satisfies github.com/docker/cli/cli/context/store.tlsDataDoesNotExist
|
||||
func (e *noDefaultTLSDataError) IsTLSDataDoesNotExist() {}
|
||||
|
||||
// GetStorageInfo implements store.Store's GetStorageInfo
|
||||
func (s *ContextStoreWithDefault) GetStorageInfo(contextName string) store.StorageInfo {
|
||||
if contextName == DefaultContextName {
|
||||
|
|
|
@ -8,8 +8,10 @@ import (
|
|||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/golden"
|
||||
)
|
||||
|
||||
|
@ -153,6 +155,7 @@ func TestErrCreateDefault(t *testing.T) {
|
|||
Metadata: testContext{Bar: "baz"},
|
||||
Name: "default",
|
||||
})
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||
assert.Error(t, err, "default context cannot be created nor updated")
|
||||
}
|
||||
|
||||
|
@ -160,6 +163,7 @@ func TestErrRemoveDefault(t *testing.T) {
|
|||
meta := testDefaultMetadata()
|
||||
s := testStore(t, meta, store.ContextTLSData{})
|
||||
err := s.Remove("default")
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||
assert.Error(t, err, "default context cannot be removed")
|
||||
}
|
||||
|
||||
|
@ -167,5 +171,5 @@ func TestErrTLSDataError(t *testing.T) {
|
|||
meta := testDefaultMetadata()
|
||||
s := testStore(t, meta, store.ContextTLSData{})
|
||||
_, err := s.GetTLSData("default", "noop", "noop")
|
||||
assert.Check(t, store.IsErrTLSDataDoesNotExist(err))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
@ -22,7 +23,7 @@ func testMetadata(name string) Metadata {
|
|||
func TestMetadataGetNotExisting(t *testing.T) {
|
||||
testee := metadataStore{root: t.TempDir(), config: testCfg}
|
||||
_, err := testee.get("noexist")
|
||||
assert.Assert(t, IsErrContextDoesNotExist(err))
|
||||
assert.ErrorType(t, err, errdefs.IsNotFound)
|
||||
}
|
||||
|
||||
func TestMetadataCreateGetRemove(t *testing.T) {
|
||||
|
@ -41,7 +42,7 @@ func TestMetadataCreateGetRemove(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
// create a new instance to check it does not depend on some sort of state
|
||||
testee = metadataStore{root: testDir, config: testCfg}
|
||||
meta, err := testee.get(contextdirOf("test-context"))
|
||||
meta, err := testee.get("test-context")
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, meta, testMeta)
|
||||
|
||||
|
@ -49,14 +50,14 @@ func TestMetadataCreateGetRemove(t *testing.T) {
|
|||
|
||||
err = testee.createOrUpdate(expected2)
|
||||
assert.NilError(t, err)
|
||||
meta, err = testee.get(contextdirOf("test-context"))
|
||||
meta, err = testee.get("test-context")
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, meta, expected2)
|
||||
|
||||
assert.NilError(t, testee.remove(contextdirOf("test-context")))
|
||||
assert.NilError(t, testee.remove(contextdirOf("test-context"))) // support duplicate remove
|
||||
_, err = testee.get(contextdirOf("test-context"))
|
||||
assert.Assert(t, IsErrContextDoesNotExist(err))
|
||||
assert.NilError(t, testee.remove("test-context"))
|
||||
assert.NilError(t, testee.remove("test-context")) // support duplicate remove
|
||||
_, err = testee.get("test-context")
|
||||
assert.ErrorType(t, err, errdefs.IsNotFound)
|
||||
}
|
||||
|
||||
func TestMetadataRespectJsonAnnotation(t *testing.T) {
|
||||
|
@ -121,7 +122,7 @@ func TestWithEmbedding(t *testing.T) {
|
|||
},
|
||||
}
|
||||
assert.NilError(t, testee.createOrUpdate(Metadata{Metadata: testCtxMeta, Name: "test"}))
|
||||
res, err := testee.get(contextdirOf("test"))
|
||||
res, err := testee.get("test")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, testCtxMeta, res.Metadata)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,14 @@ package store
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -55,11 +56,21 @@ func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
|
|||
return reflect.ValueOf(typed).Elem().Interface(), nil
|
||||
}
|
||||
|
||||
func (s *metadataStore) get(id contextdir) (Metadata, error) {
|
||||
contextDir := s.contextDir(id)
|
||||
bytes, err := os.ReadFile(filepath.Join(contextDir, metaFile))
|
||||
func (s *metadataStore) get(name string) (Metadata, error) {
|
||||
m, err := s.getByID(contextdirOf(name))
|
||||
if err != nil {
|
||||
return Metadata{}, convertContextDoesNotExist(err)
|
||||
return m, errors.Wrapf(err, "load context %q", name)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
|
||||
bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile))
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context does not exist"))
|
||||
}
|
||||
return Metadata{}, err
|
||||
}
|
||||
var untyped untypedContextMetadata
|
||||
r := Metadata{
|
||||
|
@ -80,9 +91,11 @@ func (s *metadataStore) get(id contextdir) (Metadata, error) {
|
|||
return r, err
|
||||
}
|
||||
|
||||
func (s *metadataStore) remove(id contextdir) error {
|
||||
contextDir := s.contextDir(id)
|
||||
return os.RemoveAll(contextDir)
|
||||
func (s *metadataStore) remove(name string) error {
|
||||
if err := os.RemoveAll(s.contextDir(contextdirOf(name))); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove metadata")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *metadataStore) list() ([]Metadata, error) {
|
||||
|
@ -95,9 +108,12 @@ func (s *metadataStore) list() ([]Metadata, error) {
|
|||
}
|
||||
var res []Metadata
|
||||
for _, dir := range ctxDirs {
|
||||
c, err := s.get(contextdir(dir))
|
||||
c, err := s.getByID(contextdir(dir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrap(err, "failed to read metadata")
|
||||
}
|
||||
res = append(res, c)
|
||||
}
|
||||
|
@ -131,20 +147,13 @@ func listRecursivelyMetadataDirs(root string) ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
for _, s := range subs {
|
||||
result = append(result, fmt.Sprintf("%s/%s", fi.Name(), s))
|
||||
result = append(result, filepath.Join(fi.Name(), s))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func convertContextDoesNotExist(err error) error {
|
||||
if os.IsNotExist(err) {
|
||||
return &contextDoesNotExistError{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type untypedContextMetadata struct {
|
||||
Metadata json.RawMessage `json:"metadata,omitempty"`
|
||||
Endpoints map[string]json.RawMessage `json:"endpoints,omitempty"`
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"bytes"
|
||||
_ "crypto/sha256" // ensure ids can be computed
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
|
@ -94,11 +93,11 @@ type ContextTLSData struct {
|
|||
|
||||
// New creates a store from a given directory.
|
||||
// If the directory does not exist or is empty, initialize it
|
||||
func New(dir string, cfg Config) Store {
|
||||
func New(dir string, cfg Config) *ContextStore {
|
||||
metaRoot := filepath.Join(dir, metadataDir)
|
||||
tlsRoot := filepath.Join(dir, tlsDir)
|
||||
|
||||
return &store{
|
||||
return &ContextStore{
|
||||
meta: &metadataStore{
|
||||
root: metaRoot,
|
||||
config: cfg,
|
||||
|
@ -109,12 +108,14 @@ func New(dir string, cfg Config) Store {
|
|||
}
|
||||
}
|
||||
|
||||
type store struct {
|
||||
// ContextStore implements Store.
|
||||
type ContextStore struct {
|
||||
meta *metadataStore
|
||||
tls *tlsStore
|
||||
}
|
||||
|
||||
func (s *store) List() ([]Metadata, error) {
|
||||
// List return all contexts.
|
||||
func (s *ContextStore) List() ([]Metadata, error) {
|
||||
return s.meta.list()
|
||||
}
|
||||
|
||||
|
@ -131,73 +132,82 @@ func Names(s Lister) ([]string, error) {
|
|||
return names, nil
|
||||
}
|
||||
|
||||
func (s *store) CreateOrUpdate(meta Metadata) error {
|
||||
// CreateOrUpdate creates or updates metadata for the context.
|
||||
func (s *ContextStore) CreateOrUpdate(meta Metadata) error {
|
||||
return s.meta.createOrUpdate(meta)
|
||||
}
|
||||
|
||||
func (s *store) Remove(name string) error {
|
||||
id := contextdirOf(name)
|
||||
if err := s.meta.remove(id); err != nil {
|
||||
return patchErrContextName(err, name)
|
||||
// Remove deletes the context with the given name, if found.
|
||||
func (s *ContextStore) Remove(name string) error {
|
||||
if err := s.meta.remove(name); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove context %s", name)
|
||||
}
|
||||
return patchErrContextName(s.tls.removeAllContextData(id), name)
|
||||
if err := s.tls.remove(name); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove context %s", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) GetMetadata(name string) (Metadata, error) {
|
||||
res, err := s.meta.get(contextdirOf(name))
|
||||
patchErrContextName(err, name)
|
||||
return res, err
|
||||
// GetMetadata returns the metadata for the context with the given name.
|
||||
// It returns an errdefs.ErrNotFound if the context was not found.
|
||||
func (s *ContextStore) GetMetadata(name string) (Metadata, error) {
|
||||
return s.meta.get(name)
|
||||
}
|
||||
|
||||
func (s *store) ResetTLSMaterial(name string, data *ContextTLSData) error {
|
||||
id := contextdirOf(name)
|
||||
if err := s.tls.removeAllContextData(id); err != nil {
|
||||
return patchErrContextName(err, name)
|
||||
// ResetTLSMaterial removes TLS data for all endpoints in the context and replaces
|
||||
// it with the new data.
|
||||
func (s *ContextStore) ResetTLSMaterial(name string, data *ContextTLSData) error {
|
||||
if err := s.tls.remove(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
for ep, files := range data.Endpoints {
|
||||
for fileName, data := range files.Files {
|
||||
if err := s.tls.createOrUpdate(id, ep, fileName, data); err != nil {
|
||||
return patchErrContextName(err, name)
|
||||
if err := s.tls.createOrUpdate(name, ep, fileName, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) ResetEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error {
|
||||
id := contextdirOf(contextName)
|
||||
if err := s.tls.removeAllEndpointData(id, endpointName); err != nil {
|
||||
return patchErrContextName(err, contextName)
|
||||
// ResetEndpointTLSMaterial removes TLS data for the given context and endpoint,
|
||||
// and replaces it with the new data.
|
||||
func (s *ContextStore) ResetEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error {
|
||||
if err := s.tls.removeEndpoint(contextName, endpointName); err != nil {
|
||||
return err
|
||||
}
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
for fileName, data := range data.Files {
|
||||
if err := s.tls.createOrUpdate(id, endpointName, fileName, data); err != nil {
|
||||
return patchErrContextName(err, contextName)
|
||||
if err := s.tls.createOrUpdate(contextName, endpointName, fileName, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) ListTLSFiles(name string) (map[string]EndpointFiles, error) {
|
||||
res, err := s.tls.listContextData(contextdirOf(name))
|
||||
return res, patchErrContextName(err, name)
|
||||
// ListTLSFiles returns the list of TLS files present for each endpoint in the
|
||||
// context.
|
||||
func (s *ContextStore) ListTLSFiles(name string) (map[string]EndpointFiles, error) {
|
||||
return s.tls.listContextData(name)
|
||||
}
|
||||
|
||||
func (s *store) GetTLSData(contextName, endpointName, fileName string) ([]byte, error) {
|
||||
res, err := s.tls.getData(contextdirOf(contextName), endpointName, fileName)
|
||||
return res, patchErrContextName(err, contextName)
|
||||
// GetTLSData reads, and returns the content of the given fileName for an endpoint.
|
||||
// It returns an errdefs.ErrNotFound if the file was not found.
|
||||
func (s *ContextStore) GetTLSData(contextName, endpointName, fileName string) ([]byte, error) {
|
||||
return s.tls.getData(contextName, endpointName, fileName)
|
||||
}
|
||||
|
||||
func (s *store) GetStorageInfo(contextName string) StorageInfo {
|
||||
dir := contextdirOf(contextName)
|
||||
// GetStorageInfo returns the paths where the Metadata and TLS data are stored
|
||||
// for the context.
|
||||
func (s *ContextStore) GetStorageInfo(contextName string) StorageInfo {
|
||||
return StorageInfo{
|
||||
MetadataPath: s.meta.contextDir(dir),
|
||||
TLSPath: s.tls.contextDir(dir),
|
||||
MetadataPath: s.meta.contextDir(contextdirOf(contextName)),
|
||||
TLSPath: s.tls.contextDir(contextName),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +220,7 @@ func ValidateContextName(name string) error {
|
|||
return errors.New(`"default" is a reserved context name`)
|
||||
}
|
||||
if !restrictedNameRegEx.MatchString(name) {
|
||||
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
|
||||
return errors.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -484,58 +494,18 @@ func importEndpointTLS(tlsData *ContextTLSData, path string, data []byte) error
|
|||
return nil
|
||||
}
|
||||
|
||||
type setContextName interface {
|
||||
setContext(name string)
|
||||
}
|
||||
|
||||
type contextDoesNotExistError struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *contextDoesNotExistError) Error() string {
|
||||
return fmt.Sprintf("context %q does not exist", e.name)
|
||||
}
|
||||
|
||||
func (e *contextDoesNotExistError) setContext(name string) {
|
||||
e.name = name
|
||||
}
|
||||
|
||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||
func (e *contextDoesNotExistError) NotFound() {}
|
||||
|
||||
type tlsDataDoesNotExist interface {
|
||||
errdefs.ErrNotFound
|
||||
IsTLSDataDoesNotExist()
|
||||
}
|
||||
|
||||
type tlsDataDoesNotExistError struct {
|
||||
context, endpoint, file string
|
||||
}
|
||||
|
||||
func (e *tlsDataDoesNotExistError) Error() string {
|
||||
return fmt.Sprintf("tls data for %s/%s/%s does not exist", e.context, e.endpoint, e.file)
|
||||
}
|
||||
|
||||
func (e *tlsDataDoesNotExistError) setContext(name string) {
|
||||
e.context = name
|
||||
}
|
||||
|
||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||
func (e *tlsDataDoesNotExistError) NotFound() {}
|
||||
|
||||
// IsTLSDataDoesNotExist satisfies tlsDataDoesNotExist
|
||||
func (e *tlsDataDoesNotExistError) IsTLSDataDoesNotExist() {}
|
||||
|
||||
// IsErrContextDoesNotExist checks if the given error is a "context does not exist" condition
|
||||
// IsErrContextDoesNotExist checks if the given error is a "context does not exist" condition.
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/errdefs.IsNotFound()
|
||||
func IsErrContextDoesNotExist(err error) bool {
|
||||
_, ok := err.(*contextDoesNotExistError)
|
||||
return ok
|
||||
return errdefs.IsNotFound(err)
|
||||
}
|
||||
|
||||
// IsErrTLSDataDoesNotExist checks if the given error is a "context does not exist" condition
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/errdefs.IsNotFound()
|
||||
func IsErrTLSDataDoesNotExist(err error) bool {
|
||||
_, ok := err.(tlsDataDoesNotExist)
|
||||
return ok
|
||||
return errdefs.IsNotFound(err)
|
||||
}
|
||||
|
||||
type contextdir string
|
||||
|
@ -543,10 +513,3 @@ type contextdir string
|
|||
func contextdirOf(name string) contextdir {
|
||||
return contextdir(digest.FromString(name).Encoded())
|
||||
}
|
||||
|
||||
func patchErrContextName(err error, name string) error {
|
||||
if typed, ok := err.(setContextName); ok {
|
||||
typed.setContext(name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@ import (
|
|||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
type endpoint struct {
|
||||
|
@ -100,7 +102,7 @@ func TestRemove(t *testing.T) {
|
|||
}))
|
||||
assert.NilError(t, s.Remove("source"))
|
||||
_, err = s.GetMetadata("source")
|
||||
assert.Check(t, IsErrContextDoesNotExist(err))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
f, err := s.ListTLSFiles("source")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, 0, len(f))
|
||||
|
@ -115,7 +117,7 @@ func TestListEmptyStore(t *testing.T) {
|
|||
func TestErrHasCorrectContext(t *testing.T) {
|
||||
_, err := New(t.TempDir(), testCfg).GetMetadata("no-exists")
|
||||
assert.ErrorContains(t, err, "no-exists")
|
||||
assert.Check(t, IsErrContextDoesNotExist(err))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
||||
func TestDetectImportContentType(t *testing.T) {
|
||||
|
@ -173,7 +175,7 @@ func TestImportZip(t *testing.T) {
|
|||
Name: "source",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
var files = []struct {
|
||||
files := []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"meta.json", string(meta)},
|
||||
|
|
|
@ -19,7 +19,7 @@ func EndpointTypeGetter(name string, getter TypeGetter) NamedTypeGetter {
|
|||
}
|
||||
}
|
||||
|
||||
// Config is used to configure the metadata marshaler of the context store
|
||||
// Config is used to configure the metadata marshaler of the context ContextStore
|
||||
type Config struct {
|
||||
contextType TypeGetter
|
||||
endpointTypes map[string]TypeGetter
|
||||
|
|
|
@ -6,10 +6,12 @@ import (
|
|||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
type testCtx struct{}
|
||||
type testEP1 struct{}
|
||||
type testEP2 struct{}
|
||||
type testEP3 struct{}
|
||||
type (
|
||||
testCtx struct{}
|
||||
testEP1 struct{}
|
||||
testEP2 struct{}
|
||||
testEP3 struct{}
|
||||
)
|
||||
|
||||
func TestConfigModification(t *testing.T) {
|
||||
cfg := NewConfig(func() interface{} { return &testCtx{} }, EndpointTypeGetter("ep1", func() interface{} { return &testEP1{} }))
|
||||
|
|
|
@ -3,6 +3,9 @@ package store
|
|||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const tlsDir = "tls"
|
||||
|
@ -11,69 +14,70 @@ type tlsStore struct {
|
|||
root string
|
||||
}
|
||||
|
||||
func (s *tlsStore) contextDir(id contextdir) string {
|
||||
return filepath.Join(s.root, string(id))
|
||||
func (s *tlsStore) contextDir(name string) string {
|
||||
return filepath.Join(s.root, string(contextdirOf(name)))
|
||||
}
|
||||
|
||||
func (s *tlsStore) endpointDir(contextID contextdir, name string) string {
|
||||
return filepath.Join(s.root, string(contextID), name)
|
||||
func (s *tlsStore) endpointDir(name, endpointName string) string {
|
||||
return filepath.Join(s.contextDir(name), endpointName)
|
||||
}
|
||||
|
||||
func (s *tlsStore) filePath(contextID contextdir, endpointName, filename string) string {
|
||||
return filepath.Join(s.root, string(contextID), endpointName, filename)
|
||||
}
|
||||
|
||||
func (s *tlsStore) createOrUpdate(contextID contextdir, endpointName, filename string, data []byte) error {
|
||||
epdir := s.endpointDir(contextID, endpointName)
|
||||
func (s *tlsStore) createOrUpdate(name, endpointName, filename string, data []byte) error {
|
||||
parentOfRoot := filepath.Dir(s.root)
|
||||
if err := os.MkdirAll(parentOfRoot, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(epdir, 0700); err != nil {
|
||||
endpointDir := s.endpointDir(name, endpointName)
|
||||
if err := os.MkdirAll(endpointDir, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(s.filePath(contextID, endpointName, filename), data, 0600)
|
||||
return os.WriteFile(filepath.Join(endpointDir, filename), data, 0600)
|
||||
}
|
||||
|
||||
func (s *tlsStore) getData(contextID contextdir, endpointName, filename string) ([]byte, error) {
|
||||
data, err := os.ReadFile(s.filePath(contextID, endpointName, filename))
|
||||
func (s *tlsStore) getData(name, endpointName, filename string) ([]byte, error) {
|
||||
data, err := os.ReadFile(filepath.Join(s.endpointDir(name, endpointName), filename))
|
||||
if err != nil {
|
||||
return nil, convertTLSDataDoesNotExist(endpointName, filename, err)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, errdefs.NotFound(errors.Errorf("TLS data for %s/%s/%s does not exist", name, endpointName, filename))
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to read TLS data for endpoint %s", endpointName)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error {
|
||||
err := os.Remove(s.filePath(contextID, endpointName, filename))
|
||||
if os.IsNotExist(err) {
|
||||
// remove deletes all TLS data for the given context.
|
||||
func (s *tlsStore) remove(name string) error {
|
||||
if err := os.RemoveAll(s.contextDir(name)); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove TLS data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
||||
func (s *tlsStore) removeEndpoint(name, endpointName string) error {
|
||||
if err := os.RemoveAll(s.endpointDir(name, endpointName)); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove TLS data for endpoint %s", endpointName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tlsStore) removeAllEndpointData(contextID contextdir, endpointName string) error {
|
||||
return os.RemoveAll(s.endpointDir(contextID, endpointName))
|
||||
}
|
||||
|
||||
func (s *tlsStore) removeAllContextData(contextID contextdir) error {
|
||||
return os.RemoveAll(s.contextDir(contextID))
|
||||
}
|
||||
|
||||
func (s *tlsStore) listContextData(contextID contextdir) (map[string]EndpointFiles, error) {
|
||||
epFSs, err := os.ReadDir(s.contextDir(contextID))
|
||||
func (s *tlsStore) listContextData(name string) (map[string]EndpointFiles, error) {
|
||||
contextDir := s.contextDir(name)
|
||||
epFSs, err := os.ReadDir(contextDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return map[string]EndpointFiles{}, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "failed to list TLS files for context %s", name)
|
||||
}
|
||||
r := make(map[string]EndpointFiles)
|
||||
for _, epFS := range epFSs {
|
||||
if epFS.IsDir() {
|
||||
epDir := s.endpointDir(contextID, epFS.Name())
|
||||
fss, err := os.ReadDir(epDir)
|
||||
fss, err := os.ReadDir(filepath.Join(contextDir, epFS.Name()))
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "failed to list TLS files for endpoint %s", epFS.Name())
|
||||
}
|
||||
var files EndpointFiles
|
||||
for _, fs := range fss {
|
||||
|
@ -89,10 +93,3 @@ func (s *tlsStore) listContextData(contextID contextdir) (map[string]EndpointFil
|
|||
|
||||
// EndpointFiles is a slice of strings representing file names
|
||||
type EndpointFiles []string
|
||||
|
||||
func convertTLSDataDoesNotExist(endpoint, file string, err error) error {
|
||||
if os.IsNotExist(err) {
|
||||
return &tlsDataDoesNotExistError{endpoint: endpoint, file: file}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -3,32 +3,33 @@ package store
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestTlsCreateUpdateGetRemove(t *testing.T) {
|
||||
testee := tlsStore{root: t.TempDir()}
|
||||
_, err := testee.getData("test-ctx", "test-ep", "test-data")
|
||||
assert.Equal(t, true, IsErrTLSDataDoesNotExist(err))
|
||||
|
||||
err = testee.createOrUpdate("test-ctx", "test-ep", "test-data", []byte("data"))
|
||||
const contextName = "test-ctx"
|
||||
|
||||
_, err := testee.getData(contextName, "test-ep", "test-data")
|
||||
assert.ErrorType(t, err, errdefs.IsNotFound)
|
||||
|
||||
err = testee.createOrUpdate(contextName, "test-ep", "test-data", []byte("data"))
|
||||
assert.NilError(t, err)
|
||||
data, err := testee.getData("test-ctx", "test-ep", "test-data")
|
||||
data, err := testee.getData(contextName, "test-ep", "test-data")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(data), "data")
|
||||
err = testee.createOrUpdate("test-ctx", "test-ep", "test-data", []byte("data2"))
|
||||
err = testee.createOrUpdate(contextName, "test-ep", "test-data", []byte("data2"))
|
||||
assert.NilError(t, err)
|
||||
data, err = testee.getData("test-ctx", "test-ep", "test-data")
|
||||
data, err = testee.getData(contextName, "test-ep", "test-data")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(data), "data2")
|
||||
|
||||
err = testee.remove("test-ctx", "test-ep", "test-data")
|
||||
err = testee.removeEndpoint(contextName, "test-ep")
|
||||
assert.NilError(t, err)
|
||||
err = testee.remove("test-ctx", "test-ep", "test-data")
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = testee.getData("test-ctx", "test-ep", "test-data")
|
||||
assert.Equal(t, true, IsErrTLSDataDoesNotExist(err))
|
||||
_, err = testee.getData(contextName, "test-ep", "test-data")
|
||||
assert.ErrorType(t, err, errdefs.IsNotFound)
|
||||
}
|
||||
|
||||
func TestTlsListAndBatchRemove(t *testing.T) {
|
||||
|
@ -45,26 +46,27 @@ func TestTlsListAndBatchRemove(t *testing.T) {
|
|||
"ep2": {"f1", "f2", "f3"},
|
||||
}
|
||||
|
||||
const contextName = "test-ctx"
|
||||
for name, files := range all {
|
||||
for _, file := range files {
|
||||
err := testee.createOrUpdate("test-ctx", name, file, []byte("data"))
|
||||
err := testee.createOrUpdate(contextName, name, file, []byte("data"))
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
resAll, err := testee.listContextData("test-ctx")
|
||||
resAll, err := testee.listContextData(contextName)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, resAll, all)
|
||||
|
||||
err = testee.removeAllEndpointData("test-ctx", "ep3")
|
||||
err = testee.removeEndpoint(contextName, "ep3")
|
||||
assert.NilError(t, err)
|
||||
resEp1ep2, err := testee.listContextData("test-ctx")
|
||||
resEp1ep2, err := testee.listContextData(contextName)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, resEp1ep2, ep1ep2)
|
||||
|
||||
err = testee.removeAllContextData("test-ctx")
|
||||
err = testee.remove(contextName)
|
||||
assert.NilError(t, err)
|
||||
resEmpty, err := testee.listContextData("test-ctx")
|
||||
resEmpty, err := testee.listContextData(contextName)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, resEmpty, map[string]EndpointFiles{})
|
||||
}
|
||||
|
|
|
@ -45,14 +45,14 @@ func (data *TLSData) ToStoreTLSData() *store.EndpointTLSData {
|
|||
func LoadTLSData(s store.Reader, contextName, endpointName string) (*TLSData, error) {
|
||||
tlsFiles, err := s.ListTLSFiles(contextName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to retrieve context tls files for context %q", contextName)
|
||||
return nil, errors.Wrapf(err, "failed to retrieve TLS files for context %q", contextName)
|
||||
}
|
||||
if epTLSFiles, ok := tlsFiles[endpointName]; ok {
|
||||
var tlsData TLSData
|
||||
for _, f := range epTLSFiles {
|
||||
data, err := s.GetTLSData(contextName, endpointName, f)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to retrieve context tls data for file %q of context %q", f, contextName)
|
||||
return nil, errors.Wrapf(err, "failed to retrieve TLS data (%s) for context %q", f, contextName)
|
||||
}
|
||||
switch f {
|
||||
case caKey:
|
||||
|
@ -62,7 +62,7 @@ func LoadTLSData(s store.Reader, contextName, endpointName string) (*TLSData, er
|
|||
case keyKey:
|
||||
tlsData.Key = data
|
||||
default:
|
||||
logrus.Warnf("unknown file %s in context %s tls bundle", f, contextName)
|
||||
logrus.Warnf("unknown file in context %s TLS bundle: %s", contextName, f)
|
||||
}
|
||||
}
|
||||
return &tlsData, nil
|
||||
|
|
Loading…
Reference in New Issue