diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 34bd70286..106971351 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -54,11 +54,11 @@ jobs:
       - name: Install Protoc
         uses: arduino/setup-protoc@master
         with:
-          version: '3.9.1'
+          version: "3.9.1"
 
       - uses: actions/setup-node@v1
         with:
-          node-version: '10.x'
+          node-version: "10.x"
 
       - name: E2E Test
-        run: make e2e-local
\ No newline at end of file
+        run: make e2e-local
diff --git a/Dockerfile b/Dockerfile
index 3f7f2eb49..fcdb81bc5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # syntax = docker/dockerfile:experimental
-ARG GO_VERSION=1.14.2
+ARG GO_VERSION=1.14.3-alpine
 
 FROM golang:${GO_VERSION} AS base
 ARG TARGET_OS=unknown
@@ -7,6 +7,8 @@ ARG TARGET_ARCH=unknown
 ARG PWD=/api
 ENV GO111MODULE=on
 
+RUN apk update && apk add -U docker make
+
 WORKDIR ${PWD}
 ADD go.* ${PWD}
 RUN go mod download
@@ -18,9 +20,7 @@ ARG TARGET_ARCH=unknown
 ARG PWD=/api
 ENV GO111MODULE=on
 
-RUN apt-get update && apt-get install --no-install-recommends -y \
-    protobuf-compiler \
-    libprotobuf-dev
+RUN apk update && apk add protoc make
 
 RUN go get github.com/golang/protobuf/protoc-gen-go@v1.4.1 && \
     go get golang.org/x/tools/cmd/goimports
@@ -52,4 +52,5 @@ FROM scratch AS cross
 COPY --from=make-cross /api/bin/* .
 
 FROM base as test
+ENV CGO_ENABLED=0
 RUN make -f builder.Makefile test
diff --git a/backend/v1/backend.pb.go b/backend/v1/backend.pb.go
index b93e99e7c..457e7ed65 100644
--- a/backend/v1/backend.pb.go
+++ b/backend/v1/backend.pb.go
@@ -27,7 +27,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.22.0
-// 	protoc        v3.6.1
+// 	protoc        v3.11.2
 // source: backend/v1/backend.proto
 
 package v1
diff --git a/builder.Makefile b/builder.Makefile
index 4b2cff499..763106cc5 100644
--- a/builder.Makefile
+++ b/builder.Makefile
@@ -26,14 +26,14 @@
 GOOS ?= $(shell go env GOOS)
 GOARCH ?= $(shell go env GOARCH)
 
-PROTOS=$(shell find . -name \*.proto)
+PROTOS=$(shell find . -not \( -path ./tests -prune \) -name \*.proto)
 
 EXTENSION :=
 ifeq ($(GOOS),windows)
   EXTENSION := .exe
 endif
 
-STATIC_FLAGS= CGO_ENABLED=0
+STATIC_FLAGS=CGO_ENABLED=0
 LDFLAGS := "-s -w"
 GO_BUILD = $(STATIC_FLAGS) go build -trimpath -ldflags=$(LDFLAGS)
 
diff --git a/cli/cmd/context/ls.go b/cli/cmd/context/ls.go
index 569fce52c..5c7e7ffbc 100644
--- a/cli/cmd/context/ls.go
+++ b/cli/cmd/context/ls.go
@@ -31,6 +31,8 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"sort"
+	"strings"
 	"text/tabwriter"
 
 	"github.com/spf13/cobra"
@@ -60,17 +62,43 @@ func runList(ctx context.Context) error {
 		return err
 	}
 
-	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
-	fmt.Fprintln(w, "NAME\tDESCRIPTION\tTYPE")
-	format := "%s\t%s\t%s\n"
+	sort.Slice(contexts, func(i, j int) bool {
+		return strings.Compare(contexts[i].Name, contexts[j].Name) == -1
+	})
+
+	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+	fmt.Fprintln(w, "NAME\tTYPE\tDESCRIPTION\tDOCKER ENPOINT\tKUBERNETES ENDPOINT\tORCHESTRATOR")
+	format := "%s\t%s\t%s\t%s\t%s\t%s\n"
 
 	for _, c := range contexts {
 		contextName := c.Name
 		if c.Name == currentContext {
 			contextName += " *"
 		}
-		fmt.Fprintf(w, format, contextName, c.Metadata.Description, c.Metadata.Type)
+
+		fmt.Fprintf(w,
+			format,
+			contextName,
+			c.Metadata.Type,
+			c.Metadata.Description,
+			getEndpoint("docker", c.Endpoints),
+			getEndpoint("kubernetes", c.Endpoints),
+			c.Metadata.StackOrchestrator)
 	}
 
 	return w.Flush()
 }
+
+func getEndpoint(name string, meta map[string]store.Endpoint) string {
+	d, ok := meta[name]
+	if !ok {
+		return ""
+	}
+
+	result := d.Host
+	if d.DefaultNamespace != "" {
+		result += fmt.Sprintf(" (%s)", d.DefaultNamespace)
+	}
+
+	return result
+}
diff --git a/cli/cmd/context/ls_test.go b/cli/cmd/context/ls_test.go
new file mode 100644
index 000000000..861e9156c
--- /dev/null
+++ b/cli/cmd/context/ls_test.go
@@ -0,0 +1,25 @@
+package context
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"github.com/stretchr/testify/suite"
+	"gotest.tools/v3/golden"
+
+	"github.com/docker/api/tests/framework"
+)
+
+type ContextSuite struct {
+	framework.CliSuite
+}
+
+func (sut *ContextSuite) TestLs() {
+	err := runList(sut.Context())
+	require.Nil(sut.T(), err)
+	golden.Assert(sut.T(), sut.GetStdOut(), "ls-out.golden")
+}
+
+func TestPs(t *testing.T) {
+	suite.Run(t, new(ContextSuite))
+}
diff --git a/cli/cmd/context/testdata/ls-out.golden b/cli/cmd/context/testdata/ls-out.golden
new file mode 100644
index 000000000..c4b2668e8
--- /dev/null
+++ b/cli/cmd/context/testdata/ls-out.golden
@@ -0,0 +1,3 @@
+NAME                TYPE                DESCRIPTION                               DOCKER ENPOINT                KUBERNETES ENDPOINT   ORCHESTRATOR
+default             docker              Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
+example *           example                                                                                                           
diff --git a/cli/cmd/ps.go b/cli/cmd/ps.go
index 0ea4c749d..1b253ab67 100644
--- a/cli/cmd/ps.go
+++ b/cli/cmd/ps.go
@@ -54,7 +54,7 @@ func runPs(ctx context.Context, opts psOpts) error {
 		return nil
 	}
 
-	w := tabwriter.NewWriter(os.Stdout, 0, 0, 8, ' ', 0)
+	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
 	fmt.Fprintf(w, "CONTAINER ID\tIMAGE\tCOMMAND\tSTATUS\tPORTS\n")
 	format := "%s\t%s\t%s\t%s\t%s\n"
 	for _, c := range containers {
diff --git a/cli/cmd/ps_test.go b/cli/cmd/ps_test.go
index e078d12cd..4d7ffd56a 100644
--- a/cli/cmd/ps_test.go
+++ b/cli/cmd/ps_test.go
@@ -1,72 +1,18 @@
 package cmd
 
 import (
-	"context"
-	"io/ioutil"
-	"os"
 	"testing"
 
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
-	"gotest.tools/v3/assert"
 	"gotest.tools/v3/golden"
 
-	apicontext "github.com/docker/api/context"
-	"github.com/docker/api/context/store"
 	_ "github.com/docker/api/example"
+	"github.com/docker/api/tests/framework"
 )
 
 type PsSuite struct {
-	suite.Suite
-	ctx            context.Context
-	writer         *os.File
-	reader         *os.File
-	originalStdout *os.File
-	storeRoot      string
-}
-
-func (sut *PsSuite) BeforeTest(suiteName, testName string) {
-	ctx := context.Background()
-	ctx = apicontext.WithCurrentContext(ctx, "example")
-	dir, err := ioutil.TempDir("", "store")
-	require.Nil(sut.T(), err)
-	s, err := store.New(
-		store.WithRoot(dir),
-	)
-	require.Nil(sut.T(), err)
-
-	err = s.Create("example", store.TypedContext{
-		Type: "example",
-	})
-	require.Nil(sut.T(), err)
-
-	sut.storeRoot = dir
-
-	ctx = store.WithContextStore(ctx, s)
-	sut.ctx = ctx
-
-	sut.originalStdout = os.Stdout
-	r, w, err := os.Pipe()
-	require.Nil(sut.T(), err)
-
-	os.Stdout = w
-	sut.writer = w
-	sut.reader = r
-}
-
-func (sut *PsSuite) getStdOut() string {
-	err := sut.writer.Close()
-	require.Nil(sut.T(), err)
-
-	out, _ := ioutil.ReadAll(sut.reader)
-
-	return string(out)
-}
-
-func (sut *PsSuite) AfterTest(suiteName, testName string) {
-	os.Stdout = sut.originalStdout
-	err := os.RemoveAll(sut.storeRoot)
-	require.Nil(sut.T(), err)
+	framework.CliSuite
 }
 
 func (sut *PsSuite) TestPs() {
@@ -74,10 +20,10 @@ func (sut *PsSuite) TestPs() {
 		quiet: false,
 	}
 
-	err := runPs(sut.ctx, opts)
-	assert.NilError(sut.T(), err)
+	err := runPs(sut.Context(), opts)
+	require.Nil(sut.T(), err)
 
-	golden.Assert(sut.T(), sut.getStdOut(), "ps-out.golden")
+	golden.Assert(sut.T(), sut.GetStdOut(), "ps-out.golden")
 }
 
 func (sut *PsSuite) TestPsQuiet() {
@@ -85,10 +31,10 @@ func (sut *PsSuite) TestPsQuiet() {
 		quiet: true,
 	}
 
-	err := runPs(sut.ctx, opts)
-	assert.NilError(sut.T(), err)
+	err := runPs(sut.Context(), opts)
+	require.Nil(sut.T(), err)
 
-	golden.Assert(sut.T(), sut.getStdOut(), "ps-out-quiet.golden")
+	golden.Assert(sut.T(), sut.GetStdOut(), "ps-out-quiet.golden")
 }
 
 func TestPs(t *testing.T) {
diff --git a/cli/cmd/testdata/ps-out.golden b/cli/cmd/testdata/ps-out.golden
index 34dd15f60..ee7ef979c 100644
--- a/cli/cmd/testdata/ps-out.golden
+++ b/cli/cmd/testdata/ps-out.golden
@@ -1,3 +1,3 @@
-CONTAINER ID        IMAGE         COMMAND        STATUS        PORTS
-id                  nginx                                      
-1234                alpine                                     
+CONTAINER ID        IMAGE               COMMAND             STATUS              PORTS
+id                  nginx                                                       
+1234                alpine                                                      
diff --git a/cli/v1/cli.pb.go b/cli/v1/cli.pb.go
index 8a8be6910..768d0b634 100644
--- a/cli/v1/cli.pb.go
+++ b/cli/v1/cli.pb.go
@@ -27,7 +27,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.22.0
-// 	protoc        v3.6.1
+// 	protoc        v3.11.2
 // source: cli/v1/cli.proto
 
 package v1
diff --git a/compose/v1/compose.pb.go b/compose/v1/compose.pb.go
index f0dda24cc..dd41b8767 100644
--- a/compose/v1/compose.pb.go
+++ b/compose/v1/compose.pb.go
@@ -27,7 +27,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.22.0
-// 	protoc        v3.6.1
+// 	protoc        v3.11.2
 // source: compose/v1/compose.proto
 
 package v1
diff --git a/containers/v1/containers.pb.go b/containers/v1/containers.pb.go
index d78466281..b71a9507f 100644
--- a/containers/v1/containers.pb.go
+++ b/containers/v1/containers.pb.go
@@ -27,7 +27,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.22.0
-// 	protoc        v3.6.1
+// 	protoc        v3.11.2
 // source: containers/v1/containers.proto
 
 package v1
diff --git a/context/store/store.go b/context/store/store.go
index 2675ca509..9583d6c3b 100644
--- a/context/store/store.go
+++ b/context/store/store.go
@@ -104,35 +104,31 @@ func New(opts ...Opt) (Store, error) {
 	if err != nil {
 		return nil, err
 	}
+
+	root := filepath.Join(home, configDir)
+	if err := createDirIfNotExist(root); err != nil {
+		return nil, err
+	}
+
 	s := &store{
-		root: filepath.Join(home, configDir),
-	}
-	if _, err := os.Stat(s.root); os.IsNotExist(err) {
-		if err = os.Mkdir(s.root, 0755); err != nil {
-			return nil, err
-		}
+		root: root,
 	}
+
 	for _, opt := range opts {
 		opt(s)
 	}
-	cd := filepath.Join(s.root, contextsDir)
-	if _, err := os.Stat(cd); os.IsNotExist(err) {
-		if err = os.Mkdir(cd, 0755); err != nil {
-			return nil, err
-		}
-	}
-	m := filepath.Join(cd, metadataDir)
-	if _, err := os.Stat(m); os.IsNotExist(err) {
-		if err = os.Mkdir(m, 0755); err != nil {
-			return nil, err
-		}
+
+	m := filepath.Join(s.root, contextsDir, metadataDir)
+	if err := createDirIfNotExist(m); err != nil {
+		return nil, err
 	}
+
 	return s, nil
 }
 
 // Get returns the context with the given name
 func (s *store) Get(name string, getter func() interface{}) (*Metadata, error) {
-	meta := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name), metaFile)
+	meta := filepath.Join(s.root, contextsDir, metadataDir, contextDirOf(name), metaFile)
 	m, err := read(meta, getter)
 	if os.IsNotExist(err) {
 		return nil, errors.Wrap(errdefs.ErrNotFound, objectName(name))
@@ -158,19 +154,26 @@ func read(meta string, getter func() interface{}) (*Metadata, error) {
 	if err := json.Unmarshal(um.Metadata, &uc); err != nil {
 		return nil, err
 	}
+	if uc.Type == "" {
+		uc.Type = "docker"
+	}
 
-	data, err := parse(uc.Data, getter)
-	if err != nil {
-		return nil, err
+	var data interface{}
+	if uc.Data != nil {
+		data, err = parse(uc.Data, getter)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	return &Metadata{
 		Name:      um.Name,
 		Endpoints: um.Endpoints,
 		Metadata: TypedContext{
-			Description: uc.Description,
-			Type:        uc.Type,
-			Data:        data,
+			StackOrchestrator: uc.StackOrchestrator,
+			Description:       uc.Description,
+			Type:              uc.Type,
+			Data:              data,
 		},
 	}, nil
 }
@@ -183,10 +186,12 @@ func parse(payload []byte, getter func() interface{}) (interface{}, error) {
 		}
 		return res, nil
 	}
+
 	typed := getter()
 	if err := json.Unmarshal(payload, &typed); err != nil {
 		return nil, err
 	}
+
 	return reflect.ValueOf(typed).Elem().Interface(), nil
 }
 
@@ -204,7 +209,7 @@ func (s *store) Create(name string, data TypedContext) error {
 	if name == DefaultContextName {
 		return errors.Wrap(errdefs.ErrAlreadyExists, objectName(name))
 	}
-	dir := contextdirOf(name)
+	dir := contextDirOf(name)
 	metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
 	if _, err := os.Stat(metaDir); !os.IsNotExist(err) {
 		return errors.Wrap(errdefs.ErrAlreadyExists, objectName(name))
@@ -222,9 +227,9 @@ func (s *store) Create(name string, data TypedContext) error {
 	meta := Metadata{
 		Name:     name,
 		Metadata: data,
-		Endpoints: map[string]interface{}{
-			(dockerEndpointKey): dummyContext{},
-			(data.Type):         dummyContext{},
+		Endpoints: map[string]Endpoint{
+			(dockerEndpointKey): {},
+			(data.Type):         {},
 		},
 	}
 
@@ -255,6 +260,14 @@ func (s *store) List() ([]*Metadata, error) {
 		}
 	}
 
+	// The default context is not stored in the store, it is in-memory only
+	// so we need a special case for it.
+	dockerDefault, err := dockerDefaultContext()
+	if err != nil {
+		return nil, err
+	}
+
+	result = append(result, dockerDefault)
 	return result, nil
 }
 
@@ -262,7 +275,7 @@ func (s *store) Remove(name string) error {
 	if name == DefaultContextName {
 		return errors.Wrap(errdefs.ErrForbidden, objectName(name))
 	}
-	dir := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name))
+	dir := filepath.Join(s.root, contextsDir, metadataDir, contextDirOf(name))
 	// Check if directory exists because os.RemoveAll returns nil if it doesn't
 	if _, err := os.Stat(dir); os.IsNotExist(err) {
 		return errors.Wrap(errdefs.ErrNotFound, objectName(name))
@@ -273,7 +286,7 @@ func (s *store) Remove(name string) error {
 	return nil
 }
 
-func contextdirOf(name string) string {
+func contextDirOf(name string) string {
 	return digest.FromString(name).Encoded()
 }
 
@@ -281,32 +294,49 @@ func objectName(name string) string {
 	return fmt.Sprintf("context %q", name)
 }
 
+func createDirIfNotExist(dir string) error {
+	if _, err := os.Stat(dir); os.IsNotExist(err) {
+		if err = os.MkdirAll(dir, 0755); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 type dummyContext struct{}
 
+// Endpoint holds the Docker or the Kubernetes endpoint
+type Endpoint struct {
+	Host             string `json:",omitempty"`
+	DefaultNamespace string `json:",omitempty"`
+}
+
 // Metadata represents the docker context metadata
 type Metadata struct {
-	Name      string                 `json:",omitempty"`
-	Metadata  TypedContext           `json:",omitempty"`
-	Endpoints map[string]interface{} `json:",omitempty"`
+	Name      string              `json:",omitempty"`
+	Metadata  TypedContext        `json:",omitempty"`
+	Endpoints map[string]Endpoint `json:",omitempty"`
 }
 
 type untypedMetadata struct {
-	Name      string                 `json:",omitempty"`
-	Metadata  json.RawMessage        `json:",omitempty"`
-	Endpoints map[string]interface{} `json:",omitempty"`
+	Name      string              `json:",omitempty"`
+	Metadata  json.RawMessage     `json:",omitempty"`
+	Endpoints map[string]Endpoint `json:",omitempty"`
 }
 
 type untypedContext struct {
-	Data        json.RawMessage `json:",omitempty"`
-	Description string          `json:",omitempty"`
-	Type        string          `json:",omitempty"`
+	StackOrchestrator string          `json:",omitempty"`
+	Type              string          `json:",omitempty"`
+	Description       string          `json:",omitempty"`
+	Data              json.RawMessage `json:",omitempty"`
 }
 
 // TypedContext is a context with a type (moby, aci, etc...)
 type TypedContext struct {
-	Type        string      `json:",omitempty"`
-	Description string      `json:",omitempty"`
-	Data        interface{} `json:",omitempty"`
+	StackOrchestrator string      `json:",omitempty"`
+	Type              string      `json:",omitempty"`
+	Description       string      `json:",omitempty"`
+	Data              interface{} `json:",omitempty"`
 }
 
 // AciContext is the context for ACI
diff --git a/context/store/store_test.go b/context/store/store_test.go
index f76275f47..c901afaaf 100644
--- a/context/store/store_test.go
+++ b/context/store/store_test.go
@@ -103,9 +103,10 @@ func (suite *StoreTestSuite) TestList() {
 	contexts, err := suite.store.List()
 	require.Nil(suite.T(), err)
 
-	require.Equal(suite.T(), len(contexts), 2)
-	require.Equal(suite.T(), contexts[0].Name, "test1")
-	require.Equal(suite.T(), contexts[1].Name, "test2")
+	require.Equal(suite.T(), len(contexts), 3)
+	require.Equal(suite.T(), "test1", contexts[0].Name)
+	require.Equal(suite.T(), "test2", contexts[1].Name)
+	require.Equal(suite.T(), "default", contexts[2].Name)
 }
 
 func (suite *StoreTestSuite) TestRemoveNotFound() {
@@ -119,13 +120,15 @@ func (suite *StoreTestSuite) TestRemove() {
 	require.Nil(suite.T(), err)
 	contexts, err := suite.store.List()
 	require.Nil(suite.T(), err)
-	require.Equal(suite.T(), len(contexts), 1)
+	require.Equal(suite.T(), len(contexts), 2)
 
 	err = suite.store.Remove("testremove")
 	require.Nil(suite.T(), err)
 	contexts, err = suite.store.List()
 	require.Nil(suite.T(), err)
-	require.Equal(suite.T(), len(contexts), 0)
+	// The default context is always here, that's why we
+	// have len(contexts) == 1
+	require.Equal(suite.T(), len(contexts), 1)
 }
 
 func TestExampleTestSuite(t *testing.T) {
diff --git a/context/store/storedefault.go b/context/store/storedefault.go
new file mode 100644
index 000000000..61283d3fc
--- /dev/null
+++ b/context/store/storedefault.go
@@ -0,0 +1,75 @@
+package store
+
+import (
+	"bytes"
+	"encoding/json"
+	"os/exec"
+
+	"github.com/pkg/errors"
+)
+
+// Represents a context as created by the docker cli
+type defaultContext struct {
+	Metadata  TypedContext
+	Endpoints endpoints
+}
+
+// Normally (in docker/cli code), the endpoints are mapped as map[string]interface{}
+// but docker cli contexts always have a "docker" and "kubernetes" key so we
+// create real types for those to no have to juggle around with interfaces.
+type endpoints struct {
+	Docker     endpoint `json:"docker,omitempty"`
+	Kubernetes endpoint `json:"kubernetes,omitempty"`
+}
+
+// Both "docker" and "kubernetes" endpoints in the docker cli created contexts
+// have a "Host", only kubernetes has the "DefaultNamespace", we put both of
+// those here for easier manipulation and to not have to create two distinct
+// structs
+type endpoint struct {
+	Host             string
+	DefaultNamespace string
+}
+
+func dockerDefaultContext() (*Metadata, error) {
+	cmd := exec.Command("docker", "context", "inspect", "default")
+	var stdout bytes.Buffer
+	cmd.Stdout = &stdout
+	err := cmd.Run()
+	if err != nil {
+		return nil, err
+	}
+
+	var ctx []defaultContext
+	err = json.Unmarshal(stdout.Bytes(), &ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(ctx) != 1 {
+		return nil, errors.New("found more than one default context")
+	}
+
+	defaultCtx := ctx[0]
+
+	meta := Metadata{
+		Name: "default",
+		Endpoints: map[string]Endpoint{
+			"docker": {
+				Host: defaultCtx.Endpoints.Docker.Host,
+			},
+			"kubernetes": {
+				Host:             defaultCtx.Endpoints.Kubernetes.Host,
+				DefaultNamespace: defaultCtx.Endpoints.Kubernetes.DefaultNamespace,
+			},
+		},
+		Metadata: TypedContext{
+			Description:       "Current DOCKER_HOST based configuration",
+			Type:              "docker",
+			StackOrchestrator: defaultCtx.Metadata.StackOrchestrator,
+			Data:              defaultCtx.Metadata,
+		},
+	}
+
+	return &meta, nil
+}
diff --git a/context/store/storedefault_test.go b/context/store/storedefault_test.go
new file mode 100644
index 000000000..7d25e874f
--- /dev/null
+++ b/context/store/storedefault_test.go
@@ -0,0 +1,13 @@
+package store
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDefaultContext(t *testing.T) {
+	s, err := dockerDefaultContext()
+	assert.Nil(t, err)
+	assert.Equal(t, "default", s.Name)
+}
diff --git a/go.mod b/go.mod
index 4270c3493..907c7c7c0 100644
--- a/go.mod
+++ b/go.mod
@@ -40,6 +40,7 @@ require (
 	github.com/stretchr/testify v1.5.1
 	golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0
 	golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
+	golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect
 	golang.org/x/text v0.3.2 // indirect
 	google.golang.org/grpc v1.29.1
 	google.golang.org/protobuf v1.21.0
diff --git a/go.sum b/go.sum
index 6f8c8169a..dbdc66cb7 100644
--- a/go.sum
+++ b/go.sum
@@ -66,6 +66,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -217,7 +218,9 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
 github.com/robpike/filter v0.0.0-20150108201509-2984852a2183 h1:qDhD/wJDGyWrXKLIKmEKpKK/ejaZlguyeEaLZzmrtzo=
 github.com/robpike/filter v0.0.0-20150108201509-2984852a2183/go.mod h1:3dvYi47BCPInRb2ILlNnrXfl++XpwTWLbIxPyJsUvCw=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@@ -301,6 +304,8 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
diff --git a/tests/framework/clisuite.go b/tests/framework/clisuite.go
new file mode 100644
index 000000000..fafae5041
--- /dev/null
+++ b/tests/framework/clisuite.go
@@ -0,0 +1,77 @@
+package framework
+
+import (
+	"context"
+	"io/ioutil"
+	"os"
+
+	"github.com/stretchr/testify/require"
+	"github.com/stretchr/testify/suite"
+
+	apicontext "github.com/docker/api/context"
+	"github.com/docker/api/context/store"
+)
+
+// CliSuite is a helper struct that creates a configured context
+// and captures the output of a command. it should be used in the
+// same way as testify.suite.Suite
+type CliSuite struct {
+	suite.Suite
+	ctx            context.Context
+	writer         *os.File
+	reader         *os.File
+	OriginalStdout *os.File
+	storeRoot      string
+}
+
+// BeforeTest is called by testify.suite
+func (sut *CliSuite) BeforeTest(suiteName, testName string) {
+	ctx := context.Background()
+	ctx = apicontext.WithCurrentContext(ctx, "example")
+	dir, err := ioutil.TempDir("", "store")
+	require.Nil(sut.T(), err)
+	s, err := store.New(
+		store.WithRoot(dir),
+	)
+	require.Nil(sut.T(), err)
+
+	err = s.Create("example", store.TypedContext{
+		Type: "example",
+	})
+	require.Nil(sut.T(), err)
+
+	sut.storeRoot = dir
+
+	ctx = store.WithContextStore(ctx, s)
+	sut.ctx = ctx
+
+	sut.OriginalStdout = os.Stdout
+	r, w, err := os.Pipe()
+	require.Nil(sut.T(), err)
+
+	os.Stdout = w
+	sut.writer = w
+	sut.reader = r
+}
+
+// Context returns a configured context
+func (sut *CliSuite) Context() context.Context {
+	return sut.ctx
+}
+
+// GetStdOut returns the output of the command
+func (sut *CliSuite) GetStdOut() string {
+	err := sut.writer.Close()
+	require.Nil(sut.T(), err)
+
+	out, _ := ioutil.ReadAll(sut.reader)
+
+	return string(out)
+}
+
+// AfterTest is called by testify.suite
+func (sut *CliSuite) AfterTest(suiteName, testName string) {
+	os.Stdout = sut.OriginalStdout
+	err := os.RemoveAll(sut.storeRoot)
+	require.Nil(sut.T(), err)
+}