Repository: docker/compose-cli
Pull request #1413 (2021-03-10 18:22:38)

refactoring: create backend in main then pass as command target
**What I did**
Changed architecture: backend is created from `main`(where init can depend on actual CLI flags) then pass to commands as target to execute

/test-ecs
/test-aci
/test-windows
/test-kube


Patch:
diff --git a/aci/backend.go b/aci/backend.go
index c9359c21..c18b32c6 100644
--- a/aci/backend.go
+++ b/aci/backend.go
@@ -17,7 +17,6 @@
 package aci
 
 import (
-	"context"
 	"strings"
 
 	"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance"
@@ -68,9 +67,9 @@ func init() {
 	backend.Register(backendType, backendType, service, getCloudService)
 }
 
-func service(ctx context.Context) (backend.Service, error) {
-	contextStore := store.ContextStore(ctx)
-	currentContext := apicontext.CurrentContext(ctx)
+func service() (backend.Service, error) {
+	contextStore := store.Instance()
+	currentContext := apicontext.Current()
 	var aciContext store.AciContext
 
 	if err := contextStore.GetEndpoint(currentContext, &aciContext); err != nil {
diff --git a/api/backend/backend.go b/api/backend/backend.go
index b1aa3ead..94e7d0a0 100644
--- a/api/backend/backend.go
+++ b/api/backend/backend.go
@@ -17,7 +17,6 @@
 package backend
 
 import (
-	"context"
 	"errors"
 	"fmt"
 
@@ -38,7 +37,7 @@ var (
 	errTypeRegistered = errors.New("backend: already registered")
 )
 
-type initFunc func(context.Context) (Service, error)
+type initFunc func() (Service, error)
 type getCloudServiceFunc func() (cloud.Service, error)
 
 type registeredBackend struct {
@@ -52,6 +51,18 @@ var backends = struct {
 	r []*registeredBackend
 }{}
 
+var instance Service
+
+// Current return the active backend instance
+func Current() Service {
+	return instance
+}
+
+// WithBackend set the active backend instance
+func WithBackend(s Service) {
+	instance = s
+}
+
 // Service aggregates the service interfaces
 type Service interface {
 	ContainerService() containers.Service
@@ -85,10 +96,10 @@ func Register(name string, backendType string, init initFunc, getCoudService get
 
 // Get returns the backend registered for a particular type, it returns
 // an error if there is no registered backends for the given type.
-func Get(ctx context.Context, backendType string) (Service, error) {
+func Get(backendType string) (Service, error) {
 	for _, b := range backends.r {
 		if b.backendType == backendType {
-			return b.init(ctx)
+			return b.init()
 		}
 	}
 
@@ -97,7 +108,7 @@ func Get(ctx context.Context, backendType string) (Service, error) {
 
 // GetCloudService returns the backend registered for a particular type, it returns
 // an error if there is no registered backends for the given type.
-func GetCloudService(ctx context.Context, backendType string) (cloud.Service, error) {
+func GetCloudService(backendType string) (cloud.Service, error) {
 	for _, b := range backends.r {
 		if b.backendType == backendType {
 			return b.getCloudService()
diff --git a/api/client/client.go b/api/client/client.go
index bbcd759a..faf081c4 100644
--- a/api/client/client.go
+++ b/api/client/client.go
@@ -25,6 +25,7 @@ import (
 	"github.com/docker/compose-cli/api/containers"
 	apicontext "github.com/docker/compose-cli/api/context"
 	"github.com/docker/compose-cli/api/context/store"
+	"github.com/docker/compose-cli/api/errdefs"
 	"github.com/docker/compose-cli/api/resources"
 	"github.com/docker/compose-cli/api/secrets"
 	"github.com/docker/compose-cli/api/volumes"
@@ -32,31 +33,17 @@ 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)
+	currentContext := apicontext.Current()
+	s := store.Instance()
 
 	cc, err := s.Get(currentContext)
 	if err != nil {
 		return nil, err
 	}
 
-	backendName := cc.Type()
-	if backendName == store.DefaultContextType && defaultBackend != "" {
-		backendName = defaultBackend
-	}
-
-	service, err := backend.Get(ctx, backendName)
-	if err != nil {
-		return nil, err
+	service := backend.Current()
+	if service == nil {
+		return nil, errdefs.ErrNotFound
 	}
 
 	client := NewClient(cc.Type(), service)
@@ -73,7 +60,7 @@ func NewClient(backendType string, service backend.Service) Client {
 
 // GetCloudService returns a backend CloudService (typically login, create context)
 func GetCloudService(ctx context.Context, backendType string) (cloud.Service, error) {
-	return backend.GetCloudService(ctx, backendType)
+	return backend.GetCloudService(backendType)
 }
 
 // Client is a multi-backend client
diff --git a/api/config/config.go b/api/config/config.go
index b771ded8..12fe9214 100644
--- a/api/config/config.go
+++ b/api/config/config.go
@@ -17,7 +17,6 @@
 package config
 
 import (
-	"context"
 	"encoding/json"
 	"io/ioutil"
 	"os"
@@ -28,17 +27,16 @@ import (
 	"github.com/docker/compose-cli/api/context/store"
 )
 
-type dirKey struct{}
+var configDir string
 
 // WithDir sets the config directory path in the context
-func WithDir(ctx context.Context, path string) context.Context {
-	return context.WithValue(ctx, dirKey{}, path)
+func WithDir(path string) {
+	configDir = path
 }
 
 // Dir returns the config directory path
-func Dir(ctx context.Context) string {
-	cd, _ := ctx.Value(dirKey{}).(string)
-	return cd
+func Dir() string {
+	return configDir
 }
 
 // LoadFile loads the docker configuration
diff --git a/api/context/context.go b/api/context/context.go
index 27ac5e36..5d8577a4 100644
--- a/api/context/context.go
+++ b/api/context/context.go
@@ -16,35 +16,14 @@
 
 package context
 
-import (
-	gocontext "context"
-
-	"golang.org/x/net/context"
-
-	cliflags "github.com/docker/cli/cli/flags"
-)
-
-type currentContextKey struct{}
-type cliOptionsKey struct{}
+var current string
 
 // WithCurrentContext sets the name of the current docker context
-func WithCurrentContext(ctx gocontext.Context, contextName string) context.Context {
-	return context.WithValue(ctx, currentContextKey{}, contextName)
-}
-
-// CurrentContext returns the current context name
-func CurrentContext(ctx context.Context) string {
-	cc, _ := ctx.Value(currentContextKey{}).(string)
-	return cc
-}
-
-// WithCliOptions sets CLI options
-func WithCliOptions(ctx gocontext.Context, options cliflags.CommonOptions) context.Context {
-	return context.WithValue(ctx, cliOptionsKey{}, options)
+func WithCurrentContext(contextName string) {
+	current = contextName
 }
 
-// CliOptions returns common cli options
-func CliOptions(ctx context.Context) cliflags.CommonOptions {
-	cc, _ := ctx.Value(cliOptionsKey{}).(cliflags.CommonOptions)
-	return cc
+// Current returns the current context name
+func Current() string {
+	return current
 }
diff --git a/api/context/store/store.go b/api/context/store/store.go
index 5427f752..146ed21f 100644
--- a/api/context/store/store.go
+++ b/api/context/store/store.go
@@ -17,7 +17,6 @@
 package store
 
 import (
-	"context"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -67,17 +66,16 @@ const (
 	metaFile          = "meta.json"
 )
 
-type contextStoreKey struct{}
+var instance Store
 
 // WithContextStore adds the store to the context
-func WithContextStore(ctx context.Context, store Store) context.Context {
-	return context.WithValue(ctx, contextStoreKey{}, store)
+func WithContextStore(store Store) {
+	instance = store
 }
 
-// ContextStore returns the store from the context
-func ContextStore(ctx context.Context) Store {
-	s, _ := ctx.Value(contextStoreKey{}).(Store)
-	return s
+// Instance returns the store from the context
+func Instance() Store {
+	return instance
 }
 
 // Store is the context store
diff --git a/cli/cmd/compose/build.go b/cli/cmd/compose/build.go
index 3bb7c315..c5f58b73 100644
--- a/cli/cmd/compose/build.go
+++ b/cli/cmd/compose/build.go
@@ -63,7 +63,7 @@ func buildCommand(p *projectOptions) *cobra.Command {
 }
 
 func runBuild(ctx context.Context, opts buildOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/convert.go b/cli/cmd/compose/convert.go
index 46fb9a8c..d4165f93 100644
--- a/cli/cmd/compose/convert.go
+++ b/cli/cmd/compose/convert.go
@@ -77,7 +77,7 @@ func convertCommand(p *projectOptions) *cobra.Command {
 
 func runConvert(ctx context.Context, opts convertOptions, services []string) error {
 	var json []byte
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
@@ -88,7 +88,7 @@ func runConvert(ctx context.Context, opts convertOptions, services []string) err
 	}
 
 	if opts.resolve {
-		configFile, err := cliconfig.Load(config.Dir(ctx))
+		configFile, err := cliconfig.Load(config.Dir())
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/compose/down.go b/cli/cmd/compose/down.go
index 36ac97e5..edc66968 100644
--- a/cli/cmd/compose/down.go
+++ b/cli/cmd/compose/down.go
@@ -69,7 +69,7 @@ func downCommand(p *projectOptions, contextType string) *cobra.Command {
 }
 
 func runDown(ctx context.Context, opts downOptions) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/events.go b/cli/cmd/compose/events.go
index e37b150a..0628f970 100644
--- a/cli/cmd/compose/events.go
+++ b/cli/cmd/compose/events.go
@@ -51,7 +51,7 @@ func eventsCommand(p *projectOptions) *cobra.Command {
 }
 
 func runEvents(ctx context.Context, opts eventsOpts, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/exec.go b/cli/cmd/compose/exec.go
index b9b92ae0..1cd0170d 100644
--- a/cli/cmd/compose/exec.go
+++ b/cli/cmd/compose/exec.go
@@ -75,7 +75,7 @@ func execCommand(p *projectOptions) *cobra.Command {
 }
 
 func runExec(ctx context.Context, opts execOpts) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/kill.go b/cli/cmd/compose/kill.go
index 7dbf08ff..1ffb99dd 100644
--- a/cli/cmd/compose/kill.go
+++ b/cli/cmd/compose/kill.go
@@ -49,7 +49,7 @@ func killCommand(p *projectOptions) *cobra.Command {
 }
 
 func runKill(ctx context.Context, opts killOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/list.go b/cli/cmd/compose/list.go
index 54f8d1d4..8ae601d8 100644
--- a/cli/cmd/compose/list.go
+++ b/cli/cmd/compose/list.go
@@ -69,7 +69,7 @@ func runList(ctx context.Context, opts lsOptions) error {
 		return err
 	}
 
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/logs.go b/cli/cmd/compose/logs.go
index a87b4442..75ec4803 100644
--- a/cli/cmd/compose/logs.go
+++ b/cli/cmd/compose/logs.go
@@ -62,7 +62,7 @@ func logsCommand(p *projectOptions, contextType string) *cobra.Command {
 }
 
 func runLogs(ctx context.Context, opts logsOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/pause.go b/cli/cmd/compose/pause.go
index 8cd08c26..87f8fc71 100644
--- a/cli/cmd/compose/pause.go
+++ b/cli/cmd/compose/pause.go
@@ -44,7 +44,7 @@ func pauseCommand(p *projectOptions) *cobra.Command {
 }
 
 func runPause(ctx context.Context, opts pauseOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
@@ -79,7 +79,7 @@ func unpauseCommand(p *projectOptions) *cobra.Command {
 }
 
 func runUnPause(ctx context.Context, opts unpauseOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/ps.go b/cli/cmd/compose/ps.go
index 54161759..be0f0a10 100644
--- a/cli/cmd/compose/ps.go
+++ b/cli/cmd/compose/ps.go
@@ -58,7 +58,7 @@ func psCommand(p *projectOptions) *cobra.Command {
 }
 
 func runPs(ctx context.Context, opts psOptions) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/pull.go b/cli/cmd/compose/pull.go
index 0bbd6e41..44ccbae3 100644
--- a/cli/cmd/compose/pull.go
+++ b/cli/cmd/compose/pull.go
@@ -49,7 +49,7 @@ func pullCommand(p *projectOptions) *cobra.Command {
 }
 
 func runPull(ctx context.Context, opts pullOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/push.go b/cli/cmd/compose/push.go
index 21c6f1ee..9c1b6a0d 100644
--- a/cli/cmd/compose/push.go
+++ b/cli/cmd/compose/push.go
@@ -50,7 +50,7 @@ func pushCommand(p *projectOptions) *cobra.Command {
 }
 
 func runPush(ctx context.Context, opts pushOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/remove.go b/cli/cmd/compose/remove.go
index 02a0e0b8..753f8300 100644
--- a/cli/cmd/compose/remove.go
+++ b/cli/cmd/compose/remove.go
@@ -61,7 +61,7 @@ Any data which is not in a volume will be lost.`,
 }
 
 func runRemove(ctx context.Context, opts removeOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/start.go b/cli/cmd/compose/start.go
index ed17ced8..ac50b719 100644
--- a/cli/cmd/compose/start.go
+++ b/cli/cmd/compose/start.go
@@ -45,7 +45,7 @@ func startCommand(p *projectOptions) *cobra.Command {
 }
 
 func runStart(ctx context.Context, opts startOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/stop.go b/cli/cmd/compose/stop.go
index a0c87007..f80dc1c9 100644
--- a/cli/cmd/compose/stop.go
+++ b/cli/cmd/compose/stop.go
@@ -52,7 +52,7 @@ func stopCommand(p *projectOptions) *cobra.Command {
 }
 
 func runStop(ctx context.Context, opts stopOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/top.go b/cli/cmd/compose/top.go
index c7d839cc..cc5719ba 100644
--- a/cli/cmd/compose/top.go
+++ b/cli/cmd/compose/top.go
@@ -49,7 +49,7 @@ func topCommand(p *projectOptions) *cobra.Command {
 }
 
 func runTop(ctx context.Context, opts topOptions, services []string) error {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go
index f9ac8e16..55955775 100644
--- a/cli/cmd/compose/up.go
+++ b/cli/cmd/compose/up.go
@@ -338,7 +338,7 @@ func setServiceScale(project *types.Project, name string, replicas int) error {
 }
 
 func setup(ctx context.Context, opts composeOptions, services []string) (*client.Client, *types.Project, error) {
-	c, err := client.NewWithDefaultLocalBackend(ctx)
+	c, err := client.New(ctx)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cli/cmd/context/create.go b/cli/cmd/context/create.go
index aa24f15d..b2dcd3ca 100644
--- a/cli/cmd/context/create.go
+++ b/cli/cmd/context/create.go
@@ -17,7 +17,6 @@
 package context
 
 import (
-	"context"
 	"fmt"
 	"strings"
 
@@ -103,15 +102,15 @@ func createLocalCommand() *cobra.Command {
 		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{})
+			return createDockerContext(args[0], store.LocalContextType, opts.description, store.LocalContext{})
 		},
 	}
 	addDescriptionFlag(cmd, &opts.description)
 	return cmd
 }
 
-func createDockerContext(ctx context.Context, name string, contextType string, description string, data interface{}) error {
-	s := store.ContextStore(ctx)
+func createDockerContext(name string, contextType string, description string, data interface{}) error {
+	s := store.Instance()
 	result := s.Create(
 		name,
 		contextType,
@@ -122,8 +121,8 @@ func createDockerContext(ctx context.Context, name string, contextType string, d
 	return result
 }
 
-func contextExists(ctx context.Context, name string) bool {
-	s := store.ContextStore(ctx)
+func contextExists(name string) bool {
+	s := store.Instance()
 	return s.ContextExists(name)
 }
 
diff --git a/cli/cmd/context/create_aci.go b/cli/cmd/context/create_aci.go
index 0c6f7c41..3efaee52 100644
--- a/cli/cmd/context/create_aci.go
+++ b/cli/cmd/context/create_aci.go
@@ -57,7 +57,7 @@ func createAciCommand() *cobra.Command {
 }
 
 func runCreateAci(ctx context.Context, contextName string, opts aci.ContextParams) error {
-	if contextExists(ctx, contextName) {
+	if contextExists(contextName) {
 		return errors.Wrapf(errdefs.ErrAlreadyExists, "context %s", contextName)
 	}
 	contextData, description, err := getAciContextData(ctx, opts)
@@ -67,7 +67,7 @@ func runCreateAci(ctx context.Context, contextName string, opts aci.ContextParam
 		}
 		return err
 	}
-	return createDockerContext(ctx, contextName, store.AciContextType, description, contextData)
+	return createDockerContext(contextName, store.AciContextType, description, contextData)
 
 }
 
diff --git a/cli/cmd/context/create_ecs.go b/cli/cmd/context/create_ecs.go
index 34ba11c4..6570020e 100644
--- a/cli/cmd/context/create_ecs.go
+++ b/cli/cmd/context/create_ecs.go
@@ -109,7 +109,7 @@ func parseAccessKeysFile(file string, opts *ecs.ContextParams) error {
 }
 
 func runCreateLocalSimulation(ctx context.Context, contextName string, opts ecs.ContextParams) error {
-	if contextExists(ctx, contextName) {
+	if contextExists(contextName) {
 		return errors.Wrapf(errdefs.ErrAlreadyExists, "context %q", contextName)
 	}
 	cs, err := client.GetCloudService(ctx, store.EcsLocalSimulationContextType)
@@ -120,18 +120,18 @@ func runCreateLocalSimulation(ctx context.Context, contextName string, opts ecs.
 	if err != nil {
 		return err
 	}
-	return createDockerContext(ctx, contextName, store.EcsLocalSimulationContextType, description, data)
+	return createDockerContext(contextName, store.EcsLocalSimulationContextType, description, data)
 }
 
 func runCreateEcs(ctx context.Context, contextName string, opts ecs.ContextParams) error {
-	if contextExists(ctx, contextName) {
+	if contextExists(contextName) {
 		return errors.Wrapf(errdefs.ErrAlreadyExists, "context %q", contextName)
 	}
 	contextData, description, err := getEcsContextData(ctx, opts)
 	if err != nil {
 		return err
 	}
-	return createDockerContext(ctx, contextName, store.EcsContextType, description, contextData)
+	return createDockerContext(contextName, store.EcsContextType, description, contextData)
 
 }
 
diff --git a/cli/cmd/context/create_kube.go b/cli/cmd/context/create_kube.go
index 609716e3..1eb269cd 100644
--- a/cli/cmd/context/create_kube.go
+++ b/cli/cmd/context/create_kube.go
@@ -19,8 +19,6 @@
 package context
 
 import (
-	"context"
-
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
@@ -45,7 +43,7 @@ func createKubeCommand() *cobra.Command {
 		Short: "Create context for a Kubernetes Cluster",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runCreateKube(cmd.Context(), args[0], opts)
+			return runCreateKube(args[0], opts)
 		},
 	}
 
@@ -56,8 +54,8 @@ func createKubeCommand() *cobra.Command {
 	return cmd
 }
 
-func runCreateKube(ctx context.Context, contextName string, opts kube.ContextParams) error {
-	if contextExists(ctx, contextName) {
+func runCreateKube(contextName string, opts kube.ContextParams) error {
+	if contextExists(contextName) {
 		return errors.Wrapf(errdefs.ErrAlreadyExists, "context %q", contextName)
 	}
 
@@ -65,5 +63,5 @@ func runCreateKube(ctx context.Context, contextName string, opts kube.ContextPar
 	if err != nil {
 		return err
 	}
-	return createDockerContext(ctx, contextName, store.KubeContextType, description, contextData)
+	return createDockerContext(contextName, store.KubeContextType, description, contextData)
 }
diff --git a/cli/cmd/context/ls.go b/cli/cmd/context/ls.go
index ffcd1b8e..d301f3bc 100644
--- a/cli/cmd/context/ls.go
+++ b/cli/cmd/context/ls.go
@@ -73,9 +73,8 @@ func runList(cmd *cobra.Command, opts lsOpts) error {
 		return nil
 	}
 
-	ctx := cmd.Context()
-	currentContext := apicontext.CurrentContext(ctx)
-	s := store.ContextStore(ctx)
+	currentContext := apicontext.Current()
+	s := store.Instance()
 	contexts, err := s.List()
 	if err != nil {
 		return err
diff --git a/cli/cmd/context/rm.go b/cli/cmd/context/rm.go
index 473b13e6..b6bbde59 100644
--- a/cli/cmd/context/rm.go
+++ b/cli/cmd/context/rm.go
@@ -17,7 +17,6 @@
 package context
 
 import (
-	"context"
 	"errors"
 	"fmt"
 
@@ -41,7 +40,7 @@ func removeCommand() *cobra.Command {
 		Aliases: []string{"remove"},
 		Args:    cobra.MinimumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runRemove(cmd.Context(), args, opts.force)
+			return runRemove(args, opts.force)
 		},
 	}
 	cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Force removing current context")
@@ -49,15 +48,15 @@ func removeCommand() *cobra.Command {
 	return cmd
 }
 
-func runRemove(ctx context.Context, args []string, force bool) error {
-	currentContext := apicontext.CurrentContext(ctx)
-	s := store.ContextStore(ctx)
+func runRemove(args []string, force bool) error {
+	currentContext := apicontext.Current()
+	s := store.Instance()
 
 	var errs *multierror.Error
 	for _, contextName := range args {
 		if currentContext == contextName {
 			if force {
-				if err := runUse(ctx, "default"); err != nil {
+				if err := runUse("default"); err != nil {
 					errs = multierror.Append(errs, errors.New("cannot delete current context"))
 				} else {
 					errs = removeContext(s, contextName, errs)
diff --git a/cli/cmd/context/show.go b/cli/cmd/context/show.go
index 69dc91da..8ba588a5 100644
--- a/cli/cmd/context/show.go
+++ b/cli/cmd/context/show.go
@@ -17,7 +17,6 @@
 package context
 
 import (
-	"context"
 	"fmt"
 
 	"github.com/spf13/cobra"
@@ -32,16 +31,16 @@ func showCommand() *cobra.Command {
 		Short: "Print the current context",
 		Args:  cobra.NoArgs,
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runShow(cmd.Context())
+			return runShow()
 		},
 	}
 }
 
-func runShow(ctx context.Context) error {
-	name := apicontext.CurrentContext(ctx)
+func runShow() error {
+	name := apicontext.Current()
 	// Match behavior of existing CLI
 	if name != store.DefaultContextName {
-		s := store.ContextStore(ctx)
+		s := store.Instance()
 		if _, err := s.Get(name); err != nil {
 			return err
 		}
diff --git a/cli/cmd/context/update.go b/cli/cmd/context/update.go
index f21182b4..52e3bf7e 100644
--- a/cli/cmd/context/update.go
+++ b/cli/cmd/context/update.go
@@ -72,7 +72,7 @@ $ docker context update my-context --description "some description" --docker "ho
 }
 
 func runUpdate(cmd *cobra.Command, name string) error {
-	s := store.ContextStore(cmd.Context())
+	s := store.Instance()
 	dockerContext, err := s.Get(name)
 	if err == nil && dockerContext != nil {
 		if dockerContext.Type() != store.DefaultContextType {
diff --git a/cli/cmd/context/use.go b/cli/cmd/context/use.go
index 8c4684c5..5e8bbfd6 100644
--- a/cli/cmd/context/use.go
+++ b/cli/cmd/context/use.go
@@ -17,7 +17,6 @@
 package context
 
 import (
-	"context"
 	"fmt"
 
 	"github.com/spf13/cobra"
@@ -32,20 +31,20 @@ func useCommand() *cobra.Command {
 		Short: "Set the default context",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runUse(cmd.Context(), args[0])
+			return runUse(args[0])
 		},
 	}
 }
 
-func runUse(ctx context.Context, name string) error {
-	s := store.ContextStore(ctx)
+func runUse(name string) error {
+	s := store.Instance()
 	// Match behavior of existing CLI
 	if name != store.DefaultContextName {
 		if _, err := s.Get(name); err != nil {
 			return err
 		}
 	}
-	if err := config.WriteCurrentContext(config.Dir(ctx), name); err != nil {
+	if err := config.WriteCurrentContext(config.Dir(), name); err != nil {
 		return err
 	}
 	fmt.Println(name)
diff --git a/cli/main.go b/cli/main.go
index 2500cf22..e6bbe0b0 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -28,10 +28,14 @@ import (
 	"syscall"
 	"time"
 
+	"github.com/docker/cli/cli/command"
+	cliconfig "github.com/docker/cli/cli/config"
+	cliflags "github.com/docker/cli/cli/flags"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/docker/compose-cli/api/backend"
 	"github.com/docker/compose-cli/api/config"
 	apicontext "github.com/docker/compose-cli/api/context"
 	"github.com/docker/compose-cli/api/context/store"
@@ -46,8 +50,7 @@ import (
 	"github.com/docker/compose-cli/cli/metrics"
 	"github.com/docker/compose-cli/cli/mobycli"
 	cliopts "github.com/docker/compose-cli/cli/options"
-
-	cliflags "github.com/docker/cli/cli/flags"
+	"github.com/docker/compose-cli/local"
 
 	// Backend registrations
 	_ "github.com/docker/compose-cli/aci"
@@ -191,16 +194,17 @@ func main() {
 	if opts.Config == "" {
 		fatal(errors.New("config path cannot be empty"))
 	}
-
 	configDir := opts.Config
-	ctx = config.WithDir(ctx, configDir)
+	config.WithDir(configDir)
 
 	currentContext := determineCurrentContext(opts.Context, configDir, opts.Hosts)
+	apicontext.WithCurrentContext(currentContext)
 
 	s, err := store.New(configDir)
 	if err != nil {
 		mobycli.Exec(root)
 	}
+	store.WithContextStore(s)
 
 	ctype := store.DefaultContextType
 	cc, _ := s.Get(currentContext)
@@ -208,33 +212,54 @@ func main() {
 		ctype = cc.Type()
 	}
 
+	service, err := getBackend(ctype, configDir, opts)
+	if err != nil {
+		fatal(err)
+	}
+	backend.WithBackend(service)
+
 	root.AddCommand(
 		run.Command(ctype),
 		compose.Command(ctype),
 		volume.Command(ctype),
 	)
-	if ctype == store.DefaultContextType || ctype == store.LocalContextType {
-		cnxOptions := cliflags.CommonOptions{
-			Context:   opts.Context,
-			Debug:     opts.Debug,
-			Hosts:     opts.Hosts,
-			LogLevel:  opts.LogLevel,
-			TLS:       opts.TLS,
-			TLSVerify: opts.TLSVerify,
+
+	if err = root.ExecuteContext(ctx); err != nil {
+		handleError(ctx, err, ctype, currentContext, cc, root)
+	}
+	metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
+}
+
+func getBackend(ctype string, configDir string, opts cliopts.GlobalOpts) (backend.Service, error) {
+	switch ctype {
+	case store.DefaultContextType, store.LocalContextType:
+		configFile, err := cliconfig.Load(configDir)
+		if err != nil {
+			return nil, err
+		}
+		options := cliflags.CommonOptions{
+			Context:  opts.Context,
+			Debug:    opts.Debug,
+			Hosts:    opts.Hosts,
+			LogLevel: opts.LogLevel,
 		}
 
 		if opts.TLSVerify {
-			cnxOptions.TLSOptions = opts.TLSOptions
+			options.TLS = opts.TLS
+			options.TLSVerify = opts.TLSVerify
+			options.TLSOptions = opts.TLSOptions
 		}
-		ctx = apicontext.WithCliOptions(ctx, cnxOptions)
+		apiClient, err := command.NewAPIClientFromFlags(&options, configFile)
+		if err != nil {
+			return nil, err
+		}
+		return local.NewService(apiClient), nil
 	}
-	ctx = apicontext.WithCurrentContext(ctx, currentContext)
-	ctx = store.WithContextStore(ctx, s)
-
-	if err = root.ExecuteContext(ctx); err != nil {
-		handleError(ctx, err, ctype, currentContext, cc, root)
+	service, err := backend.Get(ctype)
+	if errdefs.IsNotFoundError(err) {
+		return service, nil
 	}
-	metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
+	return service, err
 }
 
 func handleError(ctx context.Context, err error, ctype string, currentContext string, cc *store.DockerContext, root *cobra.Command) {
diff --git a/cli/mobycli/exec.go b/cli/mobycli/exec.go
index e264e2cd..758ba53b 100644
--- a/cli/mobycli/exec.go
+++ b/cli/mobycli/exec.go
@@ -39,9 +39,9 @@ const ComDockerCli = "com.docker.cli"
 
 // ExecIfDefaultCtxType delegates to com.docker.cli if on moby context
 func ExecIfDefaultCtxType(ctx context.Context, root *cobra.Command) {
-	currentContext := apicontext.CurrentContext(ctx)
+	currentContext := apicontext.Current()
 
-	s := store.ContextStore(ctx)
+	s := store.Instance()
 
 	currentCtx, err := s.Get(currentContext)
 	// Only run original docker command if the current context is not ours.
diff --git a/cli/server/interceptor.go b/cli/server/interceptor.go
index 7528a513..617721a7 100644
--- a/cli/server/interceptor.go
+++ b/cli/server/interceptor.go
@@ -40,7 +40,7 @@ func unaryServerInterceptor(clictx context.Context) grpc.UnaryServerInterceptor
 	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
 		currentContext, err := getIncomingContext(ctx)
 		if err != nil {
-			currentContext, err = getConfigContext(clictx)
+			currentContext, err = getConfigContext()
 			if err != nil {
 				return nil, err
 			}
@@ -59,7 +59,7 @@ func streamServerInterceptor(clictx context.Context) grpc.StreamServerIntercepto
 	return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
 		currentContext, err := getIncomingContext(ss.Context())
 		if err != nil {
-			currentContext, err = getConfigContext(clictx)
+			currentContext, err = getConfigContext()
 			if err != nil {
 				return err
 			}
@@ -77,8 +77,8 @@ func streamServerInterceptor(clictx context.Context) grpc.StreamServerIntercepto
 }
 
 // Returns the current context from the configuration file
-func getConfigContext(ctx context.Context) (string, error) {
-	configDir := config.Dir(ctx)
+func getConfigContext() (string, error) {
+	configDir := config.Dir()
 	configFile, err := config.LoadFile(configDir)
 	if err != nil {
 		return "", err
@@ -100,9 +100,9 @@ func getIncomingContext(ctx context.Context) (string, error) {
 // configureContext populates the request context with objects the client
 // needs: the context store and the api client
 func configureContext(ctx context.Context, currentContext string, method string) (context.Context, error) {
-	configDir := config.Dir(ctx)
+	configDir := config.Dir()
 
-	ctx = apicontext.WithCurrentContext(ctx, currentContext)
+	apicontext.WithCurrentContext(currentContext)
 
 	// The contexts service doesn't need the client
 	if !strings.Contains(method, "/com.docker.api.protos.context.v1.Contexts") {
@@ -118,7 +118,7 @@ func configureContext(ctx context.Context, currentContext string, method string)
 	if err != nil {
 		return nil, err
 	}
-	ctx = store.WithContextStore(ctx, s)
+	store.WithContextStore(s)
 
 	return ctx, nil
 }
diff --git a/cli/server/proxy/contexts.go b/cli/server/proxy/contexts.go
index dead6921..2d686825 100644
--- a/cli/server/proxy/contexts.go
+++ b/cli/server/proxy/contexts.go
@@ -37,7 +37,7 @@ func (cp *contextsProxy) SetCurrent(ctx context.Context, request *contextsv1.Set
 }
 
 func (cp *contextsProxy) List(ctx context.Context, request *contextsv1.ListRequest) (*contextsv1.ListResponse, error) {
-	s := store.ContextStore(ctx)
+	s := store.Instance()
 	configFile, err := config.LoadFile(cp.configDir)
 	if err != nil {
 		return nil, err
diff --git a/cli/server/proxy/proxy.go b/cli/server/proxy/proxy.go
index 621b14f4..c6fd7777 100644
--- a/cli/server/proxy/proxy.go
+++ b/cli/server/proxy/proxy.go
@@ -62,7 +62,7 @@ type proxy struct {
 
 // New creates a new proxy server
 func New(ctx context.Context) Proxy {
-	configDir := config.Dir(ctx)
+	configDir := config.Dir()
 	return &proxy{
 		configDir: configDir,
 		streams:   map[string]*streams.Stream{},
diff --git a/ecs/backend.go b/ecs/backend.go
index bb075b4c..a0663c74 100644
--- a/ecs/backend.go
+++ b/ecs/backend.go
@@ -63,9 +63,9 @@ func init() {
 	backend.Register(backendType, backendType, service, getCloudService)
 }
 
-func service(ctx context.Context) (backend.Service, error) {
-	contextStore := store.ContextStore(ctx)
-	currentContext := apicontext.CurrentContext(ctx)
+func service() (backend.Service, error) {
+	contextStore := store.Instance()
+	currentContext := apicontext.Current()
 	var ecsContext store.EcsContext
 
 	if err := contextStore.GetEndpoint(currentContext, &ecsContext); err != nil {
diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go
index e6e260ab..99d1af43 100644
--- a/ecs/cloudformation.go
+++ b/ecs/cloudformation.go
@@ -104,7 +104,7 @@ func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project, opt
 }
 
 func (b *ecsAPIService) resolveServiceImagesDigests(ctx context.Context, project *types.Project) error {
-	configFile, err := cliconfig.Load(config.Dir(ctx))
+	configFile, err := cliconfig.Load(config.Dir())
 	if err != nil {
 		return err
 	}
diff --git a/ecs/local/backend.go b/ecs/local/backend.go
index 7d80380d..8e26b768 100644
--- a/ecs/local/backend.go
+++ b/ecs/local/backend.go
@@ -17,8 +17,6 @@
 package local
 
 import (
-	"context"
-
 	local_compose "github.com/docker/compose-cli/local/compose"
 
 	"github.com/docker/docker/client"
@@ -44,7 +42,7 @@ type ecsLocalSimulation struct {
 	compose compose.Service
 }
 
-func service(ctx context.Context) (backend.Service, error) {
+func service() (backend.Service, error) {
 	apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
 	if err != nil {
 		return nil, err
diff --git a/go.mod b/go.mod
index 46af0062..ba63c4e4 100644
--- a/go.mod
+++ b/go.mod
@@ -54,7 +54,6 @@ require (
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.6.1
 	github.com/valyala/fasttemplate v1.2.1 // indirect
-	golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
 	golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58
 	golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
 	google.golang.org/grpc v1.33.2
diff --git a/kube/backend.go b/kube/backend.go
index b92a60f2..fa61a7da 100644
--- a/kube/backend.go
+++ b/kube/backend.go
@@ -19,8 +19,6 @@
 package kube
 
 import (
-	"context"
-
 	"github.com/docker/compose-cli/api/backend"
 	"github.com/docker/compose-cli/api/cloud"
 	"github.com/docker/compose-cli/api/compose"
@@ -41,8 +39,8 @@ func init() {
 	backend.Register(backendType, backendType, service, cloud.NotImplementedCloudService)
 }
 
-func service(ctx context.Context) (backend.Service, error) {
-	s, err := NewComposeService(ctx)
+func service() (backend.Service, error) {
+	s, err := NewComposeService()
 	if err != nil {
 		return nil, err
 	}
diff --git a/kube/compose.go b/kube/compose.go
index 17dbfa3f..adf4992f 100644
--- a/kube/compose.go
+++ b/kube/compose.go
@@ -42,9 +42,9 @@ type composeService struct {
 }
 
 // NewComposeService create a kubernetes implementation of the compose.Service API
-func NewComposeService(ctx context.Context) (compose.Service, error) {
-	contextStore := store.ContextStore(ctx)
-	currentContext := apicontext.CurrentContext(ctx)
+func NewComposeService() (compose.Service, error) {
+	contextStore := store.Instance()
+	currentContext := apicontext.Current()
 	var kubeContext store.KubeContext
 
 	if err := contextStore.GetEndpoint(currentContext, &kubeContext); err != nil {
diff --git a/local/backend.go b/local/backend.go
index fa79f1eb..e315f5a0 100644
--- a/local/backend.go
+++ b/local/backend.go
@@ -17,22 +17,15 @@
 package local
 
 import (
-	"context"
+	"github.com/docker/docker/client"
 
-	"github.com/docker/cli/cli/command"
 	"github.com/docker/compose-cli/api/backend"
-	"github.com/docker/compose-cli/api/cloud"
-
 	"github.com/docker/compose-cli/api/compose"
-	apiconfig "github.com/docker/compose-cli/api/config"
 	"github.com/docker/compose-cli/api/containers"
-	apicontext "github.com/docker/compose-cli/api/context"
 	"github.com/docker/compose-cli/api/resources"
 	"github.com/docker/compose-cli/api/secrets"
 	"github.com/docker/compose-cli/api/volumes"
 	local_compose "github.com/docker/compose-cli/local/compose"
-
-	cliconfig "github.com/docker/cli/cli/config"
 )
 
 type local struct {
@@ -41,27 +34,13 @@ type local struct {
 	composeService   compose.Service
 }
 
-func init() {
-	backend.Register("local", "local", service, cloud.NotImplementedCloudService)
-}
-
-func service(ctx context.Context) (backend.Service, error) {
-	options := apicontext.CliOptions(ctx)
-	config := apiconfig.Dir(ctx)
-	configFile, err := cliconfig.Load(config)
-	if err != nil {
-		return nil, err
-	}
-	apiClient, err := command.NewAPIClientFromFlags(&options, configFile)
-	if err != nil {
-		return nil, err
-	}
-
+// NewService build a backend for "local" context, using Docker API client
+func NewService(apiClient client.APIClient) backend.Service {
 	return &local{
 		containerService: &containerService{apiClient},
 		volumeService:    &volumeService{apiClient},
 		composeService:   local_compose.NewComposeService(apiClient),
-	}, nil
+	}
 }
 
 func (s *local) ContainerService() containers.Service {
diff --git a/local/compose/build.go b/local/compose/build.go
index 1429fe44..7c6a3461 100644
--- a/local/compose/build.go
+++ b/local/compose/build.go
@@ -189,7 +189,6 @@ func (s *composeService) toBuildOptions(service types.ServiceConfig, contextPath
 		Target:    service.Build.Target,
 		Exports:   []bclient.ExportEntry{{Type: "image", Attrs: map[string]string{}}},
 		Platforms: plats,
-		Labels:    service.Labels,
 	}, nil
 }
 
diff --git a/local/compose/create.go b/local/compose/create.go
index 1ff3a7eb..4079806c 100644
--- a/local/compose/create.go
+++ b/local/compose/create.go
@@ -289,12 +289,6 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 		Resources:    resources,
 		VolumeDriver: service.VolumeDriver,
 		VolumesFrom:  service.VolumesFrom,
-		DNS:          service.DNS,
-		DNSSearch:    service.DNSSearch,
-		DNSOptions:   service.DNSOpts,
-		ExtraHosts:   service.ExtraHosts,
-		SecurityOpt:  service.SecurityOpt,
-		UsernsMode:   container.UsernsMode(service.UserNSMode),
 	}
 
 	networkConfig := buildDefaultNetworkConfig(service, networkMode, getContainerName(p.Name, service, number))
diff --git a/local/compose/pull.go b/local/compose/pull.go
index 9c4772fe..e0a277c1 100644
--- a/local/compose/pull.go
+++ b/local/compose/pull.go
@@ -37,7 +37,7 @@ import (
 )
 
 func (s *composeService) Pull(ctx context.Context, project *types.Project) error {
-	configFile, err := cliconfig.Load(config.Dir(ctx))
+	configFile, err := cliconfig.Load(config.Dir())
 	if err != nil {
 		return err
 	}
diff --git a/local/compose/push.go b/local/compose/push.go
index 67056fd3..ac0185f6 100644
--- a/local/compose/push.go
+++ b/local/compose/push.go
@@ -39,7 +39,7 @@ import (
 )
 
 func (s *composeService) Push(ctx context.Context, project *types.Project, options compose.PushOptions) error {
-	configFile, err := cliconfig.Load(config.Dir(ctx))
+	configFile, err := cliconfig.Load(config.Dir())
 	if err != nil {
 		return err
 	}


Test patch:
diff --git a/cli/server/interceptor_test.go b/cli/server/interceptor_test.go
index 66dbf4a7..3bc401a5 100644
--- a/cli/server/interceptor_test.go
+++ b/cli/server/interceptor_test.go
@@ -41,7 +41,7 @@ func testContext(t *testing.T) context.Context {
 	})
 
 	ctx := context.Background()
-	ctx = config.WithDir(ctx, dir)
+	config.WithDir(dir)
 	err = ioutil.WriteFile(path.Join(dir, "config.json"), []byte(`{"currentContext": "default"}`), 0644)
 	assert.NilError(t, err)
 
@@ -100,7 +100,7 @@ func callStream(ctx context.Context, t *testing.T, interceptor grpc.StreamServer
 	}, &grpc.StreamServerInfo{
 		FullMethod: "/com.docker.api.protos.context.v1.Contexts/test",
 	}, func(srv interface{}, stream grpc.ServerStream) error {
-		currentContext = apicontext.CurrentContext(stream.Context())
+		currentContext = apicontext.Current()
 		return nil
 	})
 
@@ -114,7 +114,7 @@ func callUnary(ctx context.Context, t *testing.T, interceptor grpc.UnaryServerIn
 	resp, err := interceptor(ctx, nil, &grpc.UnaryServerInfo{
 		FullMethod: "/com.docker.api.protos.context.v1.Contexts/test",
 	}, func(ctx context.Context, req interface{}) (interface{}, error) {
-		currentContext = apicontext.CurrentContext(ctx)
+		currentContext = apicontext.Current()
 		return nil, nil
 	})