set build tag for the ecs context and backend

Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
aiordache 2020-07-20 10:48:05 +02:00
parent cfa7406d73
commit 2471e51b39
12 changed files with 125 additions and 67 deletions

View File

@ -45,12 +45,12 @@ jobs:
- name: Test - name: Test
env: env:
BUILD_TAGS: example,local BUILD_TAGS: example,local,ecs
run: make -f builder.Makefile test run: make -f builder.Makefile test
- name: Build - name: Build
env: env:
BUILD_TAGS: example,local BUILD_TAGS: example,local,ecs
run: make -f builder.Makefile cli run: make -f builder.Makefile cli
- name: E2E Test - name: E2E Test

View File

@ -34,7 +34,7 @@ protos: ## Generate go code from .proto files
cli: ## Compile the cli cli: ## Compile the cli
@docker build . --target cli \ @docker build . --target cli \
--platform local \ --platform local \
--build-arg BUILD_TAGS=example,local \ --build-arg BUILD_TAGS=example,local,ecs \
--build-arg GIT_TAG=$(GIT_TAG) \ --build-arg GIT_TAG=$(GIT_TAG) \
--output ./bin --output ./bin
@ -55,7 +55,7 @@ cross: ## Compile the CLI for linux, darwin and windows
test: ## Run unit tests test: ## Run unit tests
@docker build . \ @docker build . \
--build-arg BUILD_TAGS=example,local \ --build-arg BUILD_TAGS=example,local,ecs \
--build-arg GIT_TAG=$(GIT_TAG) \ --build-arg GIT_TAG=$(GIT_TAG) \
--target test --target test

View File

@ -1,3 +1,18 @@
// +build ecs
/*
Copyright 2020 Docker, Inc.
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.
*/
package amazon package amazon
import ( import (
@ -9,9 +24,12 @@ import (
apicontext "github.com/docker/api/context" apicontext "github.com/docker/api/context"
"github.com/docker/api/context/cloud" "github.com/docker/api/context/cloud"
"github.com/docker/api/context/store" "github.com/docker/api/context/store"
aws "github.com/docker/ecs-plugin/pkg/amazon/backend" "github.com/docker/api/errdefs"
ecs "github.com/docker/ecs-plugin/pkg/amazon/backend"
) )
const backendType = store.EcsContextType
// ContextParams options for creating AWS context // ContextParams options for creating AWS context
type ContextParams struct { type ContextParams struct {
Description string Description string
@ -23,61 +41,61 @@ type ContextParams struct {
} }
func init() { func init() {
backend.Register("aws", "aws", service, getCloudService) backend.Register(backendType, backendType, service, getCloudService)
} }
func service(ctx context.Context) (backend.Service, error) { func service(ctx context.Context) (backend.Service, error) {
contextStore := store.ContextStore(ctx) contextStore := store.ContextStore(ctx)
currentContext := apicontext.CurrentContext(ctx) currentContext := apicontext.CurrentContext(ctx)
var awsContext store.AwsContext var ecsContext store.EcsContext
if err := contextStore.GetEndpoint(currentContext, &awsContext); err != nil { if err := contextStore.GetEndpoint(currentContext, &ecsContext); err != nil {
return nil, err return nil, err
} }
return getAwsAPIService(awsContext) return getEcsAPIService(ecsContext)
} }
func getAwsAPIService(awsCtx store.AwsContext) (*awsAPIService, error) { func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) {
backend, err := aws.NewBackend(awsCtx.Profile, awsCtx.Region) backend, err := ecs.NewBackend(ecsCtx.Profile, ecsCtx.Region)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &awsAPIService{ return &ecsAPIService{
ctx: awsCtx, ctx: ecsCtx,
composeBackend: backend, composeBackend: backend,
}, nil }, nil
} }
type awsAPIService struct { type ecsAPIService struct {
ctx store.AwsContext ctx store.EcsContext
composeBackend *aws.Backend composeBackend *ecs.Backend
} }
func (a *awsAPIService) ContainerService() containers.Service { func (a *ecsAPIService) ContainerService() containers.Service {
return nil return nil
} }
func (a *awsAPIService) ComposeService() compose.Service { func (a *ecsAPIService) ComposeService() compose.Service {
return a.composeBackend return a.composeBackend
} }
func getCloudService() (cloud.Service, error) { func getCloudService() (cloud.Service, error) {
return awsCloudService{}, nil return ecsCloudService{}, nil
} }
type awsCloudService struct { type ecsCloudService struct {
} }
func (a awsCloudService) Login(ctx context.Context, params interface{}) error { func (a ecsCloudService) Login(ctx context.Context, params interface{}) error {
return nil return errdefs.ErrNotImplemented
} }
func (a awsCloudService) Logout(ctx context.Context) error { func (a ecsCloudService) Logout(ctx context.Context) error {
return nil return errdefs.ErrNotImplemented
} }
func (a awsCloudService) CreateContextData(ctx context.Context, params interface{}) (interface{}, string, error) { func (a ecsCloudService) CreateContextData(ctx context.Context, params interface{}) (interface{}, string, error) {
contextHelper := newContextCreateHelper() contextHelper := newContextCreateHelper()
createOpts := params.(ContextParams) createOpts := params.(ContextParams)
return contextHelper.createContextData(ctx, createOpts) return contextHelper.createContextData(ctx, createOpts)

View File

@ -1,3 +1,18 @@
// +build ecs
/*
Copyright 2020 Docker, Inc.
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.
*/
package amazon package amazon
import ( import (
@ -30,31 +45,31 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
accessKey := opts.AwsID accessKey := opts.AwsID
secretKey := opts.AwsSecret secretKey := opts.AwsSecret
awsCtx := store.AwsContext{ ecsCtx := store.EcsContext{
Profile: opts.Profile, Profile: opts.Profile,
Region: opts.Region, Region: opts.Region,
} }
if h.missingRequiredFlags(awsCtx) { if h.missingRequiredFlags(ecsCtx) {
profilesList, err := h.getProfiles() profilesList, err := h.getProfiles()
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
// get profile // get profile
_, ok := profilesList[awsCtx.Profile] _, ok := profilesList[ecsCtx.Profile]
if !ok { if !ok {
profile, err := h.chooseProfile(profilesList) profile, err := h.chooseProfile(profilesList)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
awsCtx.Profile = profile ecsCtx.Profile = profile
} }
// set region // set region
region, err := h.chooseRegion(awsCtx.Region, profilesList[awsCtx.Profile]) region, err := h.chooseRegion(ecsCtx.Region, profilesList[ecsCtx.Profile])
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
awsCtx.Region = region ecsCtx.Region = region
accessKey, secretKey, err = h.askCredentials() accessKey, secretKey, err = h.askCredentials()
if err != nil { if err != nil {
@ -62,20 +77,20 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
} }
} }
if accessKey != "" && secretKey != "" { if accessKey != "" && secretKey != "" {
if err := h.saveCredentials(awsCtx.Profile, accessKey, secretKey); err != nil { if err := h.saveCredentials(ecsCtx.Profile, accessKey, secretKey); err != nil {
return nil, "", err return nil, "", err
} }
} }
description := awsCtx.Region description := ecsCtx.Region
if opts.Description != "" { if opts.Description != "" {
description = fmt.Sprintf("%s (%s)", opts.Description, description) description = fmt.Sprintf("%s (%s)", opts.Description, description)
} }
return awsCtx, description, nil return ecsCtx, description, nil
} }
func (h contextCreateAWSHelper) missingRequiredFlags(ctx store.AwsContext) bool { func (h contextCreateAWSHelper) missingRequiredFlags(ctx store.EcsContext) bool {
if ctx.Profile == "" || ctx.Region == "" { if ctx.Profile == "" || ctx.Region == "" {
return true return true
} }
@ -86,8 +101,7 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri
p := credentials.SharedCredentialsProvider{Profile: profile} p := credentials.SharedCredentialsProvider{Profile: profile}
_, err := p.Retrieve() _, err := p.Retrieve()
if err == nil { if err == nil {
fmt.Println("credentials already exists!") return fmt.Errorf("credentials already exists!")
return nil
} }
if err.(awserr.Error).Code() == "SharedCredsLoad" && err.(awserr.Error).Message() == "failed to load shared credentials file" { if err.(awserr.Error).Code() == "SharedCredsLoad" && err.(awserr.Error).Message() == "failed to load shared credentials file" {
@ -121,9 +135,6 @@ func (h contextCreateAWSHelper) getProfiles() (map[string]ini.Section, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err != nil {
return nil, err
}
for _, section := range credIni.Sections() { for _, section := range credIni.Sections() {
if strings.HasPrefix(section.Name(), "profile") { if strings.HasPrefix(section.Name(), "profile") {
profiles[section.Name()[len("profile "):]] = *section profiles[section.Name()[len("profile "):]] = *section
@ -142,14 +153,12 @@ func (h contextCreateAWSHelper) chooseProfile(section map[string]ini.Section) (s
selected, err := h.user.Select("Select AWS Profile", profiles) selected, err := h.user.Select("Select AWS Profile", profiles)
if err != nil { if err != nil {
if err == terminal.InterruptErr { if err == terminal.InterruptErr {
os.Exit(0) os.Exit(-1)
} }
return "", err return "", err
} }
profile := profiles[selected] profile := profiles[selected]
if profiles[selected] == "new profile" { if profiles[selected] == "new profile" {
return h.user.Input("profile name", "") return h.user.Input("profile name", "")
} }
return profile, nil return profile, nil

1
amazon/doc.go Normal file
View File

@ -0,0 +1 @@
package amazon

View File

@ -18,6 +18,8 @@ package context
import ( import (
"context" "context"
"fmt"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -29,15 +31,18 @@ type descriptionCreateOpts struct {
description string description string
} }
var extraCommands []func() *cobra.Command
var extraHelp []string
func createCommand() *cobra.Command { func createCommand() *cobra.Command {
const longHelp = `Create a new context help := strings.Join(extraHelp, "\n")
longHelp := fmt.Sprintf(`Create a new context
Create docker engine context: Create docker engine context:
$ docker context create CONTEXT [flags] $ docker context create CONTEXT [flags]
Create Azure Container Instances context: %s
$ docker context create aci CONTEXT [flags]
(see docker context create aci --help)
Docker endpoint config: Docker endpoint config:
@ -59,7 +64,7 @@ namespace-override Overrides the namespace set in the kubernetes config file
Example: Example:
$ docker context create my-context --description "some description" --docker "host=tcp://myserver:2376,ca=~/ca-file,cert=~/cert-file,key=~/key-file"` $ docker context create my-context --description "some description" --docker "host=tcp://myserver:2376,ca=~/ca-file,cert=~/cert-file,key=~/key-file"`, help)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create CONTEXT", Use: "create CONTEXT",
@ -72,10 +77,12 @@ $ docker context create my-context --description "some description" --docker "ho
cmd.AddCommand( cmd.AddCommand(
createAciCommand(), createAciCommand(),
createAwsCommand(),
createLocalCommand(), createLocalCommand(),
createExampleCommand(), createExampleCommand(),
) )
for _, command := range extraCommands {
cmd.AddCommand(command())
}
flags := cmd.Flags() flags := cmd.Flags()
flags.String("description", "", "Description of the context") flags.String("description", "", "Description of the context")

View File

@ -28,6 +28,15 @@ import (
"github.com/docker/api/errdefs" "github.com/docker/api/errdefs"
) )
func init() {
extraCommands = append(extraCommands, createAciCommand)
extraHelp = append(extraHelp, `
Create Azure Container Instances context:
$ docker context create aci CONTEXT [flags]
(see docker context create aci --help)
`)
}
func createAciCommand() *cobra.Command { func createAciCommand() *cobra.Command {
var opts azure.ContextParams var opts azure.ContextParams
cmd := &cobra.Command{ cmd := &cobra.Command{

View File

@ -1,3 +1,5 @@
// +build ecs
/* /*
Copyright 2020 Docker, Inc. Copyright 2020 Docker, Inc.
@ -28,39 +30,48 @@ import (
"github.com/docker/api/errdefs" "github.com/docker/api/errdefs"
) )
func createAwsCommand() *cobra.Command { func init() {
extraCommands = append(extraCommands, createEcsCommand)
extraHelp = append(extraHelp, `
Create Amazon ECS context:
$ docker context create ecs CONTEXT [flags]
(see docker context create ecs --help)
`)
}
func createEcsCommand() *cobra.Command {
var opts amazon.ContextParams var opts amazon.ContextParams
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "aws CONTEXT [flags]", Use: "ecs CONTEXT [flags]",
Short: "Create a context for Amazon ECS", Short: "Create a context for Amazon ECS",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCreateAws(cmd.Context(), args[0], opts) return runCreateEcs(cmd.Context(), args[0], opts)
}, },
} }
addDescriptionFlag(cmd, &opts.Description) addDescriptionFlag(cmd, &opts.Description)
cmd.Flags().StringVar(&opts.Profile, "profile", "", "AWS Profile") cmd.Flags().StringVar(&opts.Profile, "profile", "", "Profile")
cmd.Flags().StringVar(&opts.Region, "region", "", "AWS region") cmd.Flags().StringVar(&opts.Region, "region", "", "Region")
cmd.Flags().StringVar(&opts.AwsID, "key-id", "", "AWS Access Key ID") cmd.Flags().StringVar(&opts.AwsID, "key-id", "", "AWS Access Key ID")
cmd.Flags().StringVar(&opts.AwsSecret, "secret-key", "", "AWS Secret Access Key") cmd.Flags().StringVar(&opts.AwsSecret, "secret-key", "", "AWS Secret Access Key")
return cmd return cmd
} }
func runCreateAws(ctx context.Context, contextName string, opts amazon.ContextParams) error { func runCreateEcs(ctx context.Context, contextName string, opts amazon.ContextParams) error {
if contextExists(ctx, contextName) { if contextExists(ctx, contextName) {
return errors.Wrapf(errdefs.ErrAlreadyExists, "context %s", contextName) return errors.Wrapf(errdefs.ErrAlreadyExists, "context %s", contextName)
} }
contextData, description, err := getAwsContextData(ctx, opts) contextData, description, err := getEcsContextData(ctx, opts)
if err != nil { if err != nil {
return err return err
} }
return createDockerContext(ctx, contextName, store.AwsContextType, description, contextData) return createDockerContext(ctx, contextName, store.EcsContextType, description, contextData)
} }
func getAwsContextData(ctx context.Context, opts amazon.ContextParams) (interface{}, string, error) { func getEcsContextData(ctx context.Context, opts amazon.ContextParams) (interface{}, string, error) {
cs, err := client.GetCloudService(ctx, store.AwsContextType) cs, err := client.GetCloudService(ctx, store.EcsContextType)
if err != nil { if err != nil {
return nil, "", errors.Wrap(err, "cannot connect to AWS backend") return nil, "", errors.Wrap(err, "cannot connect to AWS backend")
} }

View File

@ -49,8 +49,8 @@ type AciContext struct {
ResourceGroup string `json:",omitempty"` ResourceGroup string `json:",omitempty"`
} }
// AwsContext is the context for the AWS backend // EcsContext is the context for the AWS backend
type AwsContext struct { type EcsContext struct {
Profile string `json:",omitempty"` Profile string `json:",omitempty"`
Region string `json:",omitempty"` Region string `json:",omitempty"`
} }

View File

@ -36,8 +36,8 @@ const (
DefaultContextName = "default" DefaultContextName = "default"
// DefaultContextType is the type for all moby contexts (not associated with cli backend) // DefaultContextType is the type for all moby contexts (not associated with cli backend)
DefaultContextType = "moby" DefaultContextType = "moby"
// AwsContextType is the type for ecs contexts (currently a CLI plugin, not associated with cli backend) // EcsContextType is the type for ecs contexts (currently a CLI plugin, not associated with cli backend)
AwsContextType = "aws" EcsContextType = "ecs"
// AciContextType is the endpoint key in the context endpoints for an ACI // AciContextType is the endpoint key in the context endpoints for an ACI
// backend // backend
AciContextType = "aci" AciContextType = "aci"
@ -339,8 +339,8 @@ func getters() map[string]func() interface{} {
AciContextType: func() interface{} { AciContextType: func() interface{} {
return &AciContext{} return &AciContext{}
}, },
AwsContextType: func() interface{} { EcsContextType: func() interface{} {
return &AwsContext{} return &EcsContext{}
}, },
LocalContextType: func() interface{} { LocalContextType: func() interface{} {
return &LocalContext{} return &LocalContext{}

5
go.mod
View File

@ -1,7 +1,8 @@
module github.com/docker/api module github.com/docker/api
go 1.14 go 1.14
// the distribution version from ecs plugin is quite old and it breaks containerd
// we need to create a new release tag for docker/distribution
replace github.com/docker/distribution => github.com/docker/distribution v0.0.0-20200708230824-53e18a9d9bfe replace github.com/docker/distribution => github.com/docker/distribution v0.0.0-20200708230824-53e18a9d9bfe
require ( require (
@ -24,7 +25,7 @@ require (
github.com/containerd/containerd v1.3.5 // indirect github.com/containerd/containerd v1.3.5 // indirect
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8 github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
github.com/docker/ecs-plugin v1.0.0-beta.2 github.com/docker/ecs-plugin v1.0.0-beta.4
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect

2
go.sum
View File

@ -172,6 +172,8 @@ github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGl
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/ecs-plugin v1.0.0-beta.2 h1:Z9Krz+7zKXusWzENXuskQg7n/3/ViC/cfUl4XYiJWUI= github.com/docker/ecs-plugin v1.0.0-beta.2 h1:Z9Krz+7zKXusWzENXuskQg7n/3/ViC/cfUl4XYiJWUI=
github.com/docker/ecs-plugin v1.0.0-beta.2/go.mod h1:1YaNZwrNr0dFjTP3v7zwepluaZgVNV94s0M6fL+i/iA= github.com/docker/ecs-plugin v1.0.0-beta.2/go.mod h1:1YaNZwrNr0dFjTP3v7zwepluaZgVNV94s0M6fL+i/iA=
github.com/docker/ecs-plugin v1.0.0-beta.4 h1:hZKojW0tqsdhJjfMKPw6piMw/GJgfX6CVXd1YUuXLg4=
github.com/docker/ecs-plugin v1.0.0-beta.4/go.mod h1:1YaNZwrNr0dFjTP3v7zwepluaZgVNV94s0M6fL+i/iA=
github.com/docker/go v1.5.1-1 h1:hr4w35acWBPhGBXlzPoHpmZ/ygPjnmFVxGxxGnMyP7k= github.com/docker/go v1.5.1-1 h1:hr4w35acWBPhGBXlzPoHpmZ/ygPjnmFVxGxxGnMyP7k=
github.com/docker/go v1.5.1-1/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go v1.5.1-1/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=