Merge pull request #6402 from thaJeztah/deprecate_context_funcs

cli/command/context: deprecate exported types and functions
This commit is contained in:
Sebastiaan van Stijn 2025-09-01 09:11:28 +02:00 committed by GitHub
commit 321100e38b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 238 additions and 152 deletions

View File

@ -19,6 +19,8 @@ import (
) )
// CreateOptions are the options used for creating a context // 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 { type CreateOptions struct {
Name string Name string
Description string Description string
@ -30,6 +32,18 @@ type CreateOptions struct {
metaData map[string]any 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 { func longCreateDescription() string {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
buf.WriteString("Create a context\n\nDocker endpoint config:\n\n") buf.WriteString("Create a context\n\nDocker endpoint config:\n\n")
@ -44,52 +58,68 @@ func longCreateDescription() string {
} }
func newCreateCommand(dockerCLI command.Cli) *cobra.Command { func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
opts := &CreateOptions{} opts := createOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create [OPTIONS] CONTEXT", Use: "create [OPTIONS] CONTEXT",
Short: "Create a context", Short: "Create a context",
Args: cli.ExactArgs(1), Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts.Name = args[0] opts.name = args[0]
return RunCreate(dockerCLI, opts) return runCreate(dockerCLI, &opts)
}, },
Long: longCreateDescription(), Long: longCreateDescription(),
ValidArgsFunction: completion.NoComplete, ValidArgsFunction: completion.NoComplete,
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVar(&opts.Description, "description", "", "Description of the context") flags.StringVar(&opts.description, "description", "", "Description of the context")
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint") flags.StringToStringVar(&opts.endpoint, "docker", nil, "set the docker endpoint")
flags.StringVar(&opts.From, "from", "", "create context from a named context") flags.StringVar(&opts.from, "from", "", "create context from a named context")
return cmd return cmd
} }
// RunCreate creates a Docker context // 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 { 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() s := dockerCLI.ContextStore()
err := checkContextNameForCreation(s, o.Name) err := checkContextNameForCreation(s, opts.name)
if err != nil { if err != nil {
return err return err
} }
switch { switch {
case o.From == "" && o.Docker == nil: case opts.from == "" && opts.endpoint == nil:
err = createFromExistingContext(s, dockerCLI.CurrentContext(), o) err = createFromExistingContext(s, dockerCLI.CurrentContext(), opts)
case o.From != "": case opts.from != "":
err = createFromExistingContext(s, o.From, o) err = createFromExistingContext(s, opts.from, opts)
default: default:
err = createNewContext(s, o) err = createNewContext(s, opts)
} }
if err == nil { if err == nil {
_, _ = fmt.Fprintln(dockerCLI.Out(), o.Name) _, _ = fmt.Fprintln(dockerCLI.Out(), opts.name)
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", o.Name) _, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", opts.name)
} }
return err return err
} }
func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error { func createNewContext(contextStore store.ReaderWriter, opts *createOptions) error {
if o.Docker == nil { if opts.endpoint == nil {
return errors.New("docker endpoint configuration is required") 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 { if err != nil {
return fmt.Errorf("unable to create docker endpoint config: %w", err) return fmt.Errorf("unable to create docker endpoint config: %w", err)
} }
@ -98,10 +128,10 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
docker.DockerEndpoint: dockerEP, docker.DockerEndpoint: dockerEP,
}, },
Metadata: command.DockerContext{ Metadata: command.DockerContext{
Description: o.Description, Description: opts.description,
AdditionalFields: o.metaData, AdditionalFields: opts.metaData,
}, },
Name: o.Name, Name: opts.name,
} }
contextTLSData := store.ContextTLSData{} contextTLSData := store.ContextTLSData{}
if dockerTLS != nil { if dockerTLS != nil {
@ -115,7 +145,7 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
if err := contextStore.CreateOrUpdate(contextMetadata); err != nil { if err := contextStore.CreateOrUpdate(contextMetadata); err != nil {
return err return err
} }
return contextStore.ResetTLSMaterial(o.Name, &contextTLSData) return contextStore.ResetTLSMaterial(opts.name, &contextTLSData)
} }
func checkContextNameForCreation(s store.Reader, name string) error { func checkContextNameForCreation(s store.Reader, name string) error {
@ -131,16 +161,16 @@ func checkContextNameForCreation(s store.Reader, name string) error {
return nil return nil
} }
func createFromExistingContext(s store.ReaderWriter, fromContextName string, o *CreateOptions) error { func createFromExistingContext(s store.ReaderWriter, fromContextName string, opts *createOptions) error {
if len(o.Docker) != 0 { if len(opts.endpoint) != 0 {
return errors.New("cannot use --docker flag when --from is set") return errors.New("cannot use --docker flag when --from is set")
} }
reader := store.Export(fromContextName, &descriptionDecorator{ reader := store.Export(fromContextName, &descriptionDecorator{
Reader: s, Reader: s,
description: o.Description, description: opts.description,
}) })
defer reader.Close() defer reader.Close()
return store.Import(o.Name, s, reader) return store.Import(opts.name, s, reader)
} }
type descriptionDecorator struct { type descriptionDecorator struct {

View File

@ -60,7 +60,7 @@ func TestCreate(t *testing.T) {
assert.NilError(t, cli.ContextStore().CreateOrUpdate(store.Metadata{Name: "existing-context"})) assert.NilError(t, cli.ContextStore().CreateOrUpdate(store.Metadata{Name: "existing-context"}))
tests := []struct { tests := []struct {
doc string doc string
options CreateOptions options createOptions
expecterErr string expecterErr string
}{ }{
{ {
@ -69,30 +69,30 @@ func TestCreate(t *testing.T) {
}, },
{ {
doc: "reserved name", doc: "reserved name",
options: CreateOptions{ options: createOptions{
Name: "default", name: "default",
}, },
expecterErr: `"default" is a reserved context name`, expecterErr: `"default" is a reserved context name`,
}, },
{ {
doc: "whitespace-only name", doc: "whitespace-only name",
options: CreateOptions{ options: createOptions{
Name: " ", name: " ",
}, },
expecterErr: `context name " " is invalid`, expecterErr: `context name " " is invalid`,
}, },
{ {
doc: "existing context", doc: "existing context",
options: CreateOptions{ options: createOptions{
Name: "existing-context", name: "existing-context",
}, },
expecterErr: `context "existing-context" already exists`, expecterErr: `context "existing-context" already exists`,
}, },
{ {
doc: "invalid docker host", doc: "invalid docker host",
options: CreateOptions{ options: createOptions{
Name: "invalid-docker-host", name: "invalid-docker-host",
Docker: map[string]string{ endpoint: map[string]string{
"host": "some///invalid/host", "host": "some///invalid/host",
}, },
}, },
@ -100,27 +100,27 @@ func TestCreate(t *testing.T) {
}, },
{ {
doc: "ssh host with skip-tls-verify=false", doc: "ssh host with skip-tls-verify=false",
options: CreateOptions{ options: createOptions{
Name: "skip-tls-verify-false", name: "skip-tls-verify-false",
Docker: map[string]string{ endpoint: map[string]string{
"host": "ssh://example.com,skip-tls-verify=false", "host": "ssh://example.com,skip-tls-verify=false",
}, },
}, },
}, },
{ {
doc: "ssh host with skip-tls-verify=true", doc: "ssh host with skip-tls-verify=true",
options: CreateOptions{ options: createOptions{
Name: "skip-tls-verify-true", name: "skip-tls-verify-true",
Docker: map[string]string{ endpoint: map[string]string{
"host": "ssh://example.com,skip-tls-verify=true", "host": "ssh://example.com,skip-tls-verify=true",
}, },
}, },
}, },
{ {
doc: "ssh host with skip-tls-verify=INVALID", doc: "ssh host with skip-tls-verify=INVALID",
options: CreateOptions{ options: createOptions{
Name: "skip-tls-verify-invalid", name: "skip-tls-verify-invalid",
Docker: map[string]string{ endpoint: map[string]string{
"host": "ssh://example.com", "host": "ssh://example.com",
"skip-tls-verify": "INVALID", "skip-tls-verify": "INVALID",
}, },
@ -129,9 +129,9 @@ func TestCreate(t *testing.T) {
}, },
{ {
doc: "unknown option", doc: "unknown option",
options: CreateOptions{ options: createOptions{
Name: "unknown-option", name: "unknown-option",
Docker: map[string]string{ endpoint: map[string]string{
"UNKNOWN": "value", "UNKNOWN": "value",
}, },
}, },
@ -140,7 +140,7 @@ func TestCreate(t *testing.T) {
} }
for _, tc := range tests { for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) { t.Run(tc.doc, func(t *testing.T) {
err := RunCreate(cli, &tc.options) err := runCreate(cli, &tc.options)
if tc.expecterErr == "" { if tc.expecterErr == "" {
assert.NilError(t, err) assert.NilError(t, err)
} else { } else {
@ -159,9 +159,9 @@ func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) {
func TestCreateOrchestratorEmpty(t *testing.T) { func TestCreateOrchestratorEmpty(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: "test", name: "test",
Docker: map[string]string{}, endpoint: map[string]string{},
}) })
assert.NilError(t, err) assert.NilError(t, err)
assertContextCreateLogging(t, cli, "test") assertContextCreateLogging(t, cli, "test")
@ -187,20 +187,20 @@ func TestCreateFromContext(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
cli.ResetOutputBuffers() cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{ assert.NilError(t, runCreate(cli, &createOptions{
Name: "original", name: "original",
Description: "original description", description: "original description",
Docker: map[string]string{ endpoint: map[string]string{
keyHost: "tcp://42.42.42.42:2375", keyHost: "tcp://42.42.42.42:2375",
}, },
})) }))
assertContextCreateLogging(t, cli, "original") assertContextCreateLogging(t, cli, "original")
cli.ResetOutputBuffers() cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{ assert.NilError(t, runCreate(cli, &createOptions{
Name: "dummy", name: "dummy",
Description: "dummy description", description: "dummy description",
Docker: map[string]string{ endpoint: map[string]string{
keyHost: "tcp://24.24.24.24:2375", keyHost: "tcp://24.24.24.24:2375",
}, },
})) }))
@ -211,11 +211,11 @@ func TestCreateFromContext(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cli.ResetOutputBuffers() cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
From: "original", from: "original",
Name: tc.name, name: tc.name,
Description: tc.description, description: tc.description,
Docker: tc.docker, endpoint: tc.docker,
}) })
assert.NilError(t, err) assert.NilError(t, err)
assertContextCreateLogging(t, cli, tc.name) assertContextCreateLogging(t, cli, tc.name)
@ -251,10 +251,10 @@ func TestCreateFromCurrent(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
cli.ResetOutputBuffers() cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{ assert.NilError(t, runCreate(cli, &createOptions{
Name: "original", name: "original",
Description: "original description", description: "original description",
Docker: map[string]string{ endpoint: map[string]string{
keyHost: "tcp://42.42.42.42:2375", keyHost: "tcp://42.42.42.42:2375",
}, },
})) }))
@ -265,9 +265,9 @@ func TestCreateFromCurrent(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cli.ResetOutputBuffers() cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: tc.name, name: tc.name,
Description: tc.description, description: tc.description,
}) })
assert.NilError(t, err) assert.NilError(t, err)
assertContextCreateLogging(t, cli, tc.name) assertContextCreateLogging(t, cli, tc.name)

View File

@ -21,14 +21,11 @@ func TestExportImportWithFile(t *testing.T) {
"MyCustomMetadata": t.Name(), "MyCustomMetadata": t.Name(),
}) })
cli.ErrBuffer().Reset() cli.ErrBuffer().Reset()
assert.NilError(t, RunExport(cli, &ExportOptions{ assert.NilError(t, runExport(cli, "test", contextFile))
ContextName: "test",
Dest: contextFile,
}))
assert.Equal(t, cli.ErrBuffer().String(), fmt.Sprintf("Written file %q\n", contextFile)) assert.Equal(t, cli.ErrBuffer().String(), fmt.Sprintf("Written file %q\n", contextFile))
cli.OutBuffer().Reset() cli.OutBuffer().Reset()
cli.ErrBuffer().Reset() cli.ErrBuffer().Reset()
assert.NilError(t, RunImport(cli, "test2", contextFile)) assert.NilError(t, runImport(cli, "test2", contextFile))
context1, err := cli.ContextStore().GetMetadata("test") context1, err := cli.ContextStore().GetMetadata("test")
assert.NilError(t, err) assert.NilError(t, err)
context2, err := cli.ContextStore().GetMetadata("test2") context2, err := cli.ContextStore().GetMetadata("test2")
@ -55,15 +52,12 @@ func TestExportImportPipe(t *testing.T) {
}) })
cli.ErrBuffer().Reset() cli.ErrBuffer().Reset()
cli.OutBuffer().Reset() cli.OutBuffer().Reset()
assert.NilError(t, RunExport(cli, &ExportOptions{ assert.NilError(t, runExport(cli, "test", "-"))
ContextName: "test",
Dest: "-",
}))
assert.Equal(t, cli.ErrBuffer().String(), "") assert.Equal(t, cli.ErrBuffer().String(), "")
cli.SetIn(streams.NewIn(io.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes())))) cli.SetIn(streams.NewIn(io.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes()))))
cli.OutBuffer().Reset() cli.OutBuffer().Reset()
cli.ErrBuffer().Reset() cli.ErrBuffer().Reset()
assert.NilError(t, RunImport(cli, "test2", "-")) assert.NilError(t, runImport(cli, "test2", "-"))
context1, err := cli.ContextStore().GetMetadata("test") context1, err := cli.ContextStore().GetMetadata("test")
assert.NilError(t, err) assert.NilError(t, err)
context2, err := cli.ContextStore().GetMetadata("test2") context2, err := cli.ContextStore().GetMetadata("test2")
@ -88,6 +82,6 @@ func TestExportExistingFile(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
cli.ErrBuffer().Reset() cli.ErrBuffer().Reset()
assert.NilError(t, os.WriteFile(contextFile, []byte{}, 0o644)) 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)) assert.Assert(t, os.IsExist(err))
} }

