Repository: docker/compose-cli
Pull request #1018 (2020-12-07 11:33:51)

`compose up` and other compose commands running on “Moby” context type.
Signed-off-by: Guillaume Tardif <guillaume.tardif@gmail.com>

**What I did**
* in compose command, do not warn command is not available on default context type, rather use "local" backend when on "moby" context type.
* change e2e tests to not require a local context.
* local backend is not behind a feature flag anymore

**Related issue**
https://github.com/docker/dev-tooling-team/issues/244

<!-- optional tests
You can add a / mention to run tests executed by default only on main branch : 
* `test-aci` to run ACI E2E tests
* `test-ecs` to run ECS E2E tests
* `test-windows` to run tests & E2E tests on windows
-->

**(not mandatory) A picture of a cute animal, if possible in relation with what you did**
![](https://cdna.artstation.com/p/assets/images/images/013/339/096/4k/vijay-pratap-singh-bpr-render-4.jpg?1539155130)

gtardif: I wish I could do something more centralised in the compose command, in pre-run. But ctx being immutable, I can't change the context to set the client or something similar here, I can't either change the cmd to replace the context. 
gtardif: We can look at working on contexts with remote engine in a separate PR
gtardif: Also removed build directive: local backend is not behind a feature flag anymore

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8e222673..ef59f915 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -60,12 +60,12 @@ jobs:
 
       - name: Test
         env:
-          BUILD_TAGS: example,local
+          BUILD_TAGS: example
         run: make -f builder.Makefile test
 
       - name: Build for local E2E
         env:
-          BUILD_TAGS: example,local,e2e
+          BUILD_TAGS: example,e2e
         run: make -f builder.Makefile cli
 
       - name: E2E Test
diff --git a/Makefile b/Makefile
index 4a85885e..c6cff2f3 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@ protos: ## Generate go code from .proto files
 cli: ## Compile the cli
 	@docker build . --target cli \
 	--platform local \
-	--build-arg BUILD_TAGS=example,local,e2e \
+	--build-arg BUILD_TAGS=example,e2e \
 	--build-arg GIT_TAG=$(GIT_TAG) \
 	--output ./bin
 
@@ -63,7 +63,7 @@ cross: ## Compile the CLI for linux, darwin and windows
 
 test: ## Run unit tests
 	@docker build . \
-	--build-arg BUILD_TAGS=example,local \
+	--build-arg BUILD_TAGS=example \
 	--build-arg GIT_TAG=$(GIT_TAG) \
 	--target test
 
@@ -72,7 +72,7 @@ cache-clear: ## Clear the builder cache
 
 lint: ## run linter(s)
 	@docker build . \
-	--build-arg BUILD_TAGS=example,local,e2e \
+	--build-arg BUILD_TAGS=example,e2e \
 	--build-arg GIT_TAG=$(GIT_TAG) \
 	--target lint
 
diff --git a/api/client/client.go b/api/client/client.go
index ffcdad19..10c0bbda 100644
--- a/api/client/client.go
+++ b/api/client/client.go
@@ -32,6 +32,15 @@ import (
 
 // New returns a backend client associated with current context
 func New(ctx context.Context) (*Client, error) {
+	return newWithDefaultBackend(ctx, "")
+}
+
+// NewWithDefaultLocalBackend returns a backend client associated with current context or local backend if on default context type
+func NewWithDefaultLocalBackend(ctx context.Context) (*Client, error) {
+	return newWithDefaultBackend(ctx, store.LocalContextType)
+}
+
+func newWithDefaultBackend(ctx context.Context, defaultBackend string) (*Client, error) {
 	currentContext := apicontext.CurrentContext(ctx)
 	s := store.ContextStore(ctx)
 
@@ -40,7 +49,12 @@ func New(ctx context.Context) (*Client, error) {
 		return nil, err
 	}
 
-	service, err := backend.Get(ctx, cc.Type())
+	backendName := cc.Type()
+	if backendName == store.DefaultContextType && defaultBackend != "" {
+		backendName = defaultBackend
+	}
+
+	service, err := backend.Get(ctx, backendName)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cli/cmd/compose/build.go b/cli/cmd/compose/build.go
index 2c6817a1..1433650b 100644
--- a/cli/cmd/compose/build.go
+++ b/cli/cmd/compose/build.go
@@ -45,7 +45,7 @@ func buildCommand() *cobra.Command {
 }
 
 func runBuild(ctx context.Context, opts buildOptions, services []string) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/compose.go b/cli/cmd/compose/compose.go
index f8012cc4..4105767a 100644
--- a/cli/cmd/compose/compose.go
+++ b/cli/cmd/compose/compose.go
@@ -17,16 +17,12 @@
 package compose
 
 import (
-	"context"
-
 	"github.com/compose-spec/compose-go/cli"
 	"github.com/compose-spec/compose-go/types"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 
-	"github.com/docker/compose-cli/api/client"
 	"github.com/docker/compose-cli/context/store"
-	"github.com/docker/compose-cli/errdefs"
 )
 
 type composeOptions struct {
@@ -76,9 +72,6 @@ func Command(contextType string) *cobra.Command {
 	command := &cobra.Command{
 		Short: "Docker Compose",
 		Use:   "compose",
-		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
-			return checkComposeSupport(cmd.Context())
-		},
 	}
 
 	command.AddCommand(
@@ -90,7 +83,7 @@ func Command(contextType string) *cobra.Command {
 		convertCommand(),
 	)
 
-	if contextType == store.LocalContextType {
+	if contextType == store.LocalContextType || contextType == store.DefaultContextType {
 		command.AddCommand(
 			buildCommand(),
 			pushCommand(),
@@ -101,15 +94,6 @@ func Command(contextType string) *cobra.Command {
 	return command
 }
 
-func checkComposeSupport(ctx context.Context) error {
-	_, err := client.New(ctx)
-	if errdefs.IsNotFoundError(err) {
-		return errdefs.ErrNotImplemented
-	}
-
-	return err
-}
-
 //
 func filter(project *types.Project, services []string) error {
 	if len(services) == 0 {
diff --git a/cli/cmd/compose/convert.go b/cli/cmd/compose/convert.go
index aa9d62ac..29207ad5 100644
--- a/cli/cmd/compose/convert.go
+++ b/cli/cmd/compose/convert.go
@@ -46,7 +46,7 @@ func convertCommand() *cobra.Command {
 
 func runConvert(ctx context.Context, opts composeOptions) error {
 	var json []byte
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/down.go b/cli/cmd/compose/down.go
index 3519d394..bca2fc10 100644
--- a/cli/cmd/compose/down.go
+++ b/cli/cmd/compose/down.go
@@ -41,7 +41,7 @@ func downCommand() *cobra.Command {
 }
 
 func runDown(ctx context.Context, opts composeOptions) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/list.go b/cli/cmd/compose/list.go
index 27847af1..01730b41 100644
--- a/cli/cmd/compose/list.go
+++ b/cli/cmd/compose/list.go
@@ -43,7 +43,7 @@ func listCommand() *cobra.Command {
 }
 
 func runList(ctx context.Context, opts composeOptions) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/logs.go b/cli/cmd/compose/logs.go
index 4f08f3aa..aa58f68b 100644
--- a/cli/cmd/compose/logs.go
+++ b/cli/cmd/compose/logs.go
@@ -41,7 +41,7 @@ func logsCommand() *cobra.Command {
 }
 
 func runLogs(ctx context.Context, opts composeOptions) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/ps.go b/cli/cmd/compose/ps.go
index a7075353..235443d0 100644
--- a/cli/cmd/compose/ps.go
+++ b/cli/cmd/compose/ps.go
@@ -45,7 +45,7 @@ func psCommand() *cobra.Command {
 }
 
 func runPs(ctx context.Context, opts composeOptions) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/pull.go b/cli/cmd/compose/pull.go
index f757ebb4..27b28526 100644
--- a/cli/cmd/compose/pull.go
+++ b/cli/cmd/compose/pull.go
@@ -46,7 +46,7 @@ func pullCommand() *cobra.Command {
 }
 
 func runPull(ctx context.Context, opts pullOptions, services []string) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/push.go b/cli/cmd/compose/push.go
index cb5b018f..9f19d341 100644
--- a/cli/cmd/compose/push.go
+++ b/cli/cmd/compose/push.go
@@ -46,7 +46,7 @@ func pushCommand() *cobra.Command {
 }
 
 func runPush(ctx context.Context, opts pushOptions, services []string) error {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go
index 14fa2cba..6a06c8cf 100644
--- a/cli/cmd/compose/up.go
+++ b/cli/cmd/compose/up.go
@@ -38,7 +38,7 @@ func upCommand(contextType string) *cobra.Command {
 		Use: "up [SERVICE...]",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			switch contextType {
-			case store.LocalContextType:
+			case store.LocalContextType, store.DefaultContextType:
 				return runCreateStart(cmd.Context(), opts, args)
 			default:
 				return runUp(cmd.Context(), opts, args)
@@ -100,7 +100,7 @@ func runCreateStart(ctx context.Context, opts composeOptions, services []string)
 }
 
 func setup(ctx context.Context, opts composeOptions, services []string) (*client.Client, *types.Project, error) {
-	c, err := client.New(ctx)
+	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/local/backend.go b/local/backend.go
index a97871f3..de0380e1 100644
--- a/local/backend.go
+++ b/local/backend.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/build.go b/local/build.go
index 56ace5cf..5efdc76d 100644
--- a/local/build.go
+++ b/local/build.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/compose.go b/local/compose.go
index 1d8b4430..ccb7d09e 100644
--- a/local/compose.go
+++ b/local/compose.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
@@ -502,7 +500,7 @@ func (s *composeService) Down(ctx context.Context, projectName string) error {
 func (s *composeService) removeContainers(ctx context.Context, w progress.Writer, eg *errgroup.Group, filter filters.Args) error {
 	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
 		Filters: filter,
-		All: true,
+		All:     true,
 	})
 	if err != nil {
 		return err
@@ -806,7 +804,7 @@ func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number i
 		StopTimeout: toSeconds(s.StopGracePeriod),
 	}
 
-	mountOptions, err := buildContainerMountOptions(p, s, inherit)
+	mountOptions, err := buildContainerMountOptions(s, inherit)
 	if err != nil {
 		return nil, nil, nil, err
 	}
@@ -853,7 +851,7 @@ func buildContainerBindingOptions(s types.ServiceConfig) nat.PortMap {
 	return bindings
 }
 
-func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit *moby.Container) ([]mount.Mount, error) {
+func buildContainerMountOptions(s types.ServiceConfig, inherit *moby.Container) ([]mount.Mount, error) {
 	mounts := []mount.Mount{}
 	var inherited []string
 	if inherit != nil {
diff --git a/local/container.go b/local/container.go
index 4ebdddee..a82fd8c0 100644
--- a/local/container.go
+++ b/local/container.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/containers.go b/local/containers.go
index 24912e4e..b05e5699 100644
--- a/local/containers.go
+++ b/local/containers.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/convergence.go b/local/convergence.go
index 21a51f79..e62a566f 100644
--- a/local/convergence.go
+++ b/local/convergence.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/convert.go b/local/convert.go
index f0b834a7..2b567f9d 100644
--- a/local/convert.go
+++ b/local/convert.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/dependencies.go b/local/dependencies.go
index 6d6cc515..ecfe06b4 100644
--- a/local/dependencies.go
+++ b/local/dependencies.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/labels.go b/local/labels.go
index e0ca6010..2fd388f5 100644
--- a/local/labels.go
+++ b/local/labels.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/util.go b/local/util.go
index c49af75d..6aef2070 100644
--- a/local/util.go
+++ b/local/util.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/volumes.go b/local/volumes.go
index 2f56d444..2fbf08b7 100644
--- a/local/volumes.go
+++ b/local/volumes.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 


Test patch:
diff --git a/local/compose_test.go b/local/compose_test.go
index fa406709..d035875a 100644
--- a/local/compose_test.go
+++ b/local/compose_test.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/convert_test.go b/local/convert_test.go
index 987b6c98..fa93138d 100644
--- a/local/convert_test.go
+++ b/local/convert_test.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/dependencies_test.go b/local/dependencies_test.go
index 41b31e7e..736bb50c 100644
--- a/local/dependencies_test.go
+++ b/local/dependencies_test.go
@@ -1,5 +1,3 @@
-// +build local
-
 /*
    Copyright 2020 Docker Compose CLI authors
 
diff --git a/local/e2e/compose_test.go b/local/e2e/compose_test.go
index 806c179f..f17c8e3d 100644
--- a/local/e2e/compose_test.go
+++ b/local/e2e/compose_test.go
@@ -30,8 +30,6 @@ import (
 
 func TestLocalComposeUp(t *testing.T) {
 	c := NewParallelE2eCLI(t, binDir)
-	c.RunDockerCmd("context", "create", "local", "test-context").Assert(t, icmd.Success)
-	c.RunDockerCmd("context", "use", "test-context").Assert(t, icmd.Success)
 
 	const projectName = "compose-e2e-demo"
 
@@ -54,12 +52,12 @@ func TestLocalComposeUp(t *testing.T) {
 		output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
 		assert.Assert(t, strings.Contains(output, `"word":`))
 
-		res = c.RunDockerCmd("--context", "default", "network", "ls")
+		res = c.RunDockerCmd("network", "ls")
 		res.Assert(t, icmd.Expected{Out: projectName + "_default"})
 	})
 
 	t.Run("check compose labels", func(t *testing.T) {
-		res := c.RunDockerCmd("--context", "default", "inspect", projectName+"_web_1")
+		res := c.RunDockerCmd("inspect", projectName+"_web_1")
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "compose-e2e-demo"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "False",`})
@@ -69,7 +67,7 @@ func TestLocalComposeUp(t *testing.T) {
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.service": "web"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version":`})
 
-		res = c.RunDockerCmd("--context", "default", "network", "inspect", projectName+"_default")
+		res = c.RunDockerCmd("network", "inspect", projectName+"_default")
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.network": "default"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": `})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version": `})
@@ -85,15 +83,13 @@ func TestLocalComposeUp(t *testing.T) {
 	})
 
 	t.Run("check networks after down", func(t *testing.T) {
-		res := c.RunDockerCmd("--context", "default", "network", "ls")
+		res := c.RunDockerCmd("network", "ls")
 		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
 	})
 }
 
 func TestLocalComposeVolume(t *testing.T) {
 	c := NewParallelE2eCLI(t, binDir)
-	c.RunDockerCmd("context", "create", "local", "test-context").Assert(t, icmd.Success)
-	c.RunDockerCmd("context", "use", "test-context").Assert(t, icmd.Success)
 
 	const projectName = "compose-e2e-volume"
 
diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go
index d0615490..eb49bc69 100644
--- a/tests/e2e/e2e_test.go
+++ b/tests/e2e/e2e_test.go
@@ -47,23 +47,6 @@ func TestMain(m *testing.M) {
 	os.Exit(exitCode)
 }
 
-func TestComposeNotImplemented(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
-	res := c.RunDockerCmd("context", "show")
-	res.Assert(t, icmd.Expected{Out: "default"})
-	res = c.RunDockerOrExitError("compose", "up")
-	res.Assert(t, icmd.Expected{
-		ExitCode: 1,
-		Err:      `Command "compose up" not available in current context (default)`,
-	})
-
-	res = c.RunDockerOrExitError("compose", "-f", "titi.yaml", "up")
-	res.Assert(t, icmd.Expected{
-		ExitCode: 1,
-		Err:      `Command "compose up" not available in current context (default)`,
-	})
-}
-
 func TestContextDefault(t *testing.T) {
 	c := NewParallelE2eCLI(t, binDir)