Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions cli/command/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,9 @@ type DockerCli struct {
contextStore store.Store
currentContext string
dockerEndpoint docker.Endpoint
contextStoreConfig store.Config
}

var storeConfig = store.NewConfig(
func() interface{} { return &DockerContext{} },
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
store.EndpointTypeGetter(kubcontext.KubernetesEndpoint, func() interface{} { return &kubcontext.EndpointMeta{} }),
)

// DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified.
func (cli *DockerCli) DefaultVersion() string {
return cli.clientInfo.DefaultVersion
Expand Down Expand Up @@ -184,7 +179,7 @@ func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.Registry
func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)
var err error
cli.contextStore = store.New(cliconfig.ContextStoreDir(), storeConfig)
cli.contextStore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
if err != nil {
return err
Expand Down Expand Up @@ -226,7 +221,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {

// NewAPIClientFromFlags creates a new APIClient from command line flags
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
store := store.New(cliconfig.ContextStoreDir(), storeConfig)
store := store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig())
contextName, err := resolveContextName(opts, configFile, store)
if err != nil {
return nil, err
Expand Down Expand Up @@ -372,7 +367,7 @@ func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error)
if currentContext != "" {
contextstore := cli.contextStore
if contextstore == nil {
contextstore = store.New(cliconfig.ContextStoreDir(), storeConfig)
contextstore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
}
ctxRaw, err := contextstore.GetContextMetadata(currentContext)
if store.IsErrContextDoesNotExist(err) {
Expand Down Expand Up @@ -430,6 +425,7 @@ func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
WithContentTrustFromEnv(),
WithContainerizedClient(containerizedengine.NewClient),
}
cli.contextStoreConfig = defaultContextStoreConfig()
ops = append(defaultOps, ops...)
if err := cli.Apply(ops...); err != nil {
return nil, err
Expand Down Expand Up @@ -501,3 +497,11 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
}
return "", nil
}

func defaultContextStoreConfig() store.Config {
return store.NewConfig(
func() interface{} { return &DockerContext{} },
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
store.EndpointTypeGetter(kubcontext.KubernetesEndpoint, func() interface{} { return &kubcontext.EndpointMeta{} }),
)
}
17 changes: 17 additions & 0 deletions cli/command/cli_options.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package command

import (
"fmt"
"io"
"os"
"strconv"

"github.com/docker/cli/cli/context/docker"
"github.com/docker/cli/cli/context/kubernetes"
"github.com/docker/cli/cli/context/store"
"github.com/docker/cli/cli/streams"
clitypes "github.com/docker/cli/types"
"github.com/docker/docker/pkg/term"
Expand Down Expand Up @@ -87,3 +91,16 @@ func WithContainerizedClient(containerizedFn func(string) (clitypes.Containerize
return nil
}
}

// WithContextEndpointType add support for an additional typed endpoint in the context store
// Plugins should use this to store additional endpoints configuration in the context store
func WithContextEndpointType(endpointName string, endpointType store.TypeGetter) DockerCliOption {
return func(cli *DockerCli) error {
switch endpointName {
case docker.DockerEndpoint, kubernetes.KubernetesEndpoint:
return fmt.Errorf("cannot change %q endpoint type", endpointName)
}
cli.contextStoreConfig.SetEndpoint(endpointName, endpointType)
return nil
}
}
52 changes: 27 additions & 25 deletions cli/command/context/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import (
"github.com/spf13/cobra"
)

type createOptions struct {
name string
description string
defaultStackOrchestrator string
docker map[string]string
kubernetes map[string]string
// CreateOptions are the options used for creating a context
type CreateOptions struct {
Name string
Description string
DefaultStackOrchestrator string
Docker map[string]string
Kubernetes map[string]string
}

func longCreateDescription() string {
Expand All @@ -43,61 +44,62 @@ func longCreateDescription() string {
}

func newCreateCommand(dockerCli command.Cli) *cobra.Command {
opts := &createOptions{}
opts := &CreateOptions{}
cmd := &cobra.Command{
Use: "create [OPTIONS] CONTEXT",
Short: "Create a context",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.name = args[0]
return runCreate(dockerCli, opts)
opts.Name = args[0]
return RunCreate(dockerCli, opts)
},
Long: longCreateDescription(),
}
flags := cmd.Flags()
flags.StringVar(&opts.description, "description", "", "Description of the context")
flags.StringVar(&opts.Description, "description", "", "Description of the context")
flags.StringVar(
&opts.defaultStackOrchestrator,
&opts.DefaultStackOrchestrator,
"default-stack-orchestrator", "",
"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)")
flags.StringToStringVar(&opts.docker, "docker", nil, "set the docker endpoint")
flags.StringToStringVar(&opts.kubernetes, "kubernetes", nil, "set the kubernetes endpoint")
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint")
return cmd
}

func runCreate(cli command.Cli, o *createOptions) error {
// RunCreate creates a Docker context
func RunCreate(cli command.Cli, o *CreateOptions) error {
s := cli.ContextStore()
if err := checkContextNameForCreation(s, o.name); err != nil {
if err := checkContextNameForCreation(s, o.Name); err != nil {
return err
}
stackOrchestrator, err := command.NormalizeOrchestrator(o.defaultStackOrchestrator)
stackOrchestrator, err := command.NormalizeOrchestrator(o.DefaultStackOrchestrator)
if err != nil {
return errors.Wrap(err, "unable to parse default-stack-orchestrator")
}
contextMetadata := store.ContextMetadata{
Endpoints: make(map[string]interface{}),
Metadata: command.DockerContext{
Description: o.description,
Description: o.Description,
StackOrchestrator: stackOrchestrator,
},
Name: o.name,
Name: o.Name,
}
if o.docker == nil {
if o.Docker == nil {
return errors.New("docker endpoint configuration is required")
}
contextTLSData := store.ContextTLSData{
Endpoints: make(map[string]store.EndpointTLSData),
}
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(cli, o.docker)
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(cli, o.Docker)
if err != nil {
return errors.Wrap(err, "unable to create docker endpoint config")
}
contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP
if dockerTLS != nil {
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerTLS
}
if o.kubernetes != nil {
kubernetesEP, kubernetesTLS, err := getKubernetesEndpointMetadataAndTLS(cli, o.kubernetes)
if o.Kubernetes != nil {
kubernetesEP, kubernetesTLS, err := getKubernetesEndpointMetadataAndTLS(cli, o.Kubernetes)
if err != nil {
return errors.Wrap(err, "unable to create kubernetes endpoint config")
}
Expand All @@ -117,11 +119,11 @@ func runCreate(cli command.Cli, o *createOptions) error {
if err := s.CreateOrUpdateContext(contextMetadata); err != nil {
return err
}
if err := s.ResetContextTLSMaterial(o.name, &contextTLSData); err != nil {
if err := s.ResetContextTLSMaterial(o.Name, &contextTLSData); err != nil {
return err
}
fmt.Fprintln(cli.Out(), o.name)
fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.name)
fmt.Fprintln(cli.Out(), o.Name)
fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.Name)
return nil
}

Expand Down
72 changes: 36 additions & 36 deletions cli/command/context/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,68 +46,68 @@ func TestCreateInvalids(t *testing.T) {
defer cleanup()
assert.NilError(t, cli.ContextStore().CreateOrUpdateContext(store.ContextMetadata{Name: "existing-context"}))
tests := []struct {
options createOptions
options CreateOptions
expecterErr string
}{
{
expecterErr: `context name cannot be empty`,
},
{
options: createOptions{
name: " ",
options: CreateOptions{
Name: " ",
},
expecterErr: `context name " " is invalid`,
},
{
options: createOptions{
name: "existing-context",
options: CreateOptions{
Name: "existing-context",
},
expecterErr: `context "existing-context" already exists`,
},
{
options: createOptions{
name: "invalid-docker-host",
docker: map[string]string{
options: CreateOptions{
Name: "invalid-docker-host",
Docker: map[string]string{
keyHost: "some///invalid/host",
},
},
expecterErr: `unable to parse docker host`,
},
{
options: createOptions{
name: "invalid-orchestrator",
defaultStackOrchestrator: "invalid",
options: CreateOptions{
Name: "invalid-orchestrator",
DefaultStackOrchestrator: "invalid",
},
expecterErr: `specified orchestrator "invalid" is invalid, please use either kubernetes, swarm or all`,
},
{
options: createOptions{
name: "orchestrator-swarm-no-endpoint",
defaultStackOrchestrator: "swarm",
options: CreateOptions{
Name: "orchestrator-swarm-no-endpoint",
DefaultStackOrchestrator: "swarm",
},
expecterErr: `docker endpoint configuration is required`,
},
{
options: createOptions{
name: "orchestrator-kubernetes-no-endpoint",
defaultStackOrchestrator: "kubernetes",
docker: map[string]string{},
options: CreateOptions{
Name: "orchestrator-kubernetes-no-endpoint",
DefaultStackOrchestrator: "kubernetes",
Docker: map[string]string{},
},
expecterErr: `cannot specify orchestrator "kubernetes" without configuring a Kubernetes endpoint`,
},
{
options: createOptions{
name: "orchestrator-all-no-endpoint",
defaultStackOrchestrator: "all",
docker: map[string]string{},
options: CreateOptions{
Name: "orchestrator-all-no-endpoint",
DefaultStackOrchestrator: "all",
Docker: map[string]string{},
},
expecterErr: `cannot specify orchestrator "all" without configuring a Kubernetes endpoint`,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.options.name, func(t *testing.T) {
err := runCreate(cli, &tc.options)
t.Run(tc.options.Name, func(t *testing.T) {
err := RunCreate(cli, &tc.options)
assert.ErrorContains(t, err, tc.expecterErr)
})
}
Expand All @@ -117,10 +117,10 @@ func TestCreateOrchestratorSwarm(t *testing.T) {
cli, cleanup := makeFakeCli(t)
defer cleanup()

err := runCreate(cli, &createOptions{
name: "test",
defaultStackOrchestrator: "swarm",
docker: map[string]string{},
err := RunCreate(cli, &CreateOptions{
Name: "test",
DefaultStackOrchestrator: "swarm",
Docker: map[string]string{},
})
assert.NilError(t, err)
assert.Equal(t, "test\n", cli.OutBuffer().String())
Expand All @@ -131,9 +131,9 @@ func TestCreateOrchestratorEmpty(t *testing.T) {
cli, cleanup := makeFakeCli(t)
defer cleanup()

err := runCreate(cli, &createOptions{
name: "test",
docker: map[string]string{},
err := RunCreate(cli, &CreateOptions{
Name: "test",
Docker: map[string]string{},
})
assert.NilError(t, err)
}
Expand All @@ -156,13 +156,13 @@ func createTestContextWithKube(t *testing.T, cli command.Cli) {
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
defer revert()

err := runCreate(cli, &createOptions{
name: "test",
defaultStackOrchestrator: "all",
kubernetes: map[string]string{
err := RunCreate(cli, &CreateOptions{
Name: "test",
DefaultStackOrchestrator: "all",
Kubernetes: map[string]string{
keyFromCurrent: "true",
},
docker: map[string]string{},
Docker: map[string]string{},
})
assert.NilError(t, err)
}
Expand Down
Loading