Add comments on exported items, remove example command

Also add `make lint` to run the linter
This commit is contained in:
Djordje Lukic 2020-05-04 23:00:21 +02:00 committed by Djordje Lukic
parent 29737c2a23
commit 24c035e822
26 changed files with 136 additions and 196 deletions

33
.golangci.yml Normal file
View File

@ -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

View File

@ -53,8 +53,14 @@ test: ## Run unit tests
cache-clear: # Clear the builder cache cache-clear: # Clear the builder cache
@docker builder prune --force --filter type=exec.cachemount --filter=unused-for=24h @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 help: ## Show help
@echo Please specify a build target. The choices are: @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}' @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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,17 +14,14 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const ( var supportedFilenames = []string{
SecretInlineMark = "inline:"
)
var SupportedFilenames = []string{
"compose.yml", "compose.yml",
"compose.yaml", "compose.yaml",
"docker-compose.yml", "docker-compose.yml",
"docker-compose.yaml", "docker-compose.yaml",
} }
// ProjectOptions configures a compose project
type ProjectOptions struct { type ProjectOptions struct {
Name string Name string
WorkDir string WorkDir string
@ -32,6 +29,7 @@ type ProjectOptions struct {
Environment []string Environment []string
} }
// Project represents a compose project with a name
type Project struct { type Project struct {
types.Config types.Config
projectDir string projectDir string
@ -100,7 +98,7 @@ func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
for { for {
var candidates []string var candidates []string
for _, n := range SupportedFilenames { for _, n := range supportedFilenames {
f := filepath.Join(pwd, n) f := filepath.Join(pwd, n)
if _, err := os.Stat(f); err == nil { if _, err := os.Stat(f); err == nil {
candidates = append(candidates, f) candidates = append(candidates, f)
@ -116,7 +114,7 @@ func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
} }
parent := filepath.Dir(pwd) parent := filepath.Dir(pwd)
if parent == 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 pwd = parent
} }
@ -129,12 +127,11 @@ func parseConfigs(configPaths []string) ([]types.ConfigFile, error) {
var err error var err error
if f == "-" { if f == "-" {
return []types.ConfigFile{}, errors.New("reading compose file from stdin is not supported") return []types.ConfigFile{}, errors.New("reading compose file from stdin is not supported")
} else { }
if _, err := os.Stat(f); err != nil { if _, err := os.Stat(f); err != nil {
return nil, err return nil, err
} }
b, err = ioutil.ReadFile(f) b, err = ioutil.ReadFile(f)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -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"

View File

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

View File

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

View File

@ -6,10 +6,13 @@ import (
"golang.org/x/net/context" "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{} type currentContextKey struct{}
// WithCurrentContext sets the name of the current docker context
func WithCurrentContext(ctx gocontext.Context, contextName string) context.Context { func WithCurrentContext(ctx gocontext.Context, contextName string) context.Context {
return context.WithValue(ctx, currentContextKey{}, contextName) return context.WithValue(ctx, currentContextKey{}, contextName)
} }

View File

@ -41,12 +41,14 @@ const (
configFileDir = ".docker" configFileDir = ".docker"
) )
type ContextFlags struct { // Flags are the global cli flags
type Flags struct {
Config string Config string
Context 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.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") flags.StringVarP(&c.Context, "context", "c", os.Getenv("DOCKER_CONTEXT"), "context")
} }

View File

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

View File

@ -55,7 +55,8 @@ func (suite *StoreTestSuite) BeforeTest(suiteName, testName string) {
} }
func (suite *StoreTestSuite) AfterTest(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() { func (suite *StoreTestSuite) TestCreate() {

View File

@ -13,14 +13,10 @@ type containerService struct{}
func init() { func init() {
backend.Register("example", "example", func(ctx context.Context) (interface{}, error) { 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) { func (cs *containerService) List(ctx context.Context) ([]containers.Container, error) {
return []containers.Container{ return []containers.Container{
{ {

View File

@ -10,22 +10,25 @@ import (
type clientKey struct{} type clientKey struct{}
// WithClient adds the client to the context
func WithClient(ctx context.Context, c *client.Client) (context.Context, error) { func WithClient(ctx context.Context, c *client.Client) (context.Context, error) {
return context.WithValue(ctx, clientKey{}, c), nil return context.WithValue(ctx, clientKey{}, c), nil
} }
// Client returns the client from the context
func Client(ctx context.Context) *client.Client { func Client(ctx context.Context) *client.Client {
c, _ := ctx.Value(clientKey{}).(*client.Client) c, _ := ctx.Value(clientKey{}).(*client.Client)
return c return c
} }
func NewContainerApi() v1.ContainersServer { // NewContainerAPI creates a proxy container server
return &proxyContainerApi{} 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) client := Client(ctx)
c, err := client.ContainerService().List(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 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) client := Client(ctx)
err := client.ContainerService().Run(ctx, containers.ContainerConfig{ 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 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 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 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 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 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 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 panic("not implemented") // TODO: Implement
} }

View File

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