View File

@ -13,6 +13,8 @@ import (
) )
// ExportOptions are the options used for exporting a context // ExportOptions are the options used for exporting a context
//
// Deprecated: this type was for internal use and will be removed in the next release.
type ExportOptions struct { type ExportOptions struct {
ContextName string ContextName string
Dest string Dest string
@ -24,15 +26,14 @@ func newExportCommand(dockerCLI command.Cli) *cobra.Command {
Short: "Export a context to a tar archive FILE or a tar stream on STDOUT.", Short: "Export a context to a tar archive FILE or a tar stream on STDOUT.",
Args: cli.RequiresRangeArgs(1, 2), Args: cli.RequiresRangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts := &ExportOptions{ contextName := args[0]
ContextName: args[0], var dest string
}
if len(args) == 2 { if len(args) == 2 {
opts.Dest = args[1] dest = args[1]
} else { } else {
opts.Dest = opts.ContextName + ".dockercontext" dest = contextName + ".dockercontext"
} }
return RunExport(dockerCLI, opts) return runExport(dockerCLI, contextName, dest)
}, },
ValidArgsFunction: completeContextNames(dockerCLI, 1, true), ValidArgsFunction: completeContextNames(dockerCLI, 1, true),
} }
@ -65,11 +66,21 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error {
} }
// RunExport exports a Docker context // RunExport exports a Docker context
//
// Deprecated: this function was for internal use and will be removed in the next release.
func RunExport(dockerCli command.Cli, opts *ExportOptions) error { func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
if err := store.ValidateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName { if opts == nil {
opts = &ExportOptions{}
}
return runExport(dockerCli, opts.ContextName, opts.Dest)
}
// runExport exports a Docker context.
func runExport(dockerCLI command.Cli, contextName string, dest string) error {
if err := store.ValidateContextName(contextName); err != nil && contextName != command.DefaultContextName {
return err return err
} }
reader := store.Export(opts.ContextName, dockerCli.ContextStore()) reader := store.Export(contextName, dockerCLI.ContextStore())
defer reader.Close() defer reader.Close()
return writeTo(dockerCli, reader, opts.Dest) return writeTo(dockerCLI, reader, dest)
} }

