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
80 changes: 55 additions & 25 deletions cli/command/context/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
)

// CreateOptions are the options used for creating a context
//
// Deprecated: this type was for internal use and will be removed in the next release.
type CreateOptions struct {
Name string
Description string
Expand All @@ -30,6 +32,18 @@ type CreateOptions struct {
metaData map[string]any
}

// createOptions are the options used for creating a context
type createOptions struct {
name string
description string
endpoint map[string]string
from string

// Additional Metadata to store in the context. This option is not
// currently exposed to the user.
metaData map[string]any
}

func longCreateDescription() string {
buf := bytes.NewBuffer(nil)
buf.WriteString("Create a context\n\nDocker endpoint config:\n\n")
Expand All @@ -44,52 +58,68 @@ 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(),
ValidArgsFunction: completion.NoComplete,
}
flags := cmd.Flags()
flags.StringVar(&opts.Description, "description", "", "Description of the context")
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
flags.StringVar(&opts.From, "from", "", "create context from a named context")
flags.StringVar(&opts.description, "description", "", "Description of the context")
flags.StringToStringVar(&opts.endpoint, "docker", nil, "set the docker endpoint")
flags.StringVar(&opts.from, "from", "", "create context from a named context")
return cmd
}

// RunCreate creates a Docker context

// Deprecated: this function was for internal use and will be removed in the next release.
func RunCreate(dockerCLI command.Cli, o *CreateOptions) error {
if o == nil {
o = &CreateOptions{}
}

return runCreate(dockerCLI, &createOptions{
name: o.Name,
description: o.Description,
endpoint: o.Docker,
metaData: o.metaData,
})
}

// runCreate creates a Docker context
func runCreate(dockerCLI command.Cli, opts *createOptions) error {
s := dockerCLI.ContextStore()
err := checkContextNameForCreation(s, o.Name)
err := checkContextNameForCreation(s, opts.name)
if err != nil {
return err
}
switch {
case o.From == "" && o.Docker == nil:
err = createFromExistingContext(s, dockerCLI.CurrentContext(), o)
case o.From != "":
err = createFromExistingContext(s, o.From, o)
case opts.from == "" && opts.endpoint == nil:
err = createFromExistingContext(s, dockerCLI.CurrentContext(), opts)
case opts.from != "":
err = createFromExistingContext(s, opts.from, opts)
default:
err = createNewContext(s, o)
err = createNewContext(s, opts)
}
if err == nil {
_, _ = fmt.Fprintln(dockerCLI.Out(), o.Name)
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", o.Name)
_, _ = fmt.Fprintln(dockerCLI.Out(), opts.name)
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", opts.name)
}
return err
}

func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
if o.Docker == nil {
func createNewContext(contextStore store.ReaderWriter, opts *createOptions) error {
if opts.endpoint == nil {
return errors.New("docker endpoint configuration is required")
}
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(contextStore, o.Docker)
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(contextStore, opts.endpoint)
if err != nil {
return fmt.Errorf("unable to create docker endpoint config: %w", err)
}
Expand All @@ -98,10 +128,10 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
docker.DockerEndpoint: dockerEP,
},
Metadata: command.DockerContext{
Description: o.Description,
AdditionalFields: o.metaData,
Description: opts.description,
AdditionalFields: opts.metaData,
},
Name: o.Name,
Name: opts.name,
}
contextTLSData := store.ContextTLSData{}
if dockerTLS != nil {
Expand All @@ -115,7 +145,7 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
if err := contextStore.CreateOrUpdate(contextMetadata); err != nil {
return err
}
return contextStore.ResetTLSMaterial(o.Name, &contextTLSData)
return contextStore.ResetTLSMaterial(opts.name, &contextTLSData)
}

