mirror of https://github.com/docker/cli.git
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: [20670495] 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>
This commit is contained in:
parent
86a5a489f7
commit
b3aa17187f
|
@ -3,6 +3,7 @@ package command
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -209,12 +210,18 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
|
||||||
|
|
||||||
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)
|
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)
|
||||||
|
|
||||||
cli.contextStore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
|
baseContextSore := store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
|
||||||
|
cli.contextStore = &ContextStoreWithDefault{
|
||||||
|
Store: baseContextSore,
|
||||||
|
Resolver: func() (*DefaultContext, error) {
|
||||||
|
return resolveDefaultContext(opts.Common, cli.ConfigFile(), cli.Err())
|
||||||
|
},
|
||||||
|
}
|
||||||
cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
|
cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext, opts.Common)
|
cli.dockerEndpoint, err = resolveDockerEndpoint(cli.contextStore, cli.currentContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "unable to resolve docker endpoint")
|
return errors.Wrap(err, "unable to resolve docker endpoint")
|
||||||
}
|
}
|
||||||
|
@ -252,12 +259,17 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
|
||||||
|
|
||||||
// NewAPIClientFromFlags creates a new APIClient from command line flags
|
// NewAPIClientFromFlags creates a new APIClient from command line flags
|
||||||
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
||||||
store := store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig())
|
store := &ContextStoreWithDefault{
|
||||||
|
Store: store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig()),
|
||||||
|
Resolver: func() (*DefaultContext, error) {
|
||||||
|
return resolveDefaultContext(opts, configFile, ioutil.Discard)
|
||||||
|
},
|
||||||
|
}
|
||||||
contextName, err := resolveContextName(opts, configFile, store)
|
contextName, err := resolveContextName(opts, configFile, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
endpoint, err := resolveDockerEndpoint(store, contextName, opts)
|
endpoint, err := resolveDockerEndpoint(store, contextName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to resolve docker endpoint")
|
return nil, errors.Wrap(err, "unable to resolve docker endpoint")
|
||||||
}
|
}
|
||||||
|
@ -278,8 +290,7 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
|
||||||
return client.NewClientWithOpts(clientOpts...)
|
return client.NewClientWithOpts(clientOpts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveDockerEndpoint(s store.Store, contextName string, opts *cliflags.CommonOptions) (docker.Endpoint, error) {
|
func resolveDockerEndpoint(s store.Store, contextName string) (docker.Endpoint, error) {
|
||||||
if contextName != "" {
|
|
||||||
ctxMeta, err := s.GetContextMetadata(contextName)
|
ctxMeta, err := s.GetContextMetadata(contextName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Endpoint{}, err
|
return docker.Endpoint{}, err
|
||||||
|
@ -289,7 +300,10 @@ func resolveDockerEndpoint(s store.Store, contextName string, opts *cliflags.Com
|
||||||
return docker.Endpoint{}, err
|
return docker.Endpoint{}, err
|
||||||
}
|
}
|
||||||
return docker.WithTLSData(s, contextName, epMeta)
|
return docker.WithTLSData(s, contextName, epMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
|
||||||
|
func resolveDefaultDockerEndpoint(opts *cliflags.CommonOptions) (docker.Endpoint, error) {
|
||||||
host, err := getServerHost(opts.Hosts, opts.TLSOptions)
|
host, err := getServerHost(opts.Hosts, opts.TLSOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Endpoint{}, err
|
return docker.Endpoint{}, err
|
||||||
|
@ -384,26 +398,11 @@ func (cli *DockerCli) CurrentContext() string {
|
||||||
|
|
||||||
// StackOrchestrator resolves which stack orchestrator is in use
|
// StackOrchestrator resolves which stack orchestrator is in use
|
||||||
func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error) {
|
func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error) {
|
||||||
var ctxOrchestrator string
|
|
||||||
|
|
||||||
configFile := cli.configFile
|
|
||||||
if configFile == nil {
|
|
||||||
configFile = cliconfig.LoadDefaultConfigFile(cli.Err())
|
|
||||||
}
|
|
||||||
|
|
||||||
currentContext := cli.CurrentContext()
|
currentContext := cli.CurrentContext()
|
||||||
if currentContext == "" {
|
ctxRaw, err := cli.ContextStore().GetContextMetadata(currentContext)
|
||||||
currentContext = configFile.CurrentContext
|
|
||||||
}
|
|
||||||
if currentContext != "" {
|
|
||||||
contextstore := cli.contextStore
|
|
||||||
if contextstore == nil {
|
|
||||||
contextstore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
|
|
||||||
}
|
|
||||||
ctxRaw, err := contextstore.GetContextMetadata(currentContext)
|
|
||||||
if store.IsErrContextDoesNotExist(err) {
|
if store.IsErrContextDoesNotExist(err) {
|
||||||
// case where the currentContext has been removed (CLI behavior is to fallback to using DOCKER_HOST based resolution)
|
// case where the currentContext has been removed (CLI behavior is to fallback to using DOCKER_HOST based resolution)
|
||||||
return GetStackOrchestrator(flagValue, "", configFile.StackOrchestrator, cli.Err())
|
return GetStackOrchestrator(flagValue, "", cli.ConfigFile().StackOrchestrator, cli.Err())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -412,10 +411,8 @@ func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
ctxOrchestrator = string(ctxMeta.StackOrchestrator)
|
ctxOrchestrator := string(ctxMeta.StackOrchestrator)
|
||||||
}
|
return GetStackOrchestrator(flagValue, ctxOrchestrator, cli.ConfigFile().StackOrchestrator, cli.Err())
|
||||||
|
|
||||||
return GetStackOrchestrator(flagValue, ctxOrchestrator, configFile.StackOrchestrator, cli.Err())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DockerEndpoint returns the current docker endpoint
|
// DockerEndpoint returns the current docker endpoint
|
||||||
|
@ -511,10 +508,10 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
|
||||||
return opts.Context, nil
|
return opts.Context, nil
|
||||||
}
|
}
|
||||||
if len(opts.Hosts) > 0 {
|
if len(opts.Hosts) > 0 {
|
||||||
return "", nil
|
return DefaultContextName, nil
|
||||||
}
|
}
|
||||||
if _, present := os.LookupEnv("DOCKER_HOST"); present {
|
if _, present := os.LookupEnv("DOCKER_HOST"); present {
|
||||||
return "", nil
|
return DefaultContextName, nil
|
||||||
}
|
}
|
||||||
if ctxName, ok := os.LookupEnv("DOCKER_CONTEXT"); ok {
|
if ctxName, ok := os.LookupEnv("DOCKER_CONTEXT"); ok {
|
||||||
return ctxName, nil
|
return ctxName, nil
|
||||||
|
@ -526,7 +523,7 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
|
||||||
}
|
}
|
||||||
return config.CurrentContext, err
|
return config.CurrentContext, err
|
||||||
}
|
}
|
||||||
return "", nil
|
return DefaultContextName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultContextStoreConfig() store.Config {
|
func defaultContextStoreConfig() store.Config {
|
||||||
|
|
|
@ -23,7 +23,26 @@ func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) (*test.FakeCli, func
|
||||||
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
|
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
|
||||||
store.EndpointTypeGetter(kubernetes.KubernetesEndpoint, func() interface{} { return &kubernetes.EndpointMeta{} }),
|
store.EndpointTypeGetter(kubernetes.KubernetesEndpoint, func() interface{} { return &kubernetes.EndpointMeta{} }),
|
||||||
)
|
)
|
||||||
store := store.New(dir, storeConfig)
|
store := &command.ContextStoreWithDefault{
|
||||||
|
Store: store.New(dir, storeConfig),
|
||||||
|
Resolver: func() (*command.DefaultContext, error) {
|
||||||
|
return &command.DefaultContext{
|
||||||
|
Meta: store.ContextMetadata{
|
||||||
|
Endpoints: map[string]interface{}{
|
||||||
|
docker.DockerEndpoint: docker.EndpointMeta{
|
||||||
|
Host: "unix:///var/run/docker.sock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: command.DockerContext{
|
||||||
|
Description: "",
|
||||||
|
StackOrchestrator: command.OrchestratorSwarm,
|
||||||
|
},
|
||||||
|
Name: command.DefaultContextName,
|
||||||
|
},
|
||||||
|
TLS: store.ContextTLSData{},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
os.RemoveAll(dir)
|
os.RemoveAll(dir)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +71,12 @@ func TestCreateInvalids(t *testing.T) {
|
||||||
{
|
{
|
||||||
expecterErr: `context name cannot be empty`,
|
expecterErr: `context name cannot be empty`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
options: CreateOptions{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
expecterErr: `"default" is a reserved context name`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
options: CreateOptions{
|
options: CreateOptions{
|
||||||
Name: " ",
|
Name: " ",
|
||||||
|
|
|
@ -77,7 +77,7 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error {
|
||||||
|
|
||||||
// RunExport exports a Docker context
|
// RunExport exports a Docker context
|
||||||
func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
|
func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
|
||||||
if err := validateContextName(opts.ContextName); err != nil {
|
if err := validateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctxMeta, err := dockerCli.ContextStore().GetContextMetadata(opts.ContextName)
|
ctxMeta, err := dockerCli.ContextStore().GetContextMetadata(opts.ContextName)
|
||||||
|
|
|
@ -40,9 +40,6 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
|
|
||||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||||
getRefFunc := func(ref string) (interface{}, []byte, error) {
|
getRefFunc := func(ref string) (interface{}, []byte, error) {
|
||||||
if ref == "default" {
|
|
||||||
return nil, nil, errors.New(`context "default" cannot be inspected`)
|
|
||||||
}
|
|
||||||
c, err := dockerCli.ContextStore().GetContextMetadata(ref)
|
c, err := dockerCli.ContextStore().GetContextMetadata(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/cli/cli/context/docker"
|
"github.com/docker/cli/cli/context/docker"
|
||||||
kubecontext "github.com/docker/cli/cli/context/kubernetes"
|
kubecontext "github.com/docker/cli/cli/context/kubernetes"
|
||||||
"github.com/docker/cli/kubernetes"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"vbom.ml/util/sortorder"
|
"vbom.ml/util/sortorder"
|
||||||
)
|
)
|
||||||
|
@ -61,6 +60,9 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
||||||
if kubernetesEndpoint != nil {
|
if kubernetesEndpoint != nil {
|
||||||
kubEndpointText = fmt.Sprintf("%s (%s)", kubernetesEndpoint.Host, kubernetesEndpoint.DefaultNamespace)
|
kubEndpointText = fmt.Sprintf("%s (%s)", kubernetesEndpoint.Host, kubernetesEndpoint.DefaultNamespace)
|
||||||
}
|
}
|
||||||
|
if rawMeta.Name == command.DefaultContextName {
|
||||||
|
meta.Description = "Current DOCKER_HOST based configuration"
|
||||||
|
}
|
||||||
desc := formatter.ClientContext{
|
desc := formatter.ClientContext{
|
||||||
Name: rawMeta.Name,
|
Name: rawMeta.Name,
|
||||||
Current: rawMeta.Name == curContext,
|
Current: rawMeta.Name == curContext,
|
||||||
|
@ -71,29 +73,6 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
||||||
}
|
}
|
||||||
contexts = append(contexts, &desc)
|
contexts = append(contexts, &desc)
|
||||||
}
|
}
|
||||||
if !opts.quiet {
|
|
||||||
desc := &formatter.ClientContext{
|
|
||||||
Name: "default",
|
|
||||||
Description: "Current DOCKER_HOST based configuration",
|
|
||||||
}
|
|
||||||
if dockerCli.CurrentContext() == "" {
|
|
||||||
orchestrator, _ := dockerCli.StackOrchestrator("")
|
|
||||||
kubEndpointText := ""
|
|
||||||
kubeconfig := kubernetes.NewKubernetesConfig("")
|
|
||||||
if cfg, err := kubeconfig.ClientConfig(); err == nil {
|
|
||||||
ns, _, _ := kubeconfig.Namespace()
|
|
||||||
if ns == "" {
|
|
||||||
ns = "default"
|
|
||||||
}
|
|
||||||
kubEndpointText = fmt.Sprintf("%s (%s)", cfg.Host, ns)
|
|
||||||
}
|
|
||||||
desc.Current = true
|
|
||||||
desc.StackOrchestrator = string(orchestrator)
|
|
||||||
desc.DockerEndpoint = dockerCli.DockerEndpoint().Host
|
|
||||||
desc.KubernetesEndpoint = kubEndpointText
|
|
||||||
}
|
|
||||||
contexts = append(contexts, desc)
|
|
||||||
}
|
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/context/docker"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/env"
|
"gotest.tools/env"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
|
@ -36,20 +35,6 @@ func TestList(t *testing.T) {
|
||||||
golden.Assert(t, cli.OutBuffer().String(), "list.golden")
|
golden.Assert(t, cli.OutBuffer().String(), "list.golden")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListNoContext(t *testing.T) {
|
|
||||||
cli, cleanup := makeFakeCli(t)
|
|
||||||
defer cleanup()
|
|
||||||
defer env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")()
|
|
||||||
cli.SetDockerEndpoint(docker.Endpoint{
|
|
||||||
EndpointMeta: docker.EndpointMeta{
|
|
||||||
Host: "https://someswarmserver",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
cli.OutBuffer().Reset()
|
|
||||||
assert.NilError(t, runList(cli, &listOptions{}))
|
|
||||||
golden.Assert(t, cli.OutBuffer().String(), "list.no-context.golden")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListQuiet(t *testing.T) {
|
func TestListQuiet(t *testing.T) {
|
||||||
cli, cleanup := makeFakeCli(t)
|
cli, cleanup := makeFakeCli(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
|
@ -62,3 +62,12 @@ func TestRemoveCurrentForce(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, "", reloadedConfig.CurrentContext)
|
assert.Equal(t, "", reloadedConfig.CurrentContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoveDefault(t *testing.T) {
|
||||||
|
cli, cleanup := makeFakeCli(t)
|
||||||
|
defer cleanup()
|
||||||
|
createTestContextWithKubeAndSwarm(t, cli, "other", "all")
|
||||||
|
cli.SetCurrentContext("current")
|
||||||
|
err := RunRemove(cli, RemoveOptions{}, []string{"default"})
|
||||||
|
assert.ErrorContains(t, err, `default: context "default" cannot be removed`)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
||||||
current * description of current https://someswarmserver https://someserver (default) all
|
current * description of current https://someswarmserver https://someserver (default) all
|
||||||
default Current DOCKER_HOST based configuration
|
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
|
||||||
other description of other https://someswarmserver https://someserver (default) all
|
other description of other https://someswarmserver https://someserver (default) all
|
||||||
unset description of unset https://someswarmserver https://someserver (default)
|
unset description of unset https://someswarmserver https://someserver (default)
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
|
||||||
default * Current DOCKER_HOST based configuration https://someswarmserver https://someserver (default) swarm
|
|
|
@ -1,2 +1,3 @@
|
||||||
current
|
current
|
||||||
|
default
|
||||||
other
|
other
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
|
"github.com/docker/cli/cli/context/docker"
|
||||||
|
"github.com/docker/cli/cli/context/kubernetes"
|
||||||
|
"github.com/docker/cli/cli/context/store"
|
||||||
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultContextName is the name reserved for the default context (config & env based)
|
||||||
|
DefaultContextName = "default"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultContext contains the default context data for all enpoints
|
||||||
|
type DefaultContext struct {
|
||||||
|
Meta store.ContextMetadata
|
||||||
|
TLS store.ContextTLSData
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultContextResolver is a function which resolves the default context base on the configuration and the env variables
|
||||||
|
type DefaultContextResolver func() (*DefaultContext, error)
|
||||||
|
|
||||||
|
// ContextStoreWithDefault implements the store.Store interface with a support for the default context
|
||||||
|
type ContextStoreWithDefault struct {
|
||||||
|
store.Store
|
||||||
|
Resolver DefaultContextResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveDefaultContext creates a ContextMetadata for the current CLI invocation parameters
|
||||||
|
func resolveDefaultContext(opts *cliflags.CommonOptions, config *configfile.ConfigFile, stderr io.Writer) (*DefaultContext, error) {
|
||||||
|
stackOrchestrator, err := GetStackOrchestrator("", "", config.StackOrchestrator, stderr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
contextTLSData := store.ContextTLSData{
|
||||||
|
Endpoints: make(map[string]store.EndpointTLSData),
|
||||||
|
}
|
||||||
|
contextMetadata := store.ContextMetadata{
|
||||||
|
Endpoints: make(map[string]interface{}),
|
||||||
|
Metadata: DockerContext{
|
||||||
|
Description: "",
|
||||||
|
StackOrchestrator: stackOrchestrator,
|
||||||
|
},
|
||||||
|
Name: DefaultContextName,
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerEP, err := resolveDefaultDockerEndpoint(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP.EndpointMeta
|
||||||
|
if dockerEP.TLSData != nil {
|
||||||
|
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerEP.TLSData.ToStoreTLSData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default context uses env-based kubeconfig for Kubernetes endpoint configuration
|
||||||
|
kubeconfig := os.Getenv("KUBECONFIG")
|
||||||
|
if kubeconfig == "" {
|
||||||
|
kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
|
||||||
|
}
|
||||||
|
kubeEP, err := kubernetes.FromKubeConfig(kubeconfig, "", "")
|
||||||
|
if (stackOrchestrator == OrchestratorKubernetes || stackOrchestrator == OrchestratorAll) && err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "default orchestrator is %s but kubernetes endpoint could not be found", stackOrchestrator)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
contextMetadata.Endpoints[kubernetes.KubernetesEndpoint] = kubeEP.EndpointMeta
|
||||||
|
if kubeEP.TLSData != nil {
|
||||||
|
contextTLSData.Endpoints[kubernetes.KubernetesEndpoint] = *kubeEP.TLSData.ToStoreTLSData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DefaultContext{Meta: contextMetadata, TLS: contextTLSData}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListContexts implements store.Store's ListContexts
|
||||||
|
func (s *ContextStoreWithDefault) ListContexts() ([]store.ContextMetadata, error) {
|
||||||
|
contextList, err := s.Store.ListContexts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultContext, err := s.Resolver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return append(contextList, defaultContext.Meta), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrUpdateContext is not allowed for the default context and fails
|
||||||
|
func (s *ContextStoreWithDefault) CreateOrUpdateContext(meta store.ContextMetadata) error {
|
||||||
|
if meta.Name == DefaultContextName {
|
||||||
|
return errors.New("default context cannot be created nor updated")
|
||||||
|
}
|
||||||
|
return s.Store.CreateOrUpdateContext(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveContext is not allowed for the default context and fails
|
||||||
|
func (s *ContextStoreWithDefault) RemoveContext(name string) error {
|
||||||
|
if name == DefaultContextName {
|
||||||
|
return errors.New("default context cannot be removed")
|
||||||
|
}
|
||||||
|
return s.Store.RemoveContext(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextMetadata implements store.Store's GetContextMetadata
|
||||||
|
func (s *ContextStoreWithDefault) GetContextMetadata(name string) (store.ContextMetadata, error) {
|
||||||
|
if name == DefaultContextName {
|
||||||
|
defaultContext, err := s.Resolver()
|
||||||
|
if err != nil {
|
||||||
|
return store.ContextMetadata{}, err
|
||||||
|
}
|
||||||
|
return defaultContext.Meta, nil
|
||||||
|
}
|
||||||
|
return s.Store.GetContextMetadata(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetContextTLSMaterial is not implemented for default context and fails
|
||||||
|
func (s *ContextStoreWithDefault) ResetContextTLSMaterial(name string, data *store.ContextTLSData) error {
|
||||||
|
if name == DefaultContextName {
|
||||||
|
return errors.New("The default context store does not support ResetContextTLSMaterial")
|
||||||
|
}
|
||||||
|
return s.Store.ResetContextTLSMaterial(name, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetContextEndpointTLSMaterial is not implemented for default context and fails
|
||||||
|
func (s *ContextStoreWithDefault) ResetContextEndpointTLSMaterial(contextName string, endpointName string, data *store.EndpointTLSData) error {
|
||||||
|
if contextName == DefaultContextName {
|
||||||
|
return errors.New("The default context store does not support ResetContextEndpointTLSMaterial")
|
||||||
|
}
|
||||||
|
return s.Store.ResetContextEndpointTLSMaterial(contextName, endpointName, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListContextTLSFiles implements store.Store's ListContextTLSFiles
|
||||||
|
func (s *ContextStoreWithDefault) ListContextTLSFiles(name string) (map[string]store.EndpointFiles, error) {
|
||||||
|
if name == DefaultContextName {
|
||||||
|
defaultContext, err := s.Resolver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsfiles := make(map[string]store.EndpointFiles)
|
||||||
|
for epName, epTLSData := range defaultContext.TLS.Endpoints {
|
||||||
|
var files store.EndpointFiles
|
||||||
|
for filename := range epTLSData.Files {
|
||||||
|
files = append(files, filename)
|
||||||
|
}
|
||||||
|
tlsfiles[epName] = files
|
||||||
|
}
|
||||||
|
return tlsfiles, nil
|
||||||
|
}
|
||||||
|
return s.Store.ListContextTLSFiles(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextTLSData implements store.Store's GetContextTLSData
|
||||||
|
func (s *ContextStoreWithDefault) GetContextTLSData(contextName, endpointName, fileName string) ([]byte, error) {
|
||||||
|
if contextName == DefaultContextName {
|
||||||
|
defaultContext, err := s.Resolver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if defaultContext.TLS.Endpoints[endpointName].Files[fileName] == nil {
|
||||||
|
return nil, &noDefaultTLSDataError{endpointName: endpointName, fileName: fileName}
|
||||||
|
}
|
||||||
|
return defaultContext.TLS.Endpoints[endpointName].Files[fileName], nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return s.Store.GetContextTLSData(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() {}
|
||||||
|
|
||||||
|
// GetContextStorageInfo implements store.Store's GetContextStorageInfo
|
||||||
|
func (s *ContextStoreWithDefault) GetContextStorageInfo(contextName string) store.ContextStorageInfo {
|
||||||
|
if contextName == DefaultContextName {
|
||||||
|
return store.ContextStorageInfo{MetadataPath: "<IN MEMORY>", TLSPath: "<IN MEMORY>"}
|
||||||
|
}
|
||||||
|
return s.Store.GetContextStorageInfo(contextName)
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
|
"github.com/docker/cli/cli/context/docker"
|
||||||
|
"github.com/docker/cli/cli/context/kubernetes"
|
||||||
|
"github.com/docker/cli/cli/context/store"
|
||||||
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
|
"gotest.tools/assert"
|
||||||
|
"gotest.tools/env"
|
||||||
|
"gotest.tools/golden"
|
||||||
|
)
|
||||||
|
|
||||||
|
type endpoint struct {
|
||||||
|
Foo string `json:"a_very_recognizable_field_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type testContext struct {
|
||||||
|
Bar string `json:"another_very_recognizable_field_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCfg = store.NewConfig(func() interface{} { return &testContext{} },
|
||||||
|
store.EndpointTypeGetter("ep1", func() interface{} { return &endpoint{} }),
|
||||||
|
store.EndpointTypeGetter("ep2", func() interface{} { return &endpoint{} }),
|
||||||
|
)
|
||||||
|
|
||||||
|
func testDefaultMetadata() store.ContextMetadata {
|
||||||
|
return store.ContextMetadata{
|
||||||
|
Endpoints: map[string]interface{}{
|
||||||
|
"ep1": endpoint{Foo: "bar"},
|
||||||
|
},
|
||||||
|
Metadata: testContext{Bar: "baz"},
|
||||||
|
Name: DefaultContextName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStore(t *testing.T, meta store.ContextMetadata, tls store.ContextTLSData) (store.Store, func()) {
|
||||||
|
//meta := testDefaultMetadata()
|
||||||
|
testDir, err := ioutil.TempDir("", t.Name())
|
||||||
|
assert.NilError(t, err)
|
||||||
|
//defer os.RemoveAll(testDir)
|
||||||
|
store := &ContextStoreWithDefault{
|
||||||
|
Store: store.New(testDir, testCfg),
|
||||||
|
Resolver: func() (*DefaultContext, error) {
|
||||||
|
return &DefaultContext{
|
||||||
|
Meta: meta,
|
||||||
|
TLS: tls,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return store, func() {
|
||||||
|
os.RemoveAll(testDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultContextInitializer(t *testing.T) {
|
||||||
|
cli, err := NewDockerCli()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer env.Patch(t, "DOCKER_HOST", "ssh://someswarmserver")()
|
||||||
|
defer env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")()
|
||||||
|
cli.configFile = &configfile.ConfigFile{
|
||||||
|
StackOrchestrator: "all",
|
||||||
|
}
|
||||||
|
ctx, err := resolveDefaultContext(&cliflags.CommonOptions{
|
||||||
|
TLS: true,
|
||||||
|
TLSOptions: &tlsconfig.Options{
|
||||||
|
CAFile: "./testdata/ca.pem",
|
||||||
|
},
|
||||||
|
}, cli.ConfigFile(), cli.Err())
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, "default", ctx.Meta.Name)
|
||||||
|
assert.Equal(t, OrchestratorAll, ctx.Meta.Metadata.(DockerContext).StackOrchestrator)
|
||||||
|
assert.DeepEqual(t, "ssh://someswarmserver", ctx.Meta.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host)
|
||||||
|
golden.Assert(t, string(ctx.TLS.Endpoints[docker.DockerEndpoint].Files["ca.pem"]), "ca.pem")
|
||||||
|
assert.DeepEqual(t, "zoinx", ctx.Meta.Endpoints[kubernetes.KubernetesEndpoint].(kubernetes.EndpointMeta).DefaultNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExportDefaultImport(t *testing.T) {
|
||||||
|
file1 := make([]byte, 1500)
|
||||||
|
rand.Read(file1)
|
||||||
|
file2 := make([]byte, 3700)
|
||||||
|
rand.Read(file2)
|
||||||
|
s, cleanup := testStore(t, testDefaultMetadata(), store.ContextTLSData{
|
||||||
|
Endpoints: map[string]store.EndpointTLSData{
|
||||||
|
"ep2": {
|
||||||
|
Files: map[string][]byte{
|
||||||
|
"file1": file1,
|
||||||
|
"file2": file2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
defer cleanup()
|
||||||
|
r := store.Export("default", s)
|
||||||
|
defer r.Close()
|
||||||
|
err := store.Import("dest", s, r)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
srcMeta, err := s.GetContextMetadata("default")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
destMeta, err := s.GetContextMetadata("dest")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, destMeta.Metadata, srcMeta.Metadata)
|
||||||
|
assert.DeepEqual(t, destMeta.Endpoints, srcMeta.Endpoints)
|
||||||
|
|
||||||
|
srcFileList, err := s.ListContextTLSFiles("default")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
destFileList, err := s.ListContextTLSFiles("dest")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, 1, len(destFileList))
|
||||||
|
assert.Equal(t, 1, len(srcFileList))
|
||||||
|
assert.Equal(t, 2, len(destFileList["ep2"]))
|
||||||
|
assert.Equal(t, 2, len(srcFileList["ep2"]))
|
||||||
|
|
||||||
|
srcData1, err := s.GetContextTLSData("default", "ep2", "file1")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, file1, srcData1)
|
||||||
|
srcData2, err := s.GetContextTLSData("default", "ep2", "file2")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, file2, srcData2)
|
||||||
|
|
||||||
|
destData1, err := s.GetContextTLSData("dest", "ep2", "file1")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, file1, destData1)
|
||||||
|
destData2, err := s.GetContextTLSData("dest", "ep2", "file2")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, file2, destData2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListDefaultContext(t *testing.T) {
|
||||||
|
meta := testDefaultMetadata()
|
||||||
|
s, cleanup := testStore(t, meta, store.ContextTLSData{})
|
||||||
|
defer cleanup()
|
||||||
|
result, err := s.ListContexts()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, 1, len(result))
|
||||||
|
assert.DeepEqual(t, meta, result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDefaultContextStorageInfo(t *testing.T) {
|
||||||
|
s, cleanup := testStore(t, testDefaultMetadata(), store.ContextTLSData{})
|
||||||
|
defer cleanup()
|
||||||
|
result := s.GetContextStorageInfo(DefaultContextName)
|
||||||
|
assert.Equal(t, "<IN MEMORY>", result.MetadataPath)
|
||||||
|
assert.Equal(t, "<IN MEMORY>", result.TLSPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDefaultContextMetadata(t *testing.T) {
|
||||||
|
meta := testDefaultMetadata()
|
||||||
|
s, cleanup := testStore(t, meta, store.ContextTLSData{})
|
||||||
|
defer cleanup()
|
||||||
|
result, err := s.GetContextMetadata(DefaultContextName)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, DefaultContextName, result.Name)
|
||||||
|
assert.DeepEqual(t, meta.Metadata, result.Metadata)
|
||||||
|
assert.DeepEqual(t, meta.Endpoints, result.Endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrCreateDefault(t *testing.T) {
|
||||||
|
meta := testDefaultMetadata()
|
||||||
|
s, cleanup := testStore(t, meta, store.ContextTLSData{})
|
||||||
|
defer cleanup()
|
||||||
|
err := s.CreateOrUpdateContext(store.ContextMetadata{
|
||||||
|
Endpoints: map[string]interface{}{
|
||||||
|
"ep1": endpoint{Foo: "bar"},
|
||||||
|
},
|
||||||
|
Metadata: testContext{Bar: "baz"},
|
||||||
|
Name: "default",
|
||||||
|
})
|
||||||
|
assert.Error(t, err, "default context cannot be created nor updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrRemoveDefault(t *testing.T) {
|
||||||
|
meta := testDefaultMetadata()
|
||||||
|
s, cleanup := testStore(t, meta, store.ContextTLSData{})
|
||||||
|
defer cleanup()
|
||||||
|
err := s.RemoveContext("default")
|
||||||
|
assert.Error(t, err, "default context cannot be removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrTLSDataError(t *testing.T) {
|
||||||
|
meta := testDefaultMetadata()
|
||||||
|
s, cleanup := testStore(t, meta, store.ContextTLSData{})
|
||||||
|
defer cleanup()
|
||||||
|
_, err := s.GetContextTLSData("default", "noop", "noop")
|
||||||
|
assert.Check(t, store.IsErrTLSDataDoesNotExist(err))
|
||||||
|
}
|
|
@ -48,6 +48,10 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
}
|
}
|
||||||
defaultHelpFunc := cmd.HelpFunc()
|
defaultHelpFunc := cmd.HelpFunc()
|
||||||
cmd.SetHelpFunc(func(c *cobra.Command, args []string) {
|
cmd.SetHelpFunc(func(c *cobra.Command, args []string) {
|
||||||
|
if err := cmd.Root().PersistentPreRunE(c, args); err != nil {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := cmd.PersistentPreRunE(c, args); err != nil {
|
if err := cmd.PersistentPreRunE(c, args); err != nil {
|
||||||
fmt.Fprintln(dockerCli.Err(), err)
|
fmt.Fprintln(dockerCli.Err(), err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC+TCCAeGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMQswCQYDVQQGEwJGUjEP
|
||||||
|
MA0GA1UEChMGRG9ja2VyMB4XDTE5MDMwMzIzMDAwMFoXDTI0MDMwMTIzMDAwMFow
|
||||||
|
HjELMAkGA1UEBhMCRlIxDzANBgNVBAoTBkRvY2tlcjCCASIwDQYJKoZIhvcNAQEB
|
||||||
|
BQADggEPADCCAQoCggEBAMkifL8Ne9B9LQ8+pKD20meVuV34Ol/xUcH/OfxbiBMa
|
||||||
|
HrlIKsGIaO9GraBLq1DJyaZ6sP6ntfwXqwBYQrAN2fQL1AmwMetqpNjby307XqRa
|
||||||
|
GUQekjG710LfAFKsS/yD/R8L944MFmTbYwGyjROExs8ZAA4fkA8SATzRXhM3a8dE
|
||||||
|
YcrXacZQqd5dwFFS/UyJQbMoNx7IgzrXySqpt3rV8qD8MAUebgshd2p9CQO6zzoU
|
||||||
|
ImOJImMc/15LFZymemm2KvzXTM4J9UYdibXZGzpxcnyGNCb4FVV0HF0Ya+NMDwvY
|
||||||
|
nNpW5rea64ppS8McejePRCmLS8DxMxKTLB7eW97LuDECAwEAAaNCMEAwDwYDVR0T
|
||||||
|
AQH/BAUwAwEB/zAdBgNVHQ4EFgQU6zuJSXHxniajbNcc4SHoM+fatvMwDgYDVR0P
|
||||||
|
AQH/BAQDAgE+MA0GCSqGSIb3DQEBCwUAA4IBAQB2l46NnKzoTOCuUjxGmUv3s1np
|
||||||
|
rENRWlq0mHjCzoYSocg/IcwY7fz41XkwTVV8O3h/Jm25YGnj4lqaXlEKYJ63W8eI
|
||||||
|
wGLcirUAORSspcf+jd7OOjluzYCtuvVtOKR8w22pp5oE/AooGaO5y0ysefZBopr4
|
||||||
|
CNUNsEYhDKFg7tfj6Govi6+0PNxvB53we4nU7NhJNMaClhh/pi8zbeaEf67S6eKn
|
||||||
|
Z3DFqO+8FW4wEePLwhCftESCTwx6Q24v/WIYnzYOXC5mb2B9MwkyJXJIJxxPIeSs
|
||||||
|
PycNQ2kw7gk/TKkLMNQbX4fgFB0zfdofidTAkqOIqFHq/8iD2DYEZQFgCD3v
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: v1
|
||||||
|
clusters:
|
||||||
|
- cluster:
|
||||||
|
certificate-authority-data: dGhlLWNh
|
||||||
|
server: https://someserver
|
||||||
|
name: test-cluster
|
||||||
|
contexts:
|
||||||
|
- context:
|
||||||
|
cluster: test-cluster
|
||||||
|
user: test-user
|
||||||
|
namespace: zoinx
|
||||||
|
name: test
|
||||||
|
current-context: test
|
||||||
|
kind: Config
|
||||||
|
preferences: {}
|
||||||
|
users:
|
||||||
|
- name: test-user
|
||||||
|
user:
|
||||||
|
client-certificate-data: dGhlLWNlcnQ=
|
||||||
|
client-key-data: dGhlLWtleQ==
|
|
@ -12,7 +12,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/docker/docker/errdefs"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store provides a context store for easily remembering endpoints configuration
|
// Store provides a context store for easily remembering endpoints configuration
|
||||||
|
@ -299,6 +300,11 @@ func (e *contextDoesNotExistError) setContext(name string) {
|
||||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||||
func (e *contextDoesNotExistError) NotFound() {}
|
func (e *contextDoesNotExistError) NotFound() {}
|
||||||
|
|
||||||
|
type tlsDataDoesNotExist interface {
|
||||||
|
errdefs.ErrNotFound
|
||||||
|
IsTLSDataDoesNotExist()
|
||||||
|
}
|
||||||
|
|
||||||
type tlsDataDoesNotExistError struct {
|
type tlsDataDoesNotExistError struct {
|
||||||
context, endpoint, file string
|
context, endpoint, file string
|
||||||
}
|
}
|
||||||
|
@ -314,6 +320,9 @@ func (e *tlsDataDoesNotExistError) setContext(name string) {
|
||||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||||
func (e *tlsDataDoesNotExistError) NotFound() {}
|
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
|
||||||
func IsErrContextDoesNotExist(err error) bool {
|
func IsErrContextDoesNotExist(err error) bool {
|
||||||
_, ok := err.(*contextDoesNotExistError)
|
_, ok := err.(*contextDoesNotExistError)
|
||||||
|
@ -322,7 +331,7 @@ func IsErrContextDoesNotExist(err error) bool {
|
||||||
|
|
||||||
// IsErrTLSDataDoesNotExist checks if the given error is a "context does not exist" condition
|
// IsErrTLSDataDoesNotExist checks if the given error is a "context does not exist" condition
|
||||||
func IsErrTLSDataDoesNotExist(err error) bool {
|
func IsErrTLSDataDoesNotExist(err error) bool {
|
||||||
_, ok := err.(*tlsDataDoesNotExistError)
|
_, ok := err.(tlsDataDoesNotExist)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -35,9 +36,14 @@ func TestExportImport(t *testing.T) {
|
||||||
Name: "source",
|
Name: "source",
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
file1 := make([]byte, 1500)
|
||||||
|
rand.Read(file1)
|
||||||
|
file2 := make([]byte, 3700)
|
||||||
|
rand.Read(file2)
|
||||||
err = s.ResetContextEndpointTLSMaterial("source", "ep1", &EndpointTLSData{
|
err = s.ResetContextEndpointTLSMaterial("source", "ep1", &EndpointTLSData{
|
||||||
Files: map[string][]byte{
|
Files: map[string][]byte{
|
||||||
"file1": []byte("test-data"),
|
"file1": file1,
|
||||||
|
"file2": file2,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
@ -55,13 +61,22 @@ func TestExportImport(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
destFileList, err := s.ListContextTLSFiles("dest")
|
destFileList, err := s.ListContextTLSFiles("dest")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, srcFileList, destFileList)
|
assert.Equal(t, 1, len(destFileList))
|
||||||
srcData, err := s.GetContextTLSData("source", "ep1", "file1")
|
assert.Equal(t, 1, len(srcFileList))
|
||||||
|
assert.Equal(t, 2, len(destFileList["ep1"]))
|
||||||
|
assert.Equal(t, 2, len(srcFileList["ep1"]))
|
||||||
|
srcData1, err := s.GetContextTLSData("source", "ep1", "file1")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, "test-data", string(srcData))
|
assert.DeepEqual(t, file1, srcData1)
|
||||||
destData, err := s.GetContextTLSData("dest", "ep1", "file1")
|
srcData2, err := s.GetContextTLSData("source", "ep1", "file2")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, "test-data", string(destData))
|
assert.DeepEqual(t, file2, srcData2)
|
||||||
|
destData1, err := s.GetContextTLSData("dest", "ep1", "file1")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, file1, destData1)
|
||||||
|
destData2, err := s.GetContextTLSData("dest", "ep1", "file2")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, file2, destData2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemove(t *testing.T) {
|
func TestRemove(t *testing.T) {
|
||||||
|
|
|
@ -224,3 +224,24 @@ func (c *FakeCli) NewContainerizedEngineClient(sockPath string) (clitypes.Contai
|
||||||
func (c *FakeCli) SetContainerizedEngineClient(containerizedEngineClientFunc containerizedEngineFuncType) {
|
func (c *FakeCli) SetContainerizedEngineClient(containerizedEngineClientFunc containerizedEngineFuncType) {
|
||||||
c.containerizedEngineClientFunc = containerizedEngineClientFunc
|
c.containerizedEngineClientFunc = containerizedEngineClientFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StackOrchestrator return the selected stack orchestrator
|
||||||
|
func (c *FakeCli) StackOrchestrator(flagValue string) (command.Orchestrator, error) {
|
||||||
|
configOrchestrator := ""
|
||||||
|
if c.configfile != nil {
|
||||||
|
configOrchestrator = c.configfile.StackOrchestrator
|
||||||
|
}
|
||||||
|
ctxOrchestrator := ""
|
||||||
|
if c.currentContext != "" && c.contextStore != nil {
|
||||||
|
meta, err := c.contextStore.GetContextMetadata(c.currentContext)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
context, err := command.GetDockerContext(meta)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ctxOrchestrator = string(context.StackOrchestrator)
|
||||||
|
}
|
||||||
|
return command.GetStackOrchestrator(flagValue, ctxOrchestrator, configOrchestrator, c.err)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue