From 9c6aead79782780d2f5748e61d2e1451e5829463 Mon Sep 17 00:00:00 2001
From: Guillaume Tardif <guillaume.tardif@docker.com>
Date: Wed, 16 Sep 2020 15:53:25 +0200
Subject: [PATCH] =?UTF-8?q?Allow=20running=20commands=20with=20HOME=3D?=
 =?UTF-8?q?=E2=80=9C=E2=80=9D.=20Do=20not=20try=20to=20create=20~/.docker?=
 =?UTF-8?q?=20before=20using=20CONFIG=5FDIR.=20HOME=3D=E2=80=9C=E2=80=9D?=
 =?UTF-8?q?=20will=20result=20in=20trying=20to=20use=20a=20relative=20.doc?=
 =?UTF-8?q?ker=20folder=20as=20default=20config=20folder,=20and=20if=20we?=
 =?UTF-8?q?=20cannot=20create=20the=20context=20store=20for=20any=20reason?=
 =?UTF-8?q?,=20try=20delegate=20to=20Moby=20CLI.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Guillaume Tardif <guillaume.tardif@docker.com>
---
 cli/main.go                 |  4 ++--
 context/store/store.go      | 31 +++----------------------------
 context/store/store_test.go |  2 +-
 server/interceptor.go       |  2 +-
 tests/e2e/e2e_test.go       | 21 +++++++++++++++++++++
 tests/framework/unit.go     |  4 +---
 6 files changed, 29 insertions(+), 35 deletions(-)

diff --git a/cli/main.go b/cli/main.go
index a495d53e5..a7a2bcd7b 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -169,9 +169,9 @@ func main() {
 
 	currentContext := determineCurrentContext(opts.Context, configDir)
 
-	s, err := store.New(store.WithRoot(configDir))
+	s, err := store.New(configDir)
 	if err != nil {
-		fatal(errors.Wrap(err, "unable to create context store"))
+		mobycli.Exec()
 	}
 
 	ctype := store.DefaultContextType
diff --git a/context/store/store.go b/context/store/store.go
index 2fad618ef..1294f5c78 100644
--- a/context/store/store.go
+++ b/context/store/store.go
@@ -62,7 +62,6 @@ const (
 
 const (
 	dockerEndpointKey = "docker"
-	configDir         = ".docker"
 	contextsDir       = "contexts"
 	metadataDir       = "meta"
 	metaFile          = "meta.json"
@@ -111,34 +110,10 @@ type store struct {
 	root string
 }
 
-// Opt is a functional option for the store
-type Opt func(*store)
-
-// WithRoot sets a new root to the store
-func WithRoot(root string) Opt {
-	return func(s *store) {
-		s.root = root
-	}
-}
-
-// New returns a configured context store with $HOME/.docker as root
-func New(opts ...Opt) (Store, error) {
-	home, err := os.UserHomeDir()
-	if err != nil {
-		return nil, err
-	}
-
-	root := filepath.Join(home, configDir)
-	if err := createDirIfNotExist(root); err != nil {
-		return nil, err
-	}
-
+// New returns a configured context store with specified root dir (eg. $HOME/.docker) as root
+func New(rootDir string) (Store, error) {
 	s := &store{
-		root: root,
-	}
-
-	for _, opt := range opts {
-		opt(s)
+		root: rootDir,
 	}
 
 	m := filepath.Join(s.root, contextsDir, metadataDir)
diff --git a/context/store/store_test.go b/context/store/store_test.go
index 37a29e588..c4fa70d3c 100644
--- a/context/store/store_test.go
+++ b/context/store/store_test.go
@@ -36,7 +36,7 @@ func testStore(t *testing.T) Store {
 		_ = os.RemoveAll(d)
 	})
 
-	s, err := New(WithRoot(d))
+	s, err := New(d)
 	assert.NilError(t, err)
 
 	return s
diff --git a/server/interceptor.go b/server/interceptor.go
index aa6a8043d..b4f449487 100644
--- a/server/interceptor.go
+++ b/server/interceptor.go
@@ -117,7 +117,7 @@ func configureContext(ctx context.Context, currentContext string, method string)
 		}
 	}
 
-	s, err := store.New(store.WithRoot(configDir))
+	s, err := store.New(configDir)
 	if err != nil {
 		return nil, err
 	}
diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go
index 7b4db68d7..54e4c99dc 100644
--- a/tests/e2e/e2e_test.go
+++ b/tests/e2e/e2e_test.go
@@ -293,6 +293,27 @@ func TestLegacy(t *testing.T) {
 		})
 	})
 
+	t.Run("run without HOME defined", func(t *testing.T) {
+		cmd := c.NewDockerCmd("ps")
+		cmd.Env = []string{"PATH=" + c.BinDir}
+		res := icmd.RunCmd(cmd)
+		res.Assert(t, icmd.Expected{
+			ExitCode: 0,
+			Out:      "CONTAINER ID",
+		})
+		assert.Equal(t, res.Stderr(), "")
+	})
+
+	t.Run("run without write access to context store", func(t *testing.T) {
+		cmd := c.NewDockerCmd("ps")
+		cmd.Env = []string{"PATH=" + c.BinDir, "HOME=/doesnotexist/"}
+		res := icmd.RunCmd(cmd)
+		res.Assert(t, icmd.Expected{
+			ExitCode: 0,
+			Out:      "CONTAINER ID",
+		})
+	})
+
 	t.Run("host flag", func(t *testing.T) {
 		stderr := "Cannot connect to the Docker daemon at tcp://localhost:123"
 		if runtime.GOOS == "windows" {
diff --git a/tests/framework/unit.go b/tests/framework/unit.go
index 76b0df771..3d00eb8eb 100644
--- a/tests/framework/unit.go
+++ b/tests/framework/unit.go
@@ -48,9 +48,7 @@ func NewTestCLI(t *testing.T) *TestCLI {
 		_ = os.RemoveAll(dir)
 	})
 
-	s, err := store.New(
-		store.WithRoot(dir),
-	)
+	s, err := store.New(dir)
 	assert.Check(t, cmp.Nil(err))
 	err = s.Create("example", "example", "", store.ContextMetadata{})
 	assert.Check(t, cmp.Nil(err))