1
0
Эх сурвалжийг харах

Merge pull request #204 from docker/context_create_refacto

Context create refacto
Guillaume Tardif 5 жил өмнө
parent
commit
3566c720e3

+ 1 - 1
Makefile

@@ -45,7 +45,7 @@ cli: ## Compile the cli
 	--output ./bin
 
 e2e-local: ## Run End to end local tests
-	go test -v ./tests/e2e ./tests/skip-win-ci-e2e ./moby/e2e
+	go test -v ./tests/e2e ./tests/skip-win-ci-e2e ./local/e2e
 
 e2e-win-ci: ## Run End to end local tests on windows CI, no docker for linux containers available ATM
 	go test -v ./tests/e2e

+ 88 - 45
cli/cmd/context/create.go

@@ -29,75 +29,118 @@ package context
 
 import (
 	"context"
-	"fmt"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
-	"github.com/docker/api/client"
+	"github.com/docker/api/cli/dockerclassic"
 	"github.com/docker/api/context/store"
 )
 
-// AciCreateOpts Options for ACI context create
-type AciCreateOpts struct {
-	description       string
-	aciLocation       string
-	aciSubscriptionID string
-	aciResourceGroup  string
+type descriptionCreateOpts struct {
+	description string
 }
 
 func createCommand() *cobra.Command {
-	var opts AciCreateOpts
+	const longHelp = `Create a new context
+
+Create docker engine context: 
+$ docker context create CONTEXT [flags]
+
+Create Azure Container Instances context:
+$ docker context create aci CONTEXT [flags]
+(see docker context create aci --help)
+
+Docker endpoint config:
+
+NAME                DESCRIPTION
+from                Copy named context's Docker endpoint configuration
+host                Docker endpoint on which to connect
+ca                  Trust certs signed only by this CA
+cert                Path to TLS certificate file
+key                 Path to TLS key file
+skip-tls-verify     Skip TLS certificate validation
+
+Kubernetes endpoint config:
+
+NAME                 DESCRIPTION
+from                 Copy named context's Kubernetes endpoint configuration
+config-file          Path to a Kubernetes config file
+context-override     Overrides the context set in the kubernetes config file
+namespace-override   Overrides the namespace set in the kubernetes config file
+
+Example:
+
+$ docker context create my-context --description "some description" --docker "host=tcp://myserver:2376,ca=~/ca-file,cert=~/cert-file,key=~/key-file"`
+
 	cmd := &cobra.Command{
-		Use:   "create CONTEXT BACKEND [OPTIONS]",
-		Short: "Create a context",
-		Args:  cobra.ExactArgs(2),
+		Use:   "create CONTEXT",
+		Short: "Create new context",
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runCreate(cmd.Context(), opts, args[0], args[1])
+			return dockerclassic.ExecCmd(cmd)
 		},
+		Long: longHelp,
 	}
 
-	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")
+	cmd.AddCommand(
+		createAciCommand(),
+		createLocalCommand(),
+		createExampleCommand(),
+	)
+
+	flags := cmd.Flags()
+	flags.String("description", "", "Description of the context")
+	flags.String(
+		"default-stack-orchestrator", "",
+		"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)")
+	flags.StringToString("docker", nil, "set the docker endpoint")
+	flags.StringToString("kubernetes", nil, "set the kubernetes endpoint")
+	flags.String("from", "", "create context from a named context")
+
+	return cmd
+}
 
+func createLocalCommand() *cobra.Command {
+	var opts descriptionCreateOpts
+	cmd := &cobra.Command{
+		Use:    "local CONTEXT",
+		Short:  "Create a context for accessing local engine",
+		Args:   cobra.ExactArgs(1),
+		Hidden: true,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return createDockerContext(cmd.Context(), args[0], store.LocalContextType, opts.description, store.LocalContext{})
+		},
+	}
+	addDescriptionFlag(cmd, &opts.description)
 	return cmd
 }
 