func checkContextNameForCreation(s store.Reader, name string) error {
Expand All @@ -131,16 +161,16 @@ func checkContextNameForCreation(s store.Reader, name string) error {
return nil
}

func createFromExistingContext(s store.ReaderWriter, fromContextName string, o *CreateOptions) error {
if len(o.Docker) != 0 {
func createFromExistingContext(s store.ReaderWriter, fromContextName string, opts *createOptions) error {
if len(opts.endpoint) != 0 {
return errors.New("cannot use --docker flag when --from is set")
}
reader := store.Export(fromContextName, &descriptionDecorator{
Reader: s,
description: o.Description,
description: opts.description,
})
defer reader.Close()
return store.Import(o.Name, s, reader)
return store.Import(opts.name, s, reader)
}

type descriptionDecorator struct {
Expand Down
92 changes: 46 additions & 46 deletions cli/command/context/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestCreate(t *testing.T) {
assert.NilError(t, cli.ContextStore().CreateOrUpdate(store.Metadata{Name: "existing-context"}))
tests := []struct {
doc string
options CreateOptions
options createOptions
expecterErr string
}{
{
Expand All @@ -69,58 +69,58 @@ func TestCreate(t *testing.T) {
},
{
doc: "reserved name",
options: CreateOptions{
Name: "default",
options: createOptions{
name: "default",
},
expecterErr: `"default" is a reserved context name`,
},
{
doc: "whitespace-only name",
options: CreateOptions{
Name: " ",
options: createOptions{
name: " ",
},
expecterErr: `context name " " is invalid`,
},
{
doc: "existing context",
options: CreateOptions{
Name: "existing-context",
options: createOptions{
name: "existing-context",
},
expecterErr: `context "existing-context" already exists`,
},
{
doc: "invalid docker host",
options: CreateOptions{
Name: "invalid-docker-host",
Docker: map[string]string{
options: createOptions{
name: "invalid-docker-host",
endpoint: map[string]string{
"host": "some///invalid/host",
},
},
expecterErr: `unable to parse docker host`,
},
{
doc: "ssh host with skip-tls-verify=false",
options: CreateOptions{
Name: "skip-tls-verify-false",
Docker: map[string]string{
options: createOptions{
name: "skip-tls-verify-false",
endpoint: map[string]string{
"host": "ssh://example.com,skip-tls-verify=false",
},
},
},
{
doc: "ssh host with skip-tls-verify=true",
options: CreateOptions{
Name: "skip-tls-verify-true",
Docker: map[string]string{
options: createOptions{
name: "skip-tls-verify-true",
endpoint: map[string]string{
"host": "ssh://example.com,skip-tls-verify=true",
},
},
},
{
doc: "ssh host with skip-tls-verify=INVALID",
options: CreateOptions{
Name: "skip-tls-verify-invalid",
Docker: map[string]string{
options: createOptions{
name: "skip-tls-verify-invalid",
endpoint: map[string]string{
"host": "ssh://example.com",
"skip-tls-verify": "INVALID",
},
Expand All @@ -129,9 +129,9 @@ func TestCreate(t *testing.T) {
},
{
doc: "unknown option",
options: CreateOptions{
Name: "unknown-option",
Docker: map[string]string{
options: createOptions{
name: "unknown-option",
endpoint: map[string]string{
"UNKNOWN": "value",
},
},
Expand All @@ -140,7 +140,7 @@ func TestCreate(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
err := RunCreate(cli, &tc.options)
err := runCreate(cli, &tc.options)
if tc.expecterErr == "" {
assert.NilError(t, err)
} else {
Expand All @@ -159,9 +159,9 @@ func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) {
func TestCreateOrchestratorEmpty(t *testing.T) {
cli := makeFakeCli(t)

err := RunCreate(cli, &CreateOptions{
Name: "test",
Docker: map[string]string{},
err := runCreate(cli, &createOptions{
name: "test",
endpoint: map[string]string{},
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, "test")
Expand All @@ -187,20 +187,20 @@ func TestCreateFromContext(t *testing.T) {

cli := makeFakeCli(t)
cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{
Name: "original",
Description: "original description",
Docker: map[string]string{
assert.NilError(t, runCreate(cli, &createOptions{
name: "original",
description: "original description",
endpoint: map[string]string{
keyHost: "tcp://42.42.42.42:2375",
},
}))
assertContextCreateLogging(t, cli, "original")

cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{
Name: "dummy",
Description: "dummy description",
Docker: map[string]string{
assert.NilError(t, runCreate(cli, &createOptions{
name: "dummy",
description: "dummy description",
endpoint: map[string]string{
keyHost: "tcp://24.24.24.24:2375",
},
}))
Expand All @@ -211,11 +211,11 @@ func TestCreateFromContext(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{
From: "original",
Name: tc.name,
Description: tc.description,
Docker: tc.docker,
err := runCreate(cli, &createOptions{
from: "original",
name: tc.name,
description: tc.description,
endpoint: tc.docker,
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, tc.name)
Expand Down Expand Up @@ -251,10 +251,10 @@ func TestCreateFromCurrent(t *testing.T) {

cli := makeFakeCli(t)
cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{
Name: "original",
Description: "original description",
Docker: map[string]string{
assert.NilError(t, runCreate(cli, &createOptions{
name: "original",
description: "original description",
endpoint: map[string]string{
keyHost: "tcp://42.42.42.42:2375",
},
}))
Expand All @@ -265,9 +265,9 @@ func TestCreateFromCurrent(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{
Name: tc.name,
Description: tc.description,
err := runCreate(cli, &createOptions{
name: tc.name,
description: tc.description,
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, tc.name)
Expand Down
16 changes: 5 additions & 11 deletions cli/command/context/export-import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ func TestExportImportWithFile(t *testing.T) {
"MyCustomMetadata": t.Name(),
})
cli.ErrBuffer().Reset()
assert.NilError(t, RunExport(cli, &ExportOptions{
ContextName: "test",
Dest: contextFile,
}))
assert.NilError(t, runExport(cli, "test", contextFile))
assert.Equal(t, cli.ErrBuffer().String(), fmt.Sprintf("Written file %q\n", contextFile))
cli.OutBuffer().Reset()
cli.ErrBuffer().Reset()
assert.NilError(t, RunImport(cli, "test2", contextFile))
assert.NilError(t, runImport(cli, "test2", contextFile))
context1, err := cli.ContextStore().GetMetadata("test")
assert.NilError(t, err)
context2, err := cli.ContextStore().GetMetadata("test2")
Expand All @@ -55,15 +52,12 @@ func TestExportImportPipe(t *testing.T) {
})
cli.ErrBuffer().Reset()
cli.OutBuffer().Reset()
assert.NilError(t, RunExport(cli, &ExportOptions{
ContextName: "test",
Dest: "-",
}))
assert.NilError(t, runExport(cli, "test", "-"))
assert.Equal(t, cli.ErrBuffer().String(), "")
cli.SetIn(streams.NewIn(io.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes()))))
cli.OutBuffer().Reset()
cli.ErrBuffer().Reset()
assert.NilError(t, RunImport(cli, "test2", "-"))
assert.NilError(t, runImport(cli, "test2", "-"))
context1, err := cli.ContextStore().GetMetadata("test")
assert.NilError(t, err)
context2, err := cli.ContextStore().GetMetadata("test2")
Expand All @@ -88,6 +82,6 @@ func TestExportExistingFile(t *testing.T) {
cli := makeFakeCli(t)
cli.ErrBuffer().Reset()
assert.NilError(t, os.WriteFile(contextFile, []byte{}, 0o644))
err := RunExport(cli, &ExportOptions{ContextName: "test", Dest: contextFile})
err := runExport(cli, "test", contextFile)
assert.Assert(t, os.IsExist(err))
}
Loading
Loading