View File

@ -18,7 +18,7 @@ func newImportCommand(dockerCli command.Cli) *cobra.Command {
Short: "Import a context from a tar or zip file", Short: "Import a context from a tar or zip file",
Args: cli.ExactArgs(2), Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return RunImport(dockerCli, args[0], args[1]) return runImport(dockerCli, args[0], args[1])
}, },
// TODO(thaJeztah): this should also include "-" // TODO(thaJeztah): this should also include "-"
ValidArgsFunction: completion.FileNames, ValidArgsFunction: completion.FileNames,
@ -27,14 +27,21 @@ func newImportCommand(dockerCli command.Cli) *cobra.Command {
} }
// RunImport imports a Docker context // RunImport imports a Docker context
func RunImport(dockerCli command.Cli, name string, source string) error { //
if err := checkContextNameForCreation(dockerCli.ContextStore(), name); err != nil { // Deprecated: this function was for internal use and will be removed in the next release.
func RunImport(dockerCLI command.Cli, name string, source string) error {
return runImport(dockerCLI, name, source)
}
// runImport imports a Docker context.
func runImport(dockerCLI command.Cli, name string, source string) error {
if err := checkContextNameForCreation(dockerCLI.ContextStore(), name); err != nil {
return err return err
} }
var reader io.Reader var reader io.Reader
if source == "-" { if source == "-" {
reader = dockerCli.In() reader = dockerCLI.In()
} else { } else {
f, err := os.Open(source) f, err := os.Open(source)
if err != nil { if err != nil {
@ -44,11 +51,11 @@ func RunImport(dockerCli command.Cli, name string, source string) error {
reader = f reader = f
} }
if err := store.Import(name, dockerCli.ContextStore(), reader); err != nil { if err := store.Import(name, dockerCLI.ContextStore(), reader); err != nil {
return err return err
} }
_, _ = fmt.Fprintln(dockerCli.Out(), name) _, _ = fmt.Fprintln(dockerCLI.Out(), name)
_, _ = fmt.Fprintf(dockerCli.Err(), "Successfully imported context %q\n", name) _, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully imported context %q\n", name)
return nil return nil
} }

View File

@ -19,10 +19,10 @@ func createTestContexts(t *testing.T, cli command.Cli, name ...string) {
func createTestContext(t *testing.T, cli command.Cli, name string, metaData map[string]any) { func createTestContext(t *testing.T, cli command.Cli, name string, metaData map[string]any) {
t.Helper() t.Helper()
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: name, name: name,
Description: "description of " + name, description: "description of " + name,
Docker: map[string]string{keyHost: "https://someswarmserver.example.com"}, endpoint: map[string]string{keyHost: "https://someswarmserver.example.com"},
metaData: metaData, metaData: metaData,
}) })

View File

@ -11,34 +11,48 @@ import (
) )
// RemoveOptions are the options used to remove contexts // RemoveOptions are the options used to remove contexts
//
// Deprecated: this type was for internal use and will be removed in the next release.
type RemoveOptions struct { type RemoveOptions struct {
Force bool Force bool
} }
// removeOptions are the options used to remove contexts.
type removeOptions struct {
force bool
}
func newRemoveCommand(dockerCLI command.Cli) *cobra.Command { func newRemoveCommand(dockerCLI command.Cli) *cobra.Command {
var opts RemoveOptions var opts removeOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "rm CONTEXT [CONTEXT...]", Use: "rm CONTEXT [CONTEXT...]",
Aliases: []string{"remove"}, Aliases: []string{"remove"},
Short: "Remove one or more contexts", Short: "Remove one or more contexts",
Args: cli.RequiresMinArgs(1), Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return RunRemove(dockerCLI, opts, args) return runRemove(dockerCLI, opts, args)
}, },
ValidArgsFunction: completeContextNames(dockerCLI, -1, false), ValidArgsFunction: completeContextNames(dockerCLI, -1, false),
} }
cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force the removal of a context in use") cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Force the removal of a context in use")
return cmd return cmd
} }
// RunRemove removes one or more contexts // RunRemove removes one or more contexts
func RunRemove(dockerCLI command.Cli, opts RemoveOptions, names []string) error { //
// Deprecated: this function was for internal use and will be removed in the next release.
func RunRemove(dockerCLI command.Cli, opts removeOptions, names []string) error {
return runRemove(dockerCLI, opts, names)
}
// runRemove removes one or more contexts.
func runRemove(dockerCLI command.Cli, opts removeOptions, names []string) error {
var errs []error var errs []error
currentCtx := dockerCLI.CurrentContext() currentCtx := dockerCLI.CurrentContext()
for _, name := range names { for _, name := range names {
if name == "default" { if name == "default" {
errs = append(errs, errors.New(`context "default" cannot be removed`)) errs = append(errs, errors.New(`context "default" cannot be removed`))
} else if err := doRemove(dockerCLI, name, name == currentCtx, opts.Force); err != nil { } else if err := doRemove(dockerCLI, name, name == currentCtx, opts.force); err != nil {
errs = append(errs, err) errs = append(errs, err)
} else { } else {
_, _ = fmt.Fprintln(dockerCLI.Out(), name) _, _ = fmt.Fprintln(dockerCLI.Out(), name)

View File

@ -14,7 +14,7 @@ import (
func TestRemove(t *testing.T) { func TestRemove(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
createTestContexts(t, cli, "current", "other") createTestContexts(t, cli, "current", "other")
assert.NilError(t, RunRemove(cli, RemoveOptions{}, []string{"other"})) assert.NilError(t, runRemove(cli, removeOptions{}, []string{"other"}))
_, err := cli.ContextStore().GetMetadata("current") _, err := cli.ContextStore().GetMetadata("current")
assert.NilError(t, err) assert.NilError(t, err)
_, err = cli.ContextStore().GetMetadata("other") _, err = cli.ContextStore().GetMetadata("other")
@ -24,10 +24,10 @@ func TestRemove(t *testing.T) {
func TestRemoveNotAContext(t *testing.T) { func TestRemoveNotAContext(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
createTestContexts(t, cli, "current", "other") createTestContexts(t, cli, "current", "other")
err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"}) err := runRemove(cli, removeOptions{}, []string{"not-a-context"})
assert.ErrorContains(t, err, `context "not-a-context" does not exist`) assert.ErrorContains(t, err, `context "not-a-context" does not exist`)
err = RunRemove(cli, RemoveOptions{Force: true}, []string{"not-a-context"}) err = runRemove(cli, removeOptions{force: true}, []string{"not-a-context"})
assert.NilError(t, err) assert.NilError(t, err)
} }
@ -35,7 +35,7 @@ func TestRemoveCurrent(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
createTestContexts(t, cli, "current", "other") createTestContexts(t, cli, "current", "other")
cli.SetCurrentContext("current") cli.SetCurrentContext("current")
err := RunRemove(cli, RemoveOptions{}, []string{"current"}) err := runRemove(cli, removeOptions{}, []string{"current"})
assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`) assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`)
} }
@ -49,7 +49,7 @@ func TestRemoveCurrentForce(t *testing.T) {
cli := makeFakeCli(t, withCliConfig(testCfg)) cli := makeFakeCli(t, withCliConfig(testCfg))
createTestContexts(t, cli, "current", "other") createTestContexts(t, cli, "current", "other")
cli.SetCurrentContext("current") cli.SetCurrentContext("current")
assert.NilError(t, RunRemove(cli, RemoveOptions{Force: true}, []string{"current"})) assert.NilError(t, runRemove(cli, removeOptions{force: true}, []string{"current"}))
reloadedConfig, err := config.Load(configDir) reloadedConfig, err := config.Load(configDir)
assert.NilError(t, err) assert.NilError(t, err)
assert.Equal(t, "", reloadedConfig.CurrentContext) assert.Equal(t, "", reloadedConfig.CurrentContext)
@ -59,6 +59,6 @@ func TestRemoveDefault(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
createTestContext(t, cli, "other", nil) createTestContext(t, cli, "other", nil)
cli.SetCurrentContext("current") cli.SetCurrentContext("current")
err := RunRemove(cli, RemoveOptions{}, []string{"default"}) err := runRemove(cli, removeOptions{}, []string{"default"})
assert.ErrorContains(t, err, `context "default" cannot be removed`) assert.ErrorContains(t, err, `context "default" cannot be removed`)
} }

View File

@ -13,12 +13,21 @@ import (
) )
// UpdateOptions are the options used to update a context // UpdateOptions are the options used to update a context
//
// Deprecated: this type was for internal use and will be removed in the next release.
type UpdateOptions struct { type UpdateOptions struct {
Name string Name string
Description string Description string
Docker map[string]string Docker map[string]string
} }
// updateOptions are the options used to update a context.
type updateOptions struct {
name string
description string
endpoint map[string]string
}
func longUpdateDescription() string { func longUpdateDescription() string {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
buf.WriteString("Update a context\n\nDocker endpoint config:\n\n") buf.WriteString("Update a context\n\nDocker endpoint config:\n\n")
@ -33,31 +42,45 @@ func longUpdateDescription() string {
} }
func newUpdateCommand(dockerCLI command.Cli) *cobra.Command { func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
opts := &UpdateOptions{} opts := updateOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update [OPTIONS] CONTEXT", Use: "update [OPTIONS] CONTEXT",
Short: "Update a context", Short: "Update a context",
Args: cli.ExactArgs(1), Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts.Name = args[0] opts.name = args[0]
return RunUpdate(dockerCLI, opts) return runUpdate(dockerCLI, &opts)
}, },
Long: longUpdateDescription(), Long: longUpdateDescription(),
ValidArgsFunction: completeContextNames(dockerCLI, 1, false), ValidArgsFunction: completeContextNames(dockerCLI, 1, false),
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVar(&opts.Description, "description", "", "Description of the context") flags.StringVar(&opts.description, "description", "", "Description of the context")
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint") flags.StringToStringVar(&opts.endpoint, "docker", nil, "set the docker endpoint")
return cmd return cmd
} }
// RunUpdate updates a Docker context // RunUpdate updates a Docker context
//
// Deprecated: this function was for internal use and will be removed in the next release.
func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error { func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
if err := store.ValidateContextName(o.Name); err != nil { if o == nil {
o = &UpdateOptions{}
}
return runUpdate(dockerCLI, &updateOptions{
name: o.Name,
description: o.Description,
endpoint: o.Docker,
})
}
// runUpdate updates a Docker context.
func runUpdate(dockerCLI command.Cli, opts *updateOptions) error {
if err := store.ValidateContextName(opts.name); err != nil {
return err return err
} }
s := dockerCLI.ContextStore() s := dockerCLI.ContextStore()
c, err := s.GetMetadata(o.Name) c, err := s.GetMetadata(opts.name)
if err != nil { if err != nil {
return err return err
} }
@ -65,16 +88,16 @@ func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
if err != nil { if err != nil {
return err return err
} }
if o.Description != "" { if opts.description != "" {
dockerContext.Description = o.Description dockerContext.Description = opts.description
} }
c.Metadata = dockerContext c.Metadata = dockerContext
tlsDataToReset := make(map[string]*store.EndpointTLSData) tlsDataToReset := make(map[string]*store.EndpointTLSData)
if o.Docker != nil { if opts.endpoint != nil {
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(s, o.Docker) dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(s, opts.endpoint)
if err != nil { if err != nil {
return fmt.Errorf("unable to create docker endpoint config: %w", err) return fmt.Errorf("unable to create docker endpoint config: %w", err)
} }
@ -88,13 +111,13 @@ func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
return err return err
} }
for ep, tlsData := range tlsDataToReset { for ep, tlsData := range tlsDataToReset {
if err := s.ResetEndpointTLSMaterial(o.Name, ep, tlsData); err != nil { if err := s.ResetEndpointTLSMaterial(opts.name, ep, tlsData); err != nil {
return err return err
} }
} }
_, _ = fmt.Fprintln(dockerCLI.Out(), o.Name) _, _ = fmt.Fprintln(dockerCLI.Out(), opts.name)
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully updated context %q\n", o.Name) _, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully updated context %q\n", opts.name)
return nil return nil
} }

View File

@ -11,16 +11,16 @@ import (
func TestUpdateDescriptionOnly(t *testing.T) { func TestUpdateDescriptionOnly(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: "test", name: "test",
Docker: map[string]string{}, endpoint: map[string]string{},
}) })
assert.NilError(t, err) assert.NilError(t, err)
cli.OutBuffer().Reset() cli.OutBuffer().Reset()
cli.ErrBuffer().Reset() cli.ErrBuffer().Reset()
assert.NilError(t, RunUpdate(cli, &UpdateOptions{ assert.NilError(t, runUpdate(cli, &updateOptions{
Name: "test", name: "test",
Description: "description", description: "description",
})) }))
c, err := cli.ContextStore().GetMetadata("test") c, err := cli.ContextStore().GetMetadata("test")
assert.NilError(t, err) assert.NilError(t, err)
@ -35,9 +35,9 @@ func TestUpdateDescriptionOnly(t *testing.T) {
func TestUpdateDockerOnly(t *testing.T) { func TestUpdateDockerOnly(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
createTestContext(t, cli, "test", nil) createTestContext(t, cli, "test", nil)
assert.NilError(t, RunUpdate(cli, &UpdateOptions{ assert.NilError(t, runUpdate(cli, &updateOptions{
Name: "test", name: "test",
Docker: map[string]string{ endpoint: map[string]string{
keyHost: "tcp://some-host", keyHost: "tcp://some-host",
}, },
})) }))
@ -52,14 +52,14 @@ func TestUpdateDockerOnly(t *testing.T) {
func TestUpdateInvalidDockerHost(t *testing.T) { func TestUpdateInvalidDockerHost(t *testing.T) {
cli := makeFakeCli(t) cli := makeFakeCli(t)
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: "test", name: "test",
Docker: map[string]string{}, endpoint: map[string]string{},
}) })
assert.NilError(t, err) assert.NilError(t, err)
err = RunUpdate(cli, &UpdateOptions{ err = runUpdate(cli, &updateOptions{
Name: "test", name: "test",
Docker: map[string]string{ endpoint: map[string]string{
keyHost: "some///invalid/host", keyHost: "some///invalid/host",
}, },
}) })

View File

@ -17,7 +17,7 @@ func newUseCommand(dockerCLI command.Cli) *cobra.Command {
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
name := args[0] name := args[0]
return RunUse(dockerCLI, name) return runUse(dockerCLI, name)
}, },
ValidArgsFunction: completeContextNames(dockerCLI, 1, false), ValidArgsFunction: completeContextNames(dockerCLI, 1, false),
} }
@ -25,7 +25,14 @@ func newUseCommand(dockerCLI command.Cli) *cobra.Command {
} }
// RunUse set the current Docker context // RunUse set the current Docker context
//
// Deprecated: this function was for internal use and will be removed in the next release.
func RunUse(dockerCLI command.Cli, name string) error { func RunUse(dockerCLI command.Cli, name string) error {
return runUse(dockerCLI, name)
}
// runUse set the current Docker context
func runUse(dockerCLI command.Cli, name string) error {
// configValue uses an empty string for "default" // configValue uses an empty string for "default"
var configValue string var configValue string
if name != command.DefaultContextName { if name != command.DefaultContextName {

View File

@ -23,9 +23,9 @@ func TestUse(t *testing.T) {
configFilePath := filepath.Join(configDir, "config.json") configFilePath := filepath.Join(configDir, "config.json")
testCfg := configfile.New(configFilePath) testCfg := configfile.New(configFilePath)
cli := makeFakeCli(t, withCliConfig(testCfg)) cli := makeFakeCli(t, withCliConfig(testCfg))
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: "test", name: "test",
Docker: map[string]string{}, endpoint: map[string]string{},
}) })
assert.NilError(t, err) assert.NilError(t, err)
assert.NilError(t, newUseCommand(cli).RunE(nil, []string{"test"})) assert.NilError(t, newUseCommand(cli).RunE(nil, []string{"test"}))
@ -89,9 +89,9 @@ func TestUseHostOverride(t *testing.T) {
configFilePath := filepath.Join(configDir, "config.json") configFilePath := filepath.Join(configDir, "config.json")
testCfg := configfile.New(configFilePath) testCfg := configfile.New(configFilePath)
cli := makeFakeCli(t, withCliConfig(testCfg)) cli := makeFakeCli(t, withCliConfig(testCfg))
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: "test", name: "test",
Docker: map[string]string{}, endpoint: map[string]string{},
}) })
assert.NilError(t, err) assert.NilError(t, err)
@ -136,9 +136,9 @@ func TestUseHostOverrideEmpty(t *testing.T) {
assert.NilError(t, cli.Initialize(flags.NewClientOptions())) assert.NilError(t, cli.Initialize(flags.NewClientOptions()))
} }
loadCli() loadCli()
err := RunCreate(cli, &CreateOptions{ err := runCreate(cli, &createOptions{
Name: "test", name: "test",
Docker: map[string]string{"host": socketPath}, endpoint: map[string]string{"host": socketPath},
}) })
assert.NilError(t, err) assert.NilError(t, err)