-func runCreate(ctx context.Context, opts AciCreateOpts, name string, contextType string) error {
-	contextData, description, err := getContextData(ctx, contextType, opts)
-	if err != nil {
-		return nil
+func createExampleCommand() *cobra.Command {
+	var opts descriptionCreateOpts
+	cmd := &cobra.Command{
+		Use:    "example CONTEXT",
+		Short:  "Create a test context returning fixed output",
+		Args:   cobra.ExactArgs(1),
+		Hidden: true,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return createDockerContext(cmd.Context(), args[0], store.ExampleContextType, opts.description, store.ExampleContext{})
+		},
 	}
+
+	addDescriptionFlag(cmd, &opts.description)
+	return cmd
+}
+
+func createDockerContext(ctx context.Context, name string, contextType string, description string, data interface{}) error {
 	s := store.ContextStore(ctx)
-	return s.Create(
+	result := s.Create(
 		name,
 		contextType,
 		description,
-		contextData,
+		data,
 	)
+	return result
 }
 
-func getContextData(ctx context.Context, contextType string, opts AciCreateOpts) (interface{}, string, error) {
-	switch contextType {
-	case "aci":
-		cs, err := client.GetCloudService(ctx, "aci")
-		if err != nil {
-			return nil, "", errors.Wrap(err, "cannot connect to ACI backend")
-		}
-		params := map[string]string{
-			"aciSubscriptionId": opts.aciSubscriptionID,
-			"aciResourceGroup":  opts.aciResourceGroup,
-			"aciLocation":       opts.aciLocation,
-			"description":       opts.description,
-		}
-		return cs.CreateContextData(ctx, params)
-	case "moby":
-		return store.MobyContext{}, opts.description, nil
-	case "example":
-		return store.ExampleContext{}, opts.description, nil
-	default:
-		return nil, "", errors.New(fmt.Sprintf("incorrect context type %s, must be one of (aci | moby | docker)", contextType))
-	}
+func addDescriptionFlag(cmd *cobra.Command, descriptionOpt *string) {
+	cmd.Flags().StringVar(descriptionOpt, "description", "", "Description of the context")
 }

+ 0 - 35
cli/cmd/context/create_test.go

@@ -1,35 +0,0 @@
-package context
-
-import (
-	"context"
-	"testing"
-
-	"github.com/docker/api/context/store"
-
-	. "github.com/onsi/gomega"
-	"github.com/stretchr/testify/suite"
-
-	_ "github.com/docker/api/example"
-	"github.com/docker/api/tests/framework"
-)
-
-type PsSuite struct {
-	framework.CliSuite
-}
-
-func (sut *PsSuite) TestCreateContextDataMoby() {
-	data, description, err := getContextData(context.TODO(), "moby", AciCreateOpts{})
-	Expect(err).To(BeNil())
-	Expect(data).To(Equal(store.MobyContext{}))
-	Expect(description).To(Equal(""))
-}
-
-func (sut *PsSuite) TestErrorOnUnknownContextType() {
-	_, _, err := getContextData(context.TODO(), "foo", AciCreateOpts{})
-	Expect(err).To(MatchError("incorrect context type foo, must be one of (aci | moby | docker)"))
-}
-
-func TestPs(t *testing.T) {
-	RegisterTestingT(t)
-	suite.Run(t, new(PsSuite))
-}

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

@@ -0,0 +1,85 @@
+/*
+	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/pkg/errors"
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/client"
+	"github.com/docker/api/context/store"
+)
+
+type aciCreateOpts struct {
+	description    string
+	location       string
+	subscriptionID string
+	resourceGroup  string
+}
+
+func createAciCommand() *cobra.Command {
+	var opts aciCreateOpts
+	cmd := &cobra.Command{
+		Use:   "aci CONTEXT [flags]",
+		Short: "Create a context for Azure Container Instances",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			contextData, description, err := getAciContextData(cmd.Context(), opts)
+			if err != nil {
+				return nil
+			}
+			return createDockerContext(cmd.Context(), args[0], store.AciContextType, description, contextData)
+		},
+	}
+
+	addDescriptionFlag(cmd, &opts.description)
+	cmd.Flags().StringVar(&opts.location, "location", "eastus", "Location")
+	cmd.Flags().StringVar(&opts.subscriptionID, "subscription-id", "", "Location")
+	cmd.Flags().StringVar(&opts.resourceGroup, "resource-group", "", "Resource group")
+
+	return cmd
+}
+
+func getAciContextData(ctx context.Context, opts aciCreateOpts) (interface{}, string, error) {
+	cs, err := client.GetCloudService(ctx, store.AciContextType)
+	if err != nil {
+		return nil, "", errors.Wrap(err, "cannot connect to ACI backend")
+	}
+	return cs.CreateContextData(ctx, convertAciOpts(opts))
+}
+
+func convertAciOpts(opts aciCreateOpts) map[string]string {
+	return map[string]string{
+		"aciSubscriptionId": opts.subscriptionID,
+		"aciResourceGroup":  opts.resourceGroup,
+		"aciLocation":       opts.location,
+		"description":       opts.description,
+	}
+}

+ 1 - 1
cli/cmd/context/ls.go

@@ -94,7 +94,7 @@ func getEndpoint(name string, meta map[string]interface{}) string {
 	if !ok {
 		return ""
 	}
-	data, ok := endpoints.(store.Endpoint)
+	data, ok := endpoints.(*store.Endpoint)
 	if !ok {
 		return ""
 	}

+ 1 - 1
cli/main.go

@@ -45,7 +45,7 @@ import (
 	// Backend registrations
 	_ "github.com/docker/api/azure"
 	_ "github.com/docker/api/example"
-	_ "github.com/docker/api/moby"
+	_ "github.com/docker/api/local"
 
 	"github.com/docker/api/cli/cmd"
 	"github.com/docker/api/cli/cmd/compose"

+ 3 - 3
config/config_test.go

@@ -39,7 +39,7 @@ import (
 
 var sampleConfig = []byte(`{
 	"otherField": "value",
-	"currentContext": "moby"
+	"currentContext": "local"
 }`)
 
 type ConfigTestSuite struct {
@@ -66,14 +66,14 @@ func (s *ConfigTestSuite) TestLoadFile() {
 	writeSampleConfig(s.T(), s.configDir)
 	f, err := LoadFile(s.configDir)
 	require.NoError(s.T(), err)
-	require.Equal(s.T(), "moby", f.CurrentContext)
+	require.Equal(s.T(), "local", f.CurrentContext)
 }
 
 func (s *ConfigTestSuite) TestOverWriteCurrentContext() {
 	writeSampleConfig(s.T(), s.configDir)
 	f, err := LoadFile(s.configDir)
 	require.NoError(s.T(), err)
-	require.Equal(s.T(), "moby", f.CurrentContext)
+	require.Equal(s.T(), "local", f.CurrentContext)
 
 	err = WriteCurrentContext(s.configDir, "overwrite")
 	require.NoError(s.T(), err)

+ 2 - 2
context/store/contextmetadata.go

@@ -33,8 +33,8 @@ type AciContext struct {
 	ResourceGroup  string `json:",omitempty"`
 }
 
-// MobyContext is the context for the moby backend
-type MobyContext struct{}
+// LocalContext is the context for the local backend
+type LocalContext struct{}
 
 // ExampleContext is the context for the example backend
 type ExampleContext struct{}

+ 11 - 9
context/store/store.go

@@ -96,9 +96,9 @@ const (
 	// AciContextType is the endpoint key in the context endpoints for an ACI
 	// backend
 	AciContextType = "aci"
-	// MobyContextType is the endpoint key in the context endpoints for a moby
-	// backend
-	MobyContextType = "moby"
+	// LocalContextType is the endpoint key in the context endpoints for a new
+	// local backend
+	LocalContextType = "local"
 	// ExampleContextType is the endpoint key in the context endpoints for an
 	// example backend
 	ExampleContextType = "example"
@@ -211,12 +211,14 @@ func toTypedEndpoints(endpoints map[string]interface{}) (map[string]interface{},
 			return nil, err
 		}
 		typeGetters := getters()
-		if _, ok := typeGetters[k]; !ok {
-			result[k] = v
-			continue
+		typeGetter, ok := typeGetters[k]
+		if !ok {
+			typeGetter = func() interface{} {
+				return &Endpoint{}
+			}
 		}
 
-		val := typeGetters[k]()
+		val := typeGetter()
 		err = json.Unmarshal(bytes, &val)
 		if err != nil {
 			return nil, err
@@ -333,8 +335,8 @@ func getters() map[string]func() interface{} {
 		"aci": func() interface{} {
 			return &AciContext{}
 		},
-		"moby": func() interface{} {
-			return &MobyContext{}
+		"local": func() interface{} {
+			return &LocalContext{}
 		},
 		"example": func() interface{} {
 			return &ExampleContext{}

+ 3 - 3
context/store/storedefault.go

@@ -8,7 +8,7 @@ import (
 	"github.com/pkg/errors"
 )
 
-const defaultContextType = "docker"
+const defaultContextType = "moby"
 
 // Represents a context as created by the docker cli
 type defaultContext struct {
@@ -58,10 +58,10 @@ func dockerDefaultContext() (*DockerContext, error) {
 	meta := DockerContext{
 		Name: "default",
 		Endpoints: map[string]interface{}{
-			"docker": Endpoint{
+			"docker": &Endpoint{
 				Host: defaultCtx.Endpoints.Docker.Host,
 			},
-			"kubernetes": Endpoint{
+			"kubernetes": &Endpoint{
 				Host:             defaultCtx.Endpoints.Kubernetes.Host,
 				DefaultNamespace: defaultCtx.Endpoints.Kubernetes.DefaultNamespace,
 			},

+ 1 - 1
docs/cli/overview-of-the-cli.md

@@ -26,7 +26,7 @@ Insert a really small tutorial or links here.
 We have made some changes to the syntax of a few commands to make them easier to understand. Where we still support the old
 forms, the command line will tell you the new form, but will still work correctly. In cases where we remove the old
 form you will get help text. If we remove a verb, for example "docker stack" we will display a message saying that the command
-is only available with the Moby backend. For example
+is only available with the Local backend. For example
 
 ```
 > docker context create my-context --description "some description" --docker "host=tcp://myserver:2376"

+ 12 - 12
moby/backend.go → local/backend.go

@@ -1,4 +1,4 @@
-package moby
+package local
 
 import (
 	"bufio"
@@ -24,12 +24,12 @@ import (
 	"github.com/docker/api/errdefs"
 )
 
-type mobyService struct {
+type local struct {
 	apiClient *client.Client
 }
 
 func init() {
-	backend.Register("moby", "moby", service, cloud.NotImplementedCloudService)
+	backend.Register("local", "local", service, cloud.NotImplementedCloudService)
 }
 
 func service(ctx context.Context) (backend.Service, error) {
@@ -38,20 +38,20 @@ func service(ctx context.Context) (backend.Service, error) {
 		return nil, err
 	}
 
-	return &mobyService{
+	return &local{
 		apiClient,
 	}, nil
 }
 
-func (ms *mobyService) ContainerService() containers.Service {
+func (ms *local) ContainerService() containers.Service {
 	return ms
 }
 
-func (ms *mobyService) ComposeService() compose.Service {
+func (ms *local) ComposeService() compose.Service {
 	return nil
 }
 
-func (ms *mobyService) List(ctx context.Context, all bool) ([]containers.Container, error) {
+func (ms *local) List(ctx context.Context, all bool) ([]containers.Container, error) {
 	css, err := ms.apiClient.ContainerList(ctx, types.ContainerListOptions{
 		All: all,
 	})
@@ -78,7 +78,7 @@ func (ms *mobyService) List(ctx context.Context, all bool) ([]containers.Contain
 	return result, nil
 }
 
-func (ms *mobyService) Run(ctx context.Context, r containers.ContainerConfig) error {
+func (ms *local) Run(ctx context.Context, r containers.ContainerConfig) error {
 	exposedPorts, hostBindings, err := fromPorts(r.Ports)
 	if err != nil {
 		return err
@@ -125,7 +125,7 @@ func (ms *mobyService) Run(ctx context.Context, r containers.ContainerConfig) er
 	return ms.apiClient.ContainerStart(ctx, created.ID, types.ContainerStartOptions{})
 }
 
-func (ms *mobyService) Stop(ctx context.Context, containerID string, timeout *uint32) error {
+func (ms *local) Stop(ctx context.Context, containerID string, timeout *uint32) error {
 	var t *time.Duration
 	if timeout != nil {
 		timeoutValue := time.Duration(*timeout) * time.Second
@@ -134,7 +134,7 @@ func (ms *mobyService) Stop(ctx context.Context, containerID string, timeout *ui
 	return ms.apiClient.ContainerStop(ctx, containerID, t)
 }
 
-func (ms *mobyService) Exec(ctx context.Context, name string, command string, reader io.Reader, writer io.Writer) error {
+func (ms *local) Exec(ctx context.Context, name string, command string, reader io.Reader, writer io.Writer) error {
 	cec, err := ms.apiClient.ContainerExecCreate(ctx, name, types.ExecConfig{
 		Cmd:          []string{command},
 		Tty:          true,
@@ -176,7 +176,7 @@ func (ms *mobyService) Exec(ctx context.Context, name string, command string, re
 	}
 }
 
-func (ms *mobyService) Logs(ctx context.Context, containerName string, request containers.LogsRequest) error {
+func (ms *local) Logs(ctx context.Context, containerName string, request containers.LogsRequest) error {
 	c, err := ms.apiClient.ContainerInspect(ctx, containerName)
 	if err != nil {
 		return err
@@ -204,7 +204,7 @@ func (ms *mobyService) Logs(ctx context.Context, containerName string, request c
 	return err
 }
 
-func (ms *mobyService) Delete(ctx context.Context, containerID string, force bool) error {
+func (ms *local) Delete(ctx context.Context, containerID string, force bool) error {
 	err := ms.apiClient.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{
 		Force: force,
 	})

+ 9 - 9
moby/e2e/backend_test.go → local/e2e/backend_test.go

@@ -11,26 +11,26 @@ import (
 	"github.com/docker/api/tests/framework"
 )
 
-type MobyBackendTestSuite struct {
+type LocalBackendTestSuite struct {
 	framework.Suite
 }
 
-func (m *MobyBackendTestSuite) BeforeTest(suiteName string, testName string) {
-	m.NewDockerCommand("context", "create", "test-context", "moby").ExecOrDie()
+func (m *LocalBackendTestSuite) BeforeTest(suiteName string, testName string) {
+	m.NewDockerCommand("context", "create", "local", "test-context").ExecOrDie()
 	m.NewDockerCommand("context", "use", "test-context").ExecOrDie()
 }
 
-func (m *MobyBackendTestSuite) AfterTest(suiteName string, testName string) {
+func (m *LocalBackendTestSuite) AfterTest(suiteName string, testName string) {
 	m.NewDockerCommand("context", "rm", "test-context").ExecOrDie()
 	m.NewDockerCommand("context", "use", "default").ExecOrDie()
 }
 
-func (m *MobyBackendTestSuite) TestPs() {
+func (m *LocalBackendTestSuite) TestPs() {
 	out := m.NewDockerCommand("ps").ExecOrDie()
 	require.Equal(m.T(), "CONTAINER ID        IMAGE               COMMAND             STATUS              PORTS\n", out)
 }
 
-func (m *MobyBackendTestSuite) TestRun() {
+func (m *LocalBackendTestSuite) TestRun() {
 	_, err := m.NewDockerCommand("run", "--name", "nginx", "nginx").Exec()
 	require.Nil(m.T(), err)
 	out := m.NewDockerCommand("ps").ExecOrDie()
@@ -41,7 +41,7 @@ func (m *MobyBackendTestSuite) TestRun() {
 	assert.Equal(m.T(), 3, len(lines))
 }
 
-func (m *MobyBackendTestSuite) TestRunWithPorts() {
+func (m *LocalBackendTestSuite) TestRunWithPorts() {
 	_, err := m.NewDockerCommand("run", "--name", "nginx", "-p", "8080:80", "nginx").Exec()
 	require.Nil(m.T(), err)
 	out := m.NewDockerCommand("ps").ExecOrDie()
@@ -51,6 +51,6 @@ func (m *MobyBackendTestSuite) TestRunWithPorts() {
 	assert.Contains(m.T(), out, "8080")
 }
 
-func TestMobyBackendTestSuite(t *testing.T) {
-	suite.Run(t, new(MobyBackendTestSuite))
+func TestLocalBackendTestSuite(t *testing.T) {
+	suite.Run(t, new(LocalBackendTestSuite))
 }

+ 1 - 11
tests/aci-e2e/e2e-aci_test.go

@@ -38,16 +38,6 @@ type E2eACISuite struct {
 	Suite
 }
 
-func (s *E2eACISuite) TestContextHelp() {
-	It("ensures context command includes azure-login and aci-create", func() {
-		output := s.NewDockerCommand("context", "create", "--help").ExecOrDie()
-		Expect(output).To(ContainSubstring("docker context create CONTEXT BACKEND [OPTIONS] [flags]"))
-		Expect(output).To(ContainSubstring("--aci-location"))
-		Expect(output).To(ContainSubstring("--aci-subscription-id"))
-		Expect(output).To(ContainSubstring("--aci-resource-group"))
-	})
-}
-
 func (s *E2eACISuite) TestContextDefault() {
 	It("should be initialized with default context", func() {
 		_, err := s.NewCommand("docker", "context", "rm", "-f", contextName).Exec()
@@ -70,7 +60,7 @@ func (s *E2eACISuite) TestACIBackend() {
 		Expect(err).To(BeNil())
 		subscriptionID = *models[0].SubscriptionID
 
-		s.NewDockerCommand("context", "create", contextName, "aci", "--aci-subscription-id", subscriptionID, "--aci-resource-group", resourceGroupName, "--aci-location", location).ExecOrDie()
+		s.NewDockerCommand("context", "create", "aci", contextName, "--subscription-id", subscriptionID, "--resource-group", resourceGroupName, "--location", location).ExecOrDie()
 		// Expect(output).To(ContainSubstring("ACI context acitest created"))
 	})
 

+ 23 - 23
tests/e2e/e2e_test.go

@@ -46,36 +46,36 @@ type E2eSuite struct {
 }
 
 func (s *E2eSuite) TestContextHelp() {
-	It("ensures context command includes azure-login and aci-create", func() {
-		output := s.NewDockerCommand("context", "create", "--help").ExecOrDie()
-		Expect(output).To(ContainSubstring("docker context create CONTEXT BACKEND [OPTIONS] [flags]"))
-		Expect(output).To(ContainSubstring("--aci-location"))
-		Expect(output).To(ContainSubstring("--aci-subscription-id"))
-		Expect(output).To(ContainSubstring("--aci-resource-group"))
-	})
+	output := s.NewDockerCommand("context", "create", "aci", "--help").ExecOrDie()
+	Expect(output).To(ContainSubstring("docker context create aci CONTEXT [flags]"))
+	Expect(output).To(ContainSubstring("--location"))
+	Expect(output).To(ContainSubstring("--subscription-id"))
+	Expect(output).To(ContainSubstring("--resource-group"))
 }
 
-func (s *E2eSuite) TestContextDefault() {
-	It("should be initialized with default context", func() {
-		output := s.NewDockerCommand("context", "show").ExecOrDie()
-		Expect(output).To(ContainSubstring("default"))
-		output = s.NewCommand("docker", "context", "ls").ExecOrDie()
-		golden.Assert(s.T(), output, GoldenFile("ls-out-default"))
-	})
+func (s *E2eSuite) TestListAndShowDefaultContext() {
+	output := s.NewDockerCommand("context", "show").ExecOrDie()
+	Expect(output).To(ContainSubstring("default"))
+	output = s.NewCommand("docker", "context", "ls").ExecOrDie()
+	golden.Assert(s.T(), output, GoldenFile("ls-out-default"))
 }
 
-func (s *E2eSuite) TestContextLegacy() {
-	It("should inspect default", func() {
-		output := s.NewDockerCommand("context", "inspect", "default").ExecOrDie()
-		Expect(output).To(ContainSubstring(`"Name": "default"`))
-	})
+func (s *E2eSuite) TestCreateDockerContextAndListIt() {
+	s.NewDockerCommand("context", "create", "test-docker", "--from", "default").ExecOrDie()
+	output := s.NewCommand("docker", "context", "ls").ExecOrDie()
+	golden.Assert(s.T(), output, GoldenFile("ls-out-test-docker"))
+}
+
+func (s *E2eSuite) TestInspectDefaultContext() {
+	output := s.NewDockerCommand("context", "inspect", "default").ExecOrDie()
+	Expect(output).To(ContainSubstring(`"Name": "default"`))
 }
 
 func (s *E2eSuite) TestContextCreateParseErrorDoesNotDelegateToLegacy() {
 	It("should dispay new cli error when parsing context create flags", func() {
-		_, err := s.NewDockerCommand("context", "create", "--aci-subscription-id", "titi").Exec()
+		_, err := s.NewDockerCommand("context", "create", "aci", "--subscription-id", "titi").Exec()
 		Expect(err.Error()).NotTo(ContainSubstring("unknown flag"))
-		Expect(err.Error()).To(ContainSubstring("accepts 2 arg(s), received 0"))
+		Expect(err.Error()).To(ContainSubstring("accepts 1 arg(s), received 0"))
 	})
 }
 
@@ -135,7 +135,7 @@ func (s *E2eSuite) TestLeaveLegacyErrorMessagesUnchanged() {
 }
 
 func (s *E2eSuite) TestDisplayFriendlyErrorMessageForLegacyCommands() {
-	s.NewDockerCommand("context", "create", "test-example", "example").ExecOrDie()
+	s.NewDockerCommand("context", "create", "example", "test-example").ExecOrDie()
 	output, err := s.NewDockerCommand("--context", "test-example", "images").Exec()
 	Expect(output).To(Equal("Command \"images\" not available in current context (test-example), you can use the \"default\" context to run this command\n"))
 	Expect(err).NotTo(BeNil())
@@ -149,7 +149,7 @@ func (s *E2eSuite) TestDisplaysAdditionalLineInDockerVersion() {
 
 func (s *E2eSuite) TestMockBackend() {
 	It("creates a new test context to hardcoded example backend", func() {
-		s.NewDockerCommand("context", "create", "test-example", "example").ExecOrDie()
+		s.NewDockerCommand("context", "create", "example", "test-example").ExecOrDie()
 		// Expect(output).To(ContainSubstring("test-example context acitest created"))
 	})
 

+ 1 - 1
tests/e2e/testdata/ls-out-default-windows.golden

@@ -1,2 +1,2 @@
 NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT                  KUBERNETES ENDPOINT   ORCHESTRATOR
-default *           docker              Current DOCKER_HOST based configuration   npipe:////./pipe/docker_engine                         swarm
+default *           moby                Current DOCKER_HOST based configuration   npipe:////./pipe/docker_engine                         swarm

+ 1 - 1
tests/e2e/testdata/ls-out-default.golden

@@ -1,2 +1,2 @@
 NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
-default *           docker              Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
+default *           moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm

+ 3 - 0
tests/e2e/testdata/ls-out-test-docker-windows.golden

@@ -0,0 +1,3 @@
+NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT                  KUBERNETES ENDPOINT   ORCHESTRATOR
+default *           moby                Current DOCKER_HOST based configuration   npipe:////./pipe/docker_engine                         swarm
+test-docker         moby                                                          npipe:////./pipe/docker_engine                         swarm

+ 3 - 0
tests/e2e/testdata/ls-out-test-docker.golden

@@ -0,0 +1,3 @@
+NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
+default *           moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
+test-docker         moby                                                          unix:///var/run/docker.sock                         swarm

+ 1 - 1
tests/e2e/testdata/ls-out-test-example-windows.golden

@@ -1,3 +1,3 @@
 NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT                  KUBERNETES ENDPOINT   ORCHESTRATOR
-default             docker              Current DOCKER_HOST based configuration   npipe:////./pipe/docker_engine                         swarm
+default             moby                Current DOCKER_HOST based configuration   npipe:////./pipe/docker_engine                         swarm
 test-example *      example                                                                                                              

+ 1 - 1
tests/e2e/testdata/ls-out-test-example.golden

@@ -1,3 +1,3 @@
 NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
-default             docker              Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
+default             moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
 test-example *      example