瀏覽代碼

Merge pull request #92 from docker/chore-cmd-cleanup

Chore cmd cleanup
Guillaume Tardif 5 年之前
父節點
當前提交
230cccff76

+ 29 - 42
cli/cmd/compose/compose.go

@@ -1,10 +1,34 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
 package compose
 
 import (
 	"github.com/spf13/cobra"
-
-	"github.com/docker/api/client"
-	"github.com/docker/api/compose"
 )
 
 // Command returns the compose command with its child commands
@@ -13,48 +37,11 @@ func Command() *cobra.Command {
 		Short: "Docker Compose",
 		Use:   "compose",
 	}
+
 	command.AddCommand(
 		upCommand(),
 		downCommand(),
 	)
-	return command
-}
 
-func upCommand() *cobra.Command {
-	opts := &compose.ProjectOptions{}
-	upCmd := &cobra.Command{
-		Use: "up",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			c, err := client.New(cmd.Context())
-			if err != nil {
-				return err
-			}
-			return c.ComposeService().Up(cmd.Context(), *opts)
-		},
-	}
-	upCmd.Flags().StringVar(&opts.Name, "name", "", "Project name")
-	upCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
-	upCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
-	upCmd.Flags().StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
-
-	return upCmd
-}
-
-func downCommand() *cobra.Command {
-	opts := &compose.ProjectOptions{}
-	downCmd := &cobra.Command{
-		Use: "down",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			c, err := client.New(cmd.Context())
-			if err != nil {
-				return err
-			}
-			return c.ComposeService().Down(cmd.Context(), *opts)
-		},
-	}
-	downCmd.Flags().StringVar(&opts.Name, "name", "", "Project name")
-	downCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
-	downCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
-
-	return downCmd
+	return command
 }

+ 67 - 0
cli/cmd/compose/down.go

@@ -0,0 +1,67 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package compose
+
+import (
+	"context"
+	"errors"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/client"
+	"github.com/docker/api/compose"
+)
+
+func downCommand() *cobra.Command {
+	opts := compose.ProjectOptions{}
+	downCmd := &cobra.Command{
+		Use: "down",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runDown(cmd.Context(), opts)
+		},
+	}
+	downCmd.Flags().StringVar(&opts.Name, "name", "", "Project name")
+	downCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
+	downCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
+
+	return downCmd
+}
+
+func runDown(ctx context.Context, opts compose.ProjectOptions) error {
+	c, err := client.New(ctx)
+	if err != nil {
+		return err
+	}
+
+	composeService := c.ComposeService()
+	if composeService == nil {
+		return errors.New("compose not implemented in current context")
+	}
+
+	return composeService.Down(ctx, opts)
+}

+ 68 - 0
cli/cmd/compose/up.go

@@ -0,0 +1,68 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package compose
+
+import (
+	"context"
+	"errors"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/client"
+	"github.com/docker/api/compose"
+)
+
+func upCommand() *cobra.Command {
+	opts := compose.ProjectOptions{}
+	upCmd := &cobra.Command{
+		Use: "up",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runUp(cmd.Context(), opts)
+		},
+	}
+	upCmd.Flags().StringVar(&opts.Name, "name", "", "Project name")
+	upCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
+	upCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
+	upCmd.Flags().StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
+
+	return upCmd
+}
+
+func runUp(ctx context.Context, opts compose.ProjectOptions) error {
+	c, err := client.New(ctx)
+	if err != nil {
+		return err
+	}
+
+	composeService := c.ComposeService()
+	if composeService == nil {
+		return errors.New("compose not implemented in current context")
+	}
+
+	return composeService.Up(ctx, opts)
+}

+ 0 - 193
cli/cmd/context.go

@@ -1,193 +0,0 @@
-/*
-	Copyright (c) 2020 Docker Inc.
-
-	Permission is hereby granted, free of charge, to any person
-	obtaining a copy of this software and associated documentation
-	files (the "Software"), to deal in the Software without
-	restriction, including without limitation the rights to use, copy,
-	modify, merge, publish, distribute, sublicense, and/or sell copies
-	of the Software, and to permit persons to whom the Software is
-	furnished to do so, subject to the following conditions:
-
-	The above copyright notice and this permission notice shall be
-	included in all copies or substantial portions of the Software.
-
-	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-	EXPRESS OR IMPLIED,
-	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-	HOLDERS BE LIABLE FOR ANY CLAIM,
-	DAMAGES OR OTHER LIABILITY,
-	WHETHER IN AN ACTION OF CONTRACT,
-	TORT OR OTHERWISE,
-	ARISING FROM, OUT OF OR IN CONNECTION WITH
-	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-package cmd
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"text/tabwriter"
-
-	"github.com/spf13/cobra"
-
-	cliconfig "github.com/docker/api/cli/config"
-	cliopts "github.com/docker/api/cli/options"
-	"github.com/docker/api/context/store"
-	"github.com/docker/api/multierror"
-)
-
-// ContextCommand manages contexts
-func ContextCommand(opts *cliopts.GlobalOpts) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "context",
-		Short: "Manage contexts",
-	}
-
-	cmd.AddCommand(
-		createCommand(),
-		listCommand(),
-		removeCommand(),
-		useCommand(opts),
-	)
-
-	return cmd
-}
-
-type createOpts struct {
-	description       string
-	aciLocation       string
-	aciSubscriptionID string
-	aciResourceGroup  string
-}
-
-func createCommand() *cobra.Command {
-	var opts createOpts
-	cmd := &cobra.Command{
-		Use:   "create CONTEXT BACKEND [OPTIONS]",
-		Short: "Create a context",
-		Args:  cobra.ExactArgs(2),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			return runCreate(cmd.Context(), opts, args[0], args[1])
-		},
-	}
-
-	cmd.Flags().StringVar(&opts.description, "description", "", "Description of the context")
-	cmd.Flags().StringVar(&opts.aciLocation, "aci-location", "eastus", "Location")
-	cmd.Flags().StringVar(&opts.aciSubscriptionID, "aci-subscription-id", "", "Location")
-	cmd.Flags().StringVar(&opts.aciResourceGroup, "aci-resource-group", "", "Resource group")
-
-	return cmd
-}
-
-func listCommand() *cobra.Command {
-	cmd := &cobra.Command{
-		Use:     "list",
-		Short:   "List available contexts",
-		Aliases: []string{"ls"},
-		Args:    cobra.NoArgs,
-		RunE: func(cmd *cobra.Command, args []string) error {
-			return runList(cmd.Context())
-		},
-	}
-	return cmd
-}
-
-func removeCommand() *cobra.Command {
-	return &cobra.Command{
-		Use:     "rm CONTEXT [CONTEXT...]",
-		Short:   "Remove one or more contexts",
-		Aliases: []string{"remove"},
-		Args:    cobra.MinimumNArgs(1),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			return runRemove(cmd.Context(), args)
-		},
-	}
-}
-
-func useCommand(opts *cliopts.GlobalOpts) *cobra.Command {
-	return &cobra.Command{
-		Use:   "use CONTEXT",
-		Short: "Set the default context",
-		Args:  cobra.ExactArgs(1),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			return runUse(cmd.Context(), opts.Config, args[0])
-		},
-	}
-}
-
-func runCreate(ctx context.Context, opts createOpts, name string, contextType string) error {
-	switch contextType {
-	case "aci":
-		return createACIContext(ctx, name, opts)
-	default:
-		s := store.ContextStore(ctx)
-		return s.Create(name, store.TypedContext{
-			Type:        contextType,
-			Description: opts.description,
-		})
-	}
-}
-
-func createACIContext(ctx context.Context, name string, opts createOpts) error {
-	s := store.ContextStore(ctx)
-	return s.Create(name, store.TypedContext{
-		Type:        "aci",
-		Description: opts.description,
-		Data: store.AciContext{
-			SubscriptionID: opts.aciSubscriptionID,
-			Location:       opts.aciLocation,
-			ResourceGroup:  opts.aciResourceGroup,
-		},
-	})
-}
-
-func runList(ctx context.Context) error {
-	s := store.ContextStore(ctx)
-	contexts, err := s.List()
-	if err != nil {
-		return err
-	}
-
-	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
-	fmt.Fprintln(w, "NAME\tDESCRIPTION\tTYPE")
-	format := "%s\t%s\t%s\n"
-
-	for _, c := range contexts {
-		fmt.Fprintf(w, format, c.Name, c.Metadata.Description, c.Metadata.Type)
-	}
-
-	return w.Flush()
-}
-
-func runRemove(ctx context.Context, args []string) error {
-	s := store.ContextStore(ctx)
-	var errs *multierror.Error
-	for _, n := range args {
-		if err := s.Remove(n); err != nil {
-			errs = multierror.Append(errs, err)
-		} else {
-			fmt.Println(n)
-		}
-	}
-	return errs.ErrorOrNil()
-}
-
-func runUse(ctx context.Context, configDir string, name string) error {
-	s := store.ContextStore(ctx)
-	// Match behavior of existing CLI
-	if name != store.DefaultContextName {
-		if _, err := s.Get(name, nil); err != nil {
-			return err
-		}
-	}
-	if err := cliconfig.WriteCurrentContext(configDir, name); err != nil {
-		return err
-	}
-	fmt.Println(name)
-	return nil
-}

+ 51 - 0
cli/cmd/context/context.go

@@ -0,0 +1,51 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package context
+
+import (
+	"github.com/spf13/cobra"
+
+	cliopts "github.com/docker/api/cli/options"
+)
+
+// Command manages contexts
+func Command(opts *cliopts.GlobalOpts) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "context",
+		Short: "Manage contexts",
+	}
+
+	cmd.AddCommand(
+		createCommand(),
+		listCommand(),
+		removeCommand(),
+		useCommand(opts),
+	)
+
+	return cmd
+}

+ 75 - 0
cli/cmd/context/create.go

@@ -0,0 +1,75 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package context
+
+import (
+	"context"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/context/store"
+)
+
+type createOpts struct {
+	description       string
+	aciLocation       string
+	aciSubscriptionID string
+	aciResourceGroup  string
+}
+
+func createCommand() *cobra.Command {
+	var opts createOpts
+	cmd := &cobra.Command{
+		Use:   "create CONTEXT BACKEND [OPTIONS]",
+		Short: "Create a context",
+		Args:  cobra.ExactArgs(2),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runCreate(cmd.Context(), opts, args[0], args[1])
+		},
+	}
+
+	cmd.Flags().StringVar(&opts.description, "description", "", "Description of the context")
+	cmd.Flags().StringVar(&opts.aciLocation, "aci-location", "eastus", "Location")
+	cmd.Flags().StringVar(&opts.aciSubscriptionID, "aci-subscription-id", "", "Location")
+	cmd.Flags().StringVar(&opts.aciResourceGroup, "aci-resource-group", "", "Resource group")
+
+	return cmd
+}
+
+func runCreate(ctx context.Context, opts createOpts, name string, contextType string) error {
+	switch contextType {
+	case "aci":
+		return createACIContext(ctx, name, opts)
+	default:
+		s := store.ContextStore(ctx)
+		return s.Create(name, store.TypedContext{
+			Type:        contextType,
+			Description: opts.description,
+		})
+	}
+}

+ 47 - 0
cli/cmd/context/createaci.go

@@ -0,0 +1,47 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package context
+
+import (
+	"context"
+
+	"github.com/docker/api/context/store"
+)
+
+func createACIContext(ctx context.Context, name string, opts createOpts) error {
+	s := store.ContextStore(ctx)
+	return s.Create(name, store.TypedContext{
+		Type:        "aci",
+		Description: opts.description,
+		Data: store.AciContext{
+			SubscriptionID: opts.aciSubscriptionID,
+			Location:       opts.aciLocation,
+			ResourceGroup:  opts.aciResourceGroup,
+		},
+	})
+}

+ 70 - 0
cli/cmd/context/ls.go

@@ -0,0 +1,70 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package context
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"text/tabwriter"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/context/store"
+)
+
+func listCommand() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:     "list",
+		Short:   "List available contexts",
+		Aliases: []string{"ls"},
+		Args:    cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runList(cmd.Context())
+		},
+	}
+	return cmd
+}
+
+func runList(ctx context.Context) error {
+	s := store.ContextStore(ctx)
+	contexts, err := s.List()
+	if err != nil {
+		return err
+	}
+
+	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
+	fmt.Fprintln(w, "NAME\tDESCRIPTION\tTYPE")
+	format := "%s\t%s\t%s\n"
+
+	for _, c := range contexts {
+		fmt.Fprintf(w, format, c.Name, c.Metadata.Description, c.Metadata.Type)
+	}
+
+	return w.Flush()
+}

+ 63 - 0
cli/cmd/context/rm.go

@@ -0,0 +1,63 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package context
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/context/store"
+	"github.com/docker/api/multierror"
+)
+
+func removeCommand() *cobra.Command {
+	return &cobra.Command{
+		Use:     "rm CONTEXT [CONTEXT...]",
+		Short:   "Remove one or more contexts",
+		Aliases: []string{"remove"},
+		Args:    cobra.MinimumNArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runRemove(cmd.Context(), args)
+		},
+	}
+}
+
+func runRemove(ctx context.Context, args []string) error {
+	s := store.ContextStore(ctx)
+	var errs *multierror.Error
+	for _, n := range args {
+		if err := s.Remove(n); err != nil {
+			errs = multierror.Append(errs, err)
+		} else {
+			fmt.Println(n)
+		}
+	}
+	return errs.ErrorOrNil()
+}

+ 65 - 0
cli/cmd/context/use.go

@@ -0,0 +1,65 @@
+/*
+	Copyright (c) 2020 Docker Inc.
+
+	Permission is hereby granted, free of charge, to any person
+	obtaining a copy of this software and associated documentation
+	files (the "Software"), to deal in the Software without
+	restriction, including without limitation the rights to use, copy,
+	modify, merge, publish, distribute, sublicense, and/or sell copies
+	of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be
+	included in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED,
+	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+	HOLDERS BE LIABLE FOR ANY CLAIM,
+	DAMAGES OR OTHER LIABILITY,
+	WHETHER IN AN ACTION OF CONTRACT,
+	TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH
+	THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package context
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	cliconfig "github.com/docker/api/cli/config"
+	cliopts "github.com/docker/api/cli/options"
+	"github.com/docker/api/context/store"
+)
+
+func useCommand(opts *cliopts.GlobalOpts) *cobra.Command {
+	return &cobra.Command{
+		Use:   "use CONTEXT",
+		Short: "Set the default context",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runUse(cmd.Context(), opts.Config, args[0])
+		},
+	}
+}
+
+func runUse(ctx context.Context, configDir string, name string) error {
+	s := store.ContextStore(ctx)
+	// Match behavior of existing CLI
+	if name != store.DefaultContextName {
+		if _, err := s.Get(name, nil); err != nil {
+			return err
+		}
+	}
+	if err := cliconfig.WriteCurrentContext(configDir, name); err != nil {
+		return err
+	}
+	fmt.Println(name)
+	return nil
+}

+ 21 - 16
cli/cmd/rm.go

@@ -1,6 +1,7 @@
 package cmd
 
 import (
+	"context"
 	"fmt"
 
 	"github.com/pkg/errors"
@@ -23,22 +24,7 @@ func RmCommand() *cobra.Command {
 		Short:   "Remove containers",
 		Args:    cobra.MinimumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			c, err := client.New(cmd.Context())
-			if err != nil {
-				return errors.Wrap(err, "cannot connect to backend")
-			}
-
-			var errs *multierror.Error
-			for _, id := range args {
-				err := c.ContainerService().Delete(cmd.Context(), id, opts.force)
-				if err != nil {
-					errs = multierror.Append(errs, err)
-					continue
-				}
-				fmt.Println(id)
-			}
-
-			return errs.ErrorOrNil()
+			return runRm(cmd.Context(), args, opts)
 		},
 	}
 
@@ -46,3 +32,22 @@ func RmCommand() *cobra.Command {
 
 	return cmd
 }
+
+func runRm(ctx context.Context, args []string, opts rmOpts) error {
+	c, err := client.New(ctx)
+	if err != nil {
+		return errors.Wrap(err, "cannot connect to backend")
+	}
+
+	var errs *multierror.Error
+	for _, id := range args {
+		err := c.ContainerService().Delete(ctx, id, opts.force)
+		if err != nil {
+			errs = multierror.Append(errs, err)
+			continue
+		}
+		fmt.Println(id)
+	}
+
+	return errs.ErrorOrNil()
+}

+ 7 - 6
cli/cmd/run/run.go

@@ -46,11 +46,7 @@ func Command() *cobra.Command {
 		Short: "Run a container",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			if err := runRun(cmd.Context(), args[0], opts); err != nil {
-				return err
-			}
-			fmt.Println(opts.name)
-			return nil
+			return runRun(cmd.Context(), args[0], opts)
 		},
 	}
 
@@ -71,7 +67,12 @@ func runRun(ctx context.Context, image string, opts runOpts) error {
 		return err
 	}
 
-	return c.ContainerService().Run(ctx, project)
+	if err = c.ContainerService().Run(ctx, project); err != nil {
+		return err
+	}
+	fmt.Println(opts.name)
+	return nil
+
 }
 
 func getRandomName() string {

+ 2 - 1
cli/main.go

@@ -47,6 +47,7 @@ import (
 
 	"github.com/docker/api/cli/cmd"
 	"github.com/docker/api/cli/cmd/compose"
+	contextcmd "github.com/docker/api/cli/cmd/context"
 	"github.com/docker/api/cli/cmd/run"
 	cliconfig "github.com/docker/api/cli/config"
 	cliopts "github.com/docker/api/cli/options"
@@ -100,7 +101,7 @@ func main() {
 	}
 
 	root.AddCommand(
-		cmd.ContextCommand(&opts),
+		contextcmd.Command(&opts),
 		cmd.PsCommand(),
 		cmd.ServeCommand(),
 		run.Command(),