Merge pull request #3790 from thaJeztah/context_cleanup

context: various cleanups and improvements
This commit is contained in:
Sebastiaan van Stijn 2022-09-30 11:54:39 +02:00 committed by GitHub
commit a5c8d39a15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 186 additions and 220 deletions

View File

@ -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
}

View File

@ -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")
}

View File

@ -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()

View File

@ -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) {

View File

@ -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

View File

@ -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 {

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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"`

View File

@ -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
}

View File

@ -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)},

View File

@ -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

View File

@ -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{} }))

View File

@ -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) {
return nil
// 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 err
return nil
}
func (s *tlsStore) removeAllEndpointData(contextID contextdir, endpointName string) error {
return os.RemoveAll(s.endpointDir(contextID, endpointName))
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) 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
}

View File

@ -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{})
}

View File

@ -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