Merge pull request #40 from docker/chore-cleanup

Add comments on exported items, remove example command
This commit is contained in:
Djordje Lukic 2020-05-05 12:06:55 +02:00 committed by GitHub
commit f32235b8ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 189 additions and 284 deletions

View File

@ -11,23 +11,17 @@ jobs:
name: Build name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Set up Go 1.13 - name: Set up Go 1.14
uses: actions/setup-go@v1 uses: actions/setup-go@v1
with: with:
go-version: 1.13 go-version: 1.14
id: go id: go
- name: Checkout code into the Go module directory - name: Checkout code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Install Protoc - name: Lint
uses: arduino/setup-protoc@master run: make lint
- name: Get dependencies
run: ./scripts/setup/install-go-gen
- name: Protos
run: make protos
- name: Build - name: Build
run: make cli run: make cli

35
.golangci.yml Normal file
View File

@ -0,0 +1,35 @@
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:
# golangci hides some golint warnings (the warning about exported things
# withtout documentation for example), this will make it show them anyway.
exclude-use-default: false

View File

@ -4,15 +4,20 @@ ARG GO_VERSION=1.14.2
FROM golang:${GO_VERSION} AS fs FROM golang:${GO_VERSION} AS fs
ARG TARGET_OS=unknown ARG TARGET_OS=unknown
ARG TARGET_ARCH=unknown ARG TARGET_ARCH=unknown
ARG PWD=$GOPATH/src/github.com/docker/api ARG PWD=/api
ENV GO111MODULE=on
RUN apt-get update && apt-get install --no-install-recommends -y \ RUN apt-get update && apt-get install --no-install-recommends -y \
make \ make \
git \ git \
protobuf-compiler \ protobuf-compiler \
libprotobuf-dev libprotobuf-dev
RUN go get github.com/golang/protobuf/protoc-gen-go && \
RUN go get github.com/golang/protobuf/protoc-gen-go@v1.4.1 && \
go get golang.org/x/tools/cmd/goimports && \ go get golang.org/x/tools/cmd/goimports && \
go get gotest.tools/gotestsum go get gotest.tools/gotestsum@v0.4.2 && \
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.26.0
WORKDIR ${PWD} WORKDIR ${PWD}
ADD go.* ${PWD} ADD go.* ${PWD}
RUN go mod download RUN go mod download
@ -32,13 +37,16 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
make -f builder.Makefile cross make -f builder.Makefile cross
FROM scratch AS protos FROM scratch AS protos
COPY --from=make-protos /go/src/github.com/docker/api . COPY --from=make-protos /api .
FROM scratch AS cli FROM scratch AS cli
COPY --from=make-cli /go/src/github.com/docker/api/bin/* . COPY --from=make-cli /api/bin/* .
FROM scratch AS cross FROM scratch AS cross
COPY --from=make-cross /go/src/github.com/docker/api/bin/* . COPY --from=make-cross /api/bin/* .
FROM make-protos as test FROM make-protos as test
RUN make -f builder.Makefile test RUN make -f builder.Makefile test
FROM fs AS lint
RUN make -f builder.Makefile lint

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)
@docker build . \
--target lint
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

@ -4,7 +4,17 @@
## Dev Setup ## Dev Setup
Make sure you have Docker installed and running. The recommended way is to use the main `Makefile` that runs everything inside a container.
If you don't have or want to use Docker for building you need to make sure you have all the needed tools installed locally:
* go 1.14
* `go get github.com/golang/protobuf/protoc-gen-go@v1.4.1`
* `go get golang.org/x/tools/cmd/goimports`
* `go get gotest.tools/gotestsum@v0.4.2`
* `go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.26.0`
And then you can call the same make targets but you need to pass it the `builder.Makefile` (`make -f builder.Makefile`).
## Building the project ## Building the project

View File

@ -32,7 +32,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, err := getContainerGroupsClient(aciContext.SubscriptionID)
if err != nil { if err != nil {
return c, fmt.Errorf("cannot get container group client: %v", err) return c, errors.Wrapf(err, "cannot get container group client")
} }
// Check if the container group already exists // Check if the container group already exists
@ -96,30 +96,11 @@ 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, err := getContainerClient(aciContext.SubscriptionID)
if err != nil {
return c, errors.Wrapf(err, "cannot get container client")
}
rows, cols := getTermSize() rows, cols := getTermSize()
containerExecRequest := containerinstance.ContainerExecRequest{ containerExecRequest := containerinstance.ContainerExecRequest{
Command: to.StringPtr(command), Command: to.StringPtr(command),
@ -224,7 +205,10 @@ func exec(ctx context.Context, address string, password string, reader io.Reader
} }
func getACIContainerLogs(ctx context.Context, aciContext store.AciContext, containerGroupName, containerName string) (string, error) { func getACIContainerLogs(ctx context.Context, aciContext store.AciContext, containerGroupName, containerName string) (string, error) {
containerClient := getContainerClient(aciContext.SubscriptionID) containerClient, err := getContainerClient(aciContext.SubscriptionID)
if err != nil {
return "", errors.Wrapf(err, "cannot get container client")
}
logs, err := containerClient.ListLogs(ctx, aciContext.ResourceGroup, containerGroupName, containerName, nil) logs, err := containerClient.ListLogs(ctx, aciContext.ResourceGroup, containerGroupName, containerName, nil)
if err != nil { if err != nil {
@ -234,15 +218,21 @@ func getACIContainerLogs(ctx context.Context, aciContext store.AciContext, conta
} }
func getContainerGroupsClient(subscriptionID string) (containerinstance.ContainerGroupsClient, error) { func getContainerGroupsClient(subscriptionID string) (containerinstance.ContainerGroupsClient, error) {
auth, _ := auth.NewAuthorizerFromCLI() auth, err := auth.NewAuthorizerFromCLI()
if err != nil {
return containerinstance.ContainerGroupsClient{}, err
}
containerGroupsClient := containerinstance.NewContainerGroupsClient(subscriptionID) containerGroupsClient := containerinstance.NewContainerGroupsClient(subscriptionID)
containerGroupsClient.Authorizer = auth containerGroupsClient.Authorizer = auth
return containerGroupsClient, nil return containerGroupsClient, nil
} }
func getContainerClient(subscriptionID string) containerinstance.ContainerClient { func getContainerClient(subscriptionID string) (containerinstance.ContainerClient, error) {
auth, _ := auth.NewAuthorizerFromCLI() auth, err := auth.NewAuthorizerFromCLI()
if err != nil {
return containerinstance.ContainerClient{}, err
}
containerClient := containerinstance.NewContainerClient(subscriptionID) containerClient := containerinstance.NewContainerClient(subscriptionID)
containerClient.Authorizer = auth containerClient.Authorizer = auth
return containerClient return containerClient, nil
} }

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

@ -57,6 +57,10 @@ cross:
test: test:
@gotestsum ./... @gotestsum ./...
lint:
golangci-lint run --timeout 10m0s ./...
FORCE: FORCE:
.PHONY: all protos cli cross test .PHONY: all protos cli cross test lint

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 errcheck
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 errcheck
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 (global) 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{
{ {

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/docker/api module github.com/docker/api
go 1.13 go 1.14
require ( require (
github.com/Azure/azure-sdk-for-go v42.0.0+incompatible github.com/Azure/azure-sdk-for-go v42.0.0+incompatible

View File

@ -1,7 +0,0 @@
#!/usr/bin/env bash
go get -u github.com/gogo/protobuf/proto
go get -u github.com/gogo/protobuf/jsonpb
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u github.com/stevvooe/protobuild
go get -u gotest.tools/gotestsum

View File

@ -1,61 +0,0 @@
#!/usr/bin/env bash
# Copyright The containerd Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Downloads and installs protobuf
#
set -eu -o pipefail
PROTOBUF_VERSION=3.11.4
GOARCH=$(go env GOARCH)
GOOS=$(go env GOOS)
PROTOBUF_DIR=$(mktemp -d)
case $GOARCH in
arm64)
wget -O $PROTOBUF_DIR/protobuf "https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-linux-aarch64.zip"
unzip $PROTOBUF_DIR/protobuf -d /usr/local
;;
amd64 | 386)
if [ "$GOOS" = windows ]; then
wget -O $PROTOBUF_DIR/protobuf "https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-win32.zip"
elif [ "$GOOS" = linux ]; then
wget -O $PROTOBUF_DIR/protobuf "https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-linux-x86_64.zip"
elif [ "$GOOS" = darwin ]; then
curl -Lo $PROTOBUF_DIR/protobuf "https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-osx-x86_64.zip"
fi
unzip $PROTOBUF_DIR/protobuf -x readme.txt -d /usr/local
;;
ppc64le)
wget -O $PROTOBUF_DIR/protobuf "https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-linux-ppcle_64.zip"
unzip $PROTOBUF_DIR/protobuf -d /usr/local
;;
*)
wget -O $PROTOBUF_DIR/protobuf "https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protobuf-cpp-$PROTOBUF_VERSION.zip"
unzip $PROTOBUF_DIR/protobuf -d /usr/src/protobuf
cd /usr/src/protobuf/protobuf-$PROTOBUF_VERSION
./autogen.sh
./configure --disable-shared
make
make check
make install
ldconfig
;;
esac
rm -rf $PROTOBUF_DIR

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()