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

Add comments on exported items, remove example command

Also add `make lint` to run the linter
Djordje Lukic 5 жил өмнө
parent
commit
24c035e822

+ 33 - 0
.golangci.yml

@@ -0,0 +1,33 @@
+linters:
+  run:
+    concurrency: 2
+  enable-all: false
+  disable-all: true
+  enable:
+    - deadcode
+    - errcheck
+    - gocyclo
+    - gofmt
+    - goimports
+    - golint
+    - gosimple
+    - govet
+    - ineffassign
+    - interfacer
+    - lll
+    - misspell
+    - nakedret
+    - staticcheck
+    - structcheck
+    - typecheck
+    - unconvert
+    - unparam
+    - unused
+    - varcheck
+linters-settings:
+  gocyclo:
+    min-complexity: 16
+  lll:
+    line-length: 200
+issues:
+  exclude-use-default: false

+ 7 - 1
Makefile

@@ -53,8 +53,14 @@ test: ## Run unit tests
 cache-clear: # Clear the builder cache
 	@docker builder prune --force --filter type=exec.cachemount --filter=unused-for=24h
 
+lint: ## run linter(s)
+	@echo "Linting..."
+	golangci-lint run --timeout 10m0s ./...
+
 help: ## Show help
 	@echo Please specify a build target. The choices are:
 	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
 
-.PHONY: all protos cli cross test cache-clear help
+FORCE:
+
+.PHONY: all protos cli cross test cache-clear lint help

+ 3 - 28
azure/aci.go

@@ -30,10 +30,7 @@ func init() {
 }
 
 func createACIContainers(ctx context.Context, aciContext store.AciContext, groupDefinition containerinstance.ContainerGroup) (c containerinstance.ContainerGroup, err error) {
-	containerGroupsClient, err := getContainerGroupsClient(aciContext.SubscriptionID)
-	if err != nil {
-		return c, fmt.Errorf("cannot get container group client: %v", err)
-	}
+	containerGroupsClient := getContainerGroupsClient(aciContext.SubscriptionID)
 
 	// Check if the container group already exists
 	_, err = containerGroupsClient.Get(ctx, aciContext.ResourceGroup, *groupDefinition.Name)
@@ -96,28 +93,6 @@ func createACIContainers(ctx context.Context, aciContext store.AciContext, group
 	return containerGroup, err
 }
 
-func listACIContainers(aciContext store.AciContext) (c []containerinstance.ContainerGroup, err error) {
-	ctx := context.TODO()
-	containerGroupsClient, err := getContainerGroupsClient(aciContext.SubscriptionID)
-	if err != nil {
-		return c, fmt.Errorf("cannot get container group client: %v", err)
-	}
-
-	var containers []containerinstance.ContainerGroup
-	result, err := containerGroupsClient.ListByResourceGroup(ctx, aciContext.ResourceGroup)
-	if err != nil {
-		return []containerinstance.ContainerGroup{}, err
-	}
-	for result.NotDone() {
-		containers = append(containers, result.Values()...)
-		if err := result.NextWithContext(ctx); err != nil {
-			return []containerinstance.ContainerGroup{}, err
-		}
-	}
-
-	return containers, err
-}
-
 func execACIContainer(ctx context.Context, aciContext store.AciContext, command, containerGroup string, containerName string) (c containerinstance.ContainerExecResponse, err error) {
 	containerClient := getContainerClient(aciContext.SubscriptionID)
 	rows, cols := getTermSize()
@@ -233,11 +208,11 @@ func getACIContainerLogs(ctx context.Context, aciContext store.AciContext, conta
 	return *logs.Content, err
 }
 
-func getContainerGroupsClient(subscriptionID string) (containerinstance.ContainerGroupsClient, error) {
+func getContainerGroupsClient(subscriptionID string) containerinstance.ContainerGroupsClient {
 	auth, _ := auth.NewAuthorizerFromCLI()
 	containerGroupsClient := containerinstance.NewContainerGroupsClient(subscriptionID)
 	containerGroupsClient.Authorizer = auth
-	return containerGroupsClient, nil
+	return containerGroupsClient
 }
 
 func getContainerClient(subscriptionID string) containerinstance.ContainerClient {

+ 1 - 0
azure/backend.go

@@ -36,6 +36,7 @@ func getter() interface{} {
 	return &store.AciContext{}
 }
 
+// New creates a backend that can manage containers on ACI
 func New(ctx context.Context) (containers.ContainerService, error) {
 	currentContext := apicontext.CurrentContext(ctx)
 	contextStore, err := store.New()

+ 4 - 2
azure/convert/convert.go

@@ -21,8 +21,10 @@ const (
 	volumeDriveroptsAccountNameKey = "storage_account_name"
 	volumeDriveroptsAccountKeyKey  = "storage_account_key"
 	singleContainerName            = "single--container--aci"
+	secretInlineMark               = "inline:"
 )
 
+// ToContainerGroup converts a compose project into a ACI container group
 func ToContainerGroup(aciContext store.AciContext, p compose.Project) (containerinstance.ContainerGroup, error) {
 	project := projectAciHelper(p)
 	containerGroupName := strings.ToLower(project.Name)
@@ -98,8 +100,8 @@ func (p projectAciHelper) getAciSecretVolumes() ([]containerinstance.Volume, err
 	var secretVolumes []containerinstance.Volume
 	for secretName, filepathToRead := range p.Secrets {
 		var data []byte
-		if strings.HasPrefix(filepathToRead.File, compose.SecretInlineMark) {
-			data = []byte(filepathToRead.File[len(compose.SecretInlineMark):])
+		if strings.HasPrefix(filepathToRead.File, secretInlineMark) {
+			data = []byte(filepathToRead.File[len(secretInlineMark):])
 		} else {
 			var err error
 			data, err = ioutil.ReadFile(filepathToRead.File)

+ 15 - 12
backend/backend.go

@@ -7,43 +7,46 @@ import (
 )
 
 var (
-	ErrNoType         = errors.New("backend: no type")
-	ErrNoName         = errors.New("backend: no name")
-	ErrTypeRegistered = errors.New("backend: already registered")
+	errNoType         = errors.New("backend: no type")
+	errNoName         = errors.New("backend: no name")
+	errTypeRegistered = errors.New("backend: already registered")
 )
 
-type InitFunc func(context.Context) (interface{}, error)
+type initFunc func(context.Context) (interface{}, error)
 
-type Backend struct {
+type registeredBackend struct {
 	name        string
 	backendType string
-	init        InitFunc
+	init        initFunc
 }
 
 var backends = struct {
-	r []*Backend
+	r []*registeredBackend
 }{}
 
-func Register(name string, backendType string, init InitFunc) {
+// Register adds a typed backend to the registry
+func Register(name string, backendType string, init initFunc) {
 	if name == "" {
-		panic(ErrNoName)
+		panic(errNoName)
 	}
 	if backendType == "" {
-		panic(ErrNoType)
+		panic(errNoType)
 	}
 	for _, b := range backends.r {
 		if b.backendType == backendType {
-			panic(ErrTypeRegistered)
+			panic(errTypeRegistered)
 		}
 	}
 
-	backends.r = append(backends.r, &Backend{
+	backends.r = append(backends.r, &registeredBackend{
 		name,
 		backendType,
 		init,
 	})
 }
 
+// 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) (interface{}, error) {
 	for _, b := range backends.r {
 		if b.backendType == backendType {

+ 1 - 3
cli/cmd/context.go

@@ -38,9 +38,7 @@ import (
 	"github.com/docker/api/context/store"
 )
 
-type CliContext struct {
-}
-
+// ContextCommand manages contexts
 func ContextCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "context",

+ 0 - 71
cli/cmd/example.go

@@ -1,71 +0,0 @@
-/*
-	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 cmd
-
-import (
-	"context"
-	"encoding/json"
-	"os"
-
-	"github.com/pkg/errors"
-	"github.com/spf13/cobra"
-
-	v1 "github.com/docker/api/backend/v1"
-	"github.com/docker/api/client"
-)
-
-var ExampleCommand = cobra.Command{
-	Use:   "example",
-	Short: "sample command using backend, to be removed later",
-	RunE: func(cmd *cobra.Command, args []string) error {
-		ctx := cmd.Context()
-
-		c, err := client.New(ctx)
-		if err != nil {
-			return errors.Wrap(err, "cannot connect to backend")
-		}
-
-		info, err := c.BackendInformation(ctx, &v1.BackendInformationRequest{})
-		if err != nil {
-			return errors.Wrap(err, "fetch backend information")
-		}
-		enc := json.NewEncoder(os.Stdout)
-		enc.SetIndent("", " ")
-		return enc.Encode(info)
-	},
-}
-
-type backendAddressKey struct{}
-
-func BackendAddress(ctx context.Context) (string, error) {
-	v, ok := ctx.Value(backendAddressKey{}).(string)
-	if !ok {
-		return "", errors.New("no backend address key")
-	}
-	return v, nil
-}

+ 5 - 1
cli/cmd/exec.go

@@ -2,6 +2,7 @@ package cmd
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"os"
 	"strings"
@@ -17,6 +18,7 @@ type execOpts struct {
 	Tty bool
 }
 
+// ExecCommand runs a command in a running container
 func ExecCommand() *cobra.Command {
 	var opts execOpts
 	cmd := &cobra.Command{
@@ -52,7 +54,9 @@ func runExec(ctx context.Context, opts execOpts, name string, command string) er
 			return err
 		}
 		defer func() {
-			con.Reset()
+			if err := con.Reset(); err != nil {
+				fmt.Println("Unable to close the console")
+			}
 		}()
 
 		stdout = con

+ 1 - 0
cli/cmd/logs.go

@@ -16,6 +16,7 @@ type logsOpts struct {
 	Tail   string
 }
 
+// LogsCommand fetches and shows logs of a container
 func LogsCommand() *cobra.Command {
 	var opts logsOpts
 	cmd := &cobra.Command{

+ 1 - 0
cli/cmd/ps.go

@@ -11,6 +11,7 @@ import (
 	"github.com/docker/api/client"
 )
 
+// PsCommand lists containers
 var PsCommand = cobra.Command{
 	Use:   "ps",
 	Short: "List containers",

+ 1 - 0
cli/cmd/run/run.go

@@ -36,6 +36,7 @@ import (
 	"github.com/docker/api/client"
 )
 
+// Command runs a container
 func Command() *cobra.Command {
 	var opts runOpts
 	cmd := &cobra.Command{

+ 3 - 1
cli/cmd/serve.go

@@ -20,6 +20,7 @@ type serveOpts struct {
 	address string
 }
 
+// ServeCommand returns the command to serve the API
 func ServeCommand() *cobra.Command {
 	var opts serveOpts
 	cmd := &cobra.Command{
@@ -42,9 +43,10 @@ func runServe(ctx context.Context, opts serveOpts) error {
 	if err != nil {
 		return errors.Wrap(err, "listen unix socket")
 	}
+	// nolint
 	defer listener.Close()
 
-	p := proxy.NewContainerApi()
+	p := proxy.NewContainerAPI()
 
 	containersv1.RegisterContainersServer(s, p)
 	cliv1.RegisterCliServer(s, &cliServer{

+ 1 - 2
cli/main.go

@@ -51,7 +51,7 @@ import (
 )
 
 type mainOpts struct {
-	apicontext.ContextFlags
+	apicontext.Flags
 	debug bool
 }
 
@@ -99,7 +99,6 @@ func main() {
 		cmd.ContextCommand(),
 		&cmd.PsCommand,
 		cmd.ServeCommand(),
-		&cmd.ExampleCommand,
 		run.Command(),
 		cmd.ExecCommand(),
 		cmd.LogsCommand(),

+ 3 - 1
client/client.go

@@ -41,7 +41,7 @@ import (
 	"github.com/docker/api/context/store"
 )
 
-// New returns a GRPC client
+// New returns a backend client
 func New(ctx context.Context) (*Client, error) {
 	currentContext := apicontext.CurrentContext(ctx)
 	s := store.ContextStore(ctx)
@@ -68,6 +68,7 @@ func New(ctx context.Context) (*Client, error) {
 
 }
 
+// Client is a multi-backend client
 type Client struct {
 	backendv1.BackendClient
 	cliv1.CliClient
@@ -78,6 +79,7 @@ type Client struct {
 	cc          containers.ContainerService
 }
 
+// ContainerService returns the backend service for the current context
 func (c *Client) ContainerService() containers.ContainerService {
 	return c.cc
 }

+ 9 - 12
compose/project.go

@@ -14,17 +14,14 @@ import (
 	"github.com/sirupsen/logrus"
 )
 
-const (
-	SecretInlineMark = "inline:"
-)
-
-var SupportedFilenames = []string{
+var supportedFilenames = []string{
 	"compose.yml",
 	"compose.yaml",
 	"docker-compose.yml",
 	"docker-compose.yaml",
 }
 
+// ProjectOptions configures a compose project
 type ProjectOptions struct {
 	Name        string
 	WorkDir     string
@@ -32,6 +29,7 @@ type ProjectOptions struct {
 	Environment []string
 }
 
+// Project represents a compose project with a name
 type Project struct {
 	types.Config
 	projectDir string
@@ -100,7 +98,7 @@ func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
 
 	for {
 		var candidates []string
-		for _, n := range SupportedFilenames {
+		for _, n := range supportedFilenames {
 			f := filepath.Join(pwd, n)
 			if _, err := os.Stat(f); err == nil {
 				candidates = append(candidates, f)
@@ -116,7 +114,7 @@ func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
 		}
 		parent := filepath.Dir(pwd)
 		if parent == pwd {
-			return nil, fmt.Errorf("Can't find a suitable configuration file in this directory or any parent. Is %q the right directory?", pwd)
+			return nil, fmt.Errorf("can't find a suitable configuration file in this directory or any parent. Is %q the right directory?", pwd)
 		}
 		pwd = parent
 	}
@@ -129,12 +127,11 @@ func parseConfigs(configPaths []string) ([]types.ConfigFile, error) {
 		var err error
 		if f == "-" {
 			return []types.ConfigFile{}, errors.New("reading compose file from stdin is not supported")
-		} else {
-			if _, err := os.Stat(f); err != nil {
-				return nil, err
-			}
-			b, err = ioutil.ReadFile(f)
 		}
+		if _, err := os.Stat(f); err != nil {
+			return nil, err
+		}
+		b, err = ioutil.ReadFile(f)
 		if err != nil {
 			return nil, err
 		}

+ 0 - 30
consts.go

@@ -1,30 +0,0 @@
-/*
-	Copyright (c) 2019 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 api
-
-const DockerContextKey = "DOCKER_CONTEXT_KEY"

+ 2 - 1
containers/api.go

@@ -11,7 +11,7 @@ type Container struct {
 	Status      string
 	Image       string
 	Command     string
-	CpuTime     uint64
+	CPUTime     uint64
 	MemoryUsage uint64
 	MemoryLimit uint64
 	PidsCurrent uint64
@@ -37,6 +37,7 @@ type ContainerConfig struct {
 	Ports []Port
 }
 
+// LogsRequest contains configuration about a log request
 type LogsRequest struct {
 	Follow bool
 	Tail   string

+ 3 - 0
context/config.go

@@ -34,6 +34,7 @@ import (
 	"path/filepath"
 )
 
+// LoadConfigFile loads the docker configuration
 func LoadConfigFile(configDir string, configFileName string) (*ConfigFile, error) {
 	filename := filepath.Join(configDir, configFileName)
 	configFile := &ConfigFile{
@@ -45,6 +46,7 @@ func LoadConfigFile(configDir string, configFileName string) (*ConfigFile, error
 		if err != nil {
 			return nil, fmt.Errorf("can't read %s: %w", filename, err)
 		}
+		// nolint
 		defer file.Close()
 		err = json.NewDecoder(file).Decode(&configFile)
 		if err != nil {
@@ -59,6 +61,7 @@ func LoadConfigFile(configDir string, configFileName string) (*ConfigFile, error
 	return configFile, nil
 }
 
+// ConfigFile contains the current context from the docker configuration file
 type ConfigFile struct {
 	Filename       string `json:"-"` // Note: for internal use only
 	CurrentContext string `json:"currentContext,omitempty"`

+ 4 - 1
context/context.go

@@ -6,10 +6,13 @@ import (
 	"golang.org/x/net/context"
 )
 
-const KEY = "context_key"
+// Key is the key where the current docker context is stored in the metadata
+// of a gRPC request
+const Key = "context_key"
 
 type currentContextKey struct{}
 
+// WithCurrentContext sets the name of the current docker context
 func WithCurrentContext(ctx gocontext.Context, contextName string) context.Context {
 	return context.WithValue(ctx, currentContextKey{}, contextName)
 }

+ 4 - 2
context/flags.go

@@ -41,12 +41,14 @@ const (
 	configFileDir  = ".docker"
 )
 
-type ContextFlags struct {
+// Flags are the global cli flags
+type Flags struct {
 	Config  string
 	Context string
 }
 
-func (c *ContextFlags) AddFlags(flags *pflag.FlagSet) {
+// AddFlags adds persistent (globa) flags
+func (c *Flags) AddFlags(flags *pflag.FlagSet) {
 	flags.StringVar(&c.Config, "config", filepath.Join(home(), configFileDir), "Location of the client config files `DIRECTORY`")
 	flags.StringVarP(&c.Context, "context", "c", os.Getenv("DOCKER_CONTEXT"), "context")
 }

+ 17 - 10
context/store/store.go

@@ -47,16 +47,18 @@ const (
 
 type contextStoreKey struct{}
 
+// WithContextStore adds the store to the context
 func WithContextStore(ctx context.Context, store Store) context.Context {
 	return context.WithValue(ctx, contextStoreKey{}, store)
 }
 
+// ContextStore returns the store from the context
 func ContextStore(ctx context.Context) Store {
 	s, _ := ctx.Value(contextStoreKey{}).(Store)
 	return s
 }
 
-// Store
+// Store is the context store
 type Store interface {
 	// Get returns the context with name, it returns an error if the  context
 	// doesn't exist
@@ -74,16 +76,18 @@ type store struct {
 	root string
 }
 
-type StoreOpt func(*store)
+// Opt is a functional option for the store
+type Opt func(*store)
 
-func WithRoot(root string) StoreOpt {
+// 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
-func New(opts ...StoreOpt) (Store, error) {
+// 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
@@ -182,7 +186,7 @@ func (s *store) Create(name string, data TypedContext) error {
 	dir := contextdirOf(name)
 	metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
 	if _, err := os.Stat(metaDir); !os.IsNotExist(err) {
-		return fmt.Errorf("Context %q already exists", name)
+		return fmt.Errorf("context %q already exists", name)
 	}
 
 	err := os.Mkdir(metaDir, 0755)
@@ -191,15 +195,15 @@ func (s *store) Create(name string, data TypedContext) error {
 	}
 
 	if data.Data == nil {
-		data.Data = DummyContext{}
+		data.Data = dummyContext{}
 	}
 
 	meta := Metadata{
 		Name:     name,
 		Metadata: data,
 		Endpoints: map[string]interface{}{
-			"docker":    DummyContext{},
-			(data.Type): DummyContext{},
+			"docker":    dummyContext{},
+			(data.Type): dummyContext{},
 		},
 	}
 
@@ -237,8 +241,9 @@ func contextdirOf(name string) string {
 	return digest.FromString(name).Encoded()
 }
 
-type DummyContext struct{}
+type dummyContext struct{}
 
+// Metadata represents the docker context metadata
 type Metadata struct {
 	Name      string                 `json:",omitempty"`
 	Metadata  TypedContext           `json:",omitempty"`
@@ -257,12 +262,14 @@ type untypedContext struct {
 	Type        string          `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"`
 }
 
+// AciContext is the context for ACI
 type AciContext struct {
 	SubscriptionID string `json:",omitempty"`
 	Location       string `json:",omitempty"`

+ 2 - 1
context/store/store_test.go

@@ -55,7 +55,8 @@ func (suite *StoreTestSuite) BeforeTest(suiteName, testName string) {
 }
 
 func (suite *StoreTestSuite) AfterTest(suiteName, testName string) {
-	os.RemoveAll(suite.dir)
+	err := os.RemoveAll(suite.dir)
+	require.Nil(suite.T(), err)
 }
 
 func (suite *StoreTestSuite) TestCreate() {

+ 1 - 5
example/backend.go

@@ -13,14 +13,10 @@ type containerService struct{}
 
 func init() {
 	backend.Register("example", "example", func(ctx context.Context) (interface{}, error) {
-		return New(), nil
+		return &containerService{}, nil
 	})
 }
 
-func New() containers.ContainerService {
-	return &containerService{}
-}
-
 func (cs *containerService) List(ctx context.Context) ([]containers.Container, error) {
 	return []containers.Container{
 		{

+ 14 - 11
server/proxy/proxy.go

@@ -10,22 +10,25 @@ import (
 
 type clientKey struct{}
 
+// WithClient adds the client to the context
 func WithClient(ctx context.Context, c *client.Client) (context.Context, error) {
 	return context.WithValue(ctx, clientKey{}, c), nil
 }
 
+// Client returns the client from the context
 func Client(ctx context.Context) *client.Client {
 	c, _ := ctx.Value(clientKey{}).(*client.Client)
 	return c
 }
 
-func NewContainerApi() v1.ContainersServer {
-	return &proxyContainerApi{}
+// NewContainerAPI creates a proxy container server
+func NewContainerAPI() v1.ContainersServer {
+	return &proxyContainerAPI{}
 }
 
-type proxyContainerApi struct{}
+type proxyContainerAPI struct{}
 
-func (p *proxyContainerApi) List(ctx context.Context, _ *v1.ListRequest) (*v1.ListResponse, error) {
+func (p *proxyContainerAPI) List(ctx context.Context, _ *v1.ListRequest) (*v1.ListResponse, error) {
 	client := Client(ctx)
 
 	c, err := client.ContainerService().List(ctx)
@@ -46,7 +49,7 @@ func (p *proxyContainerApi) List(ctx context.Context, _ *v1.ListRequest) (*v1.Li
 	return response, nil
 }
 
-func (p *proxyContainerApi) Create(ctx context.Context, request *v1.CreateRequest) (*v1.CreateResponse, error) {
+func (p *proxyContainerAPI) Create(ctx context.Context, request *v1.CreateRequest) (*v1.CreateResponse, error) {
 	client := Client(ctx)
 
 	err := client.ContainerService().Run(ctx, containers.ContainerConfig{
@@ -57,26 +60,26 @@ func (p *proxyContainerApi) Create(ctx context.Context, request *v1.CreateReques
 	return &v1.CreateResponse{}, err
 }
 
-func (p *proxyContainerApi) Start(_ context.Context, _ *v1.StartRequest) (*v1.StartResponse, error) {
+func (p *proxyContainerAPI) Start(_ context.Context, _ *v1.StartRequest) (*v1.StartResponse, error) {
 	panic("not implemented") // TODO: Implement
 }
 
-func (p *proxyContainerApi) Stop(_ context.Context, _ *v1.StopRequest) (*v1.StopResponse, error) {
+func (p *proxyContainerAPI) Stop(_ context.Context, _ *v1.StopRequest) (*v1.StopResponse, error) {
 	panic("not implemented") // TODO: Implement
 }
 
-func (p *proxyContainerApi) Kill(_ context.Context, _ *v1.KillRequest) (*v1.KillResponse, error) {
+func (p *proxyContainerAPI) Kill(_ context.Context, _ *v1.KillRequest) (*v1.KillResponse, error) {
 	panic("not implemented") // TODO: Implement
 }
 
-func (p *proxyContainerApi) Delete(_ context.Context, _ *v1.DeleteRequest) (*v1.DeleteResponse, error) {
+func (p *proxyContainerAPI) Delete(_ context.Context, _ *v1.DeleteRequest) (*v1.DeleteResponse, error) {
 	panic("not implemented") // TODO: Implement
 }
 
-func (p *proxyContainerApi) Update(_ context.Context, _ *v1.UpdateRequest) (*v1.UpdateResponse, error) {
+func (p *proxyContainerAPI) Update(_ context.Context, _ *v1.UpdateRequest) (*v1.UpdateResponse, error) {
 	panic("not implemented") // TODO: Implement
 }
 
-func (p *proxyContainerApi) Exec(_ context.Context, _ *v1.ExecRequest) (*v1.ExecResponse, error) {
+func (p *proxyContainerAPI) Exec(_ context.Context, _ *v1.ExecRequest) (*v1.ExecResponse, error) {
 	panic("not implemented") // TODO: Implement
 }

+ 1 - 1
server/server.go

@@ -71,7 +71,7 @@ func unaryMeta(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
 		return nil, errors.New("missing metadata")
 	}
 
-	key := md[apicontext.KEY]
+	key := md[apicontext.Key]
 
 	if len(key) == 1 {
 		s, err := store.New()