diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8bc5bcf..e685dbe8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,12 +45,12 @@ jobs: - name: Test env: - BUILD_TAGS: example,local + BUILD_TAGS: example,local,ecs run: make -f builder.Makefile test - name: Build env: - BUILD_TAGS: example,local + BUILD_TAGS: example,local,ecs run: make -f builder.Makefile cli - name: E2E Test diff --git a/Makefile b/Makefile index 8cecf6f8..a475c96e 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ protos: ## Generate go code from .proto files cli: ## Compile the cli @docker build . --target cli \ --platform local \ - --build-arg BUILD_TAGS=example,local \ + --build-arg BUILD_TAGS=example,local,ecs \ --build-arg GIT_TAG=$(GIT_TAG) \ --output ./bin @@ -55,7 +55,7 @@ cross: ## Compile the CLI for linux, darwin and windows test: ## Run unit tests @docker build . \ - --build-arg BUILD_TAGS=example,local \ + --build-arg BUILD_TAGS=example,local,ecs \ --build-arg GIT_TAG=$(GIT_TAG) \ --target test diff --git a/amazon/backend.go b/amazon/backend.go index ba7a2277..b7234144 100644 --- a/amazon/backend.go +++ b/amazon/backend.go @@ -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 import ( @@ -9,9 +24,12 @@ import ( apicontext "github.com/docker/api/context" "github.com/docker/api/context/cloud" "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 type ContextParams struct { Description string @@ -23,61 +41,61 @@ type ContextParams struct { } func init() { - backend.Register("aws", "aws", service, getCloudService) + backend.Register(backendType, backendType, service, getCloudService) } func service(ctx context.Context) (backend.Service, error) { contextStore := store.ContextStore(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 getAwsAPIService(awsContext) + return getEcsAPIService(ecsContext) } -func getAwsAPIService(awsCtx store.AwsContext) (*awsAPIService, error) { - backend, err := aws.NewBackend(awsCtx.Profile, awsCtx.Region) +func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) { + backend, err := ecs.NewBackend(ecsCtx.Profile, ecsCtx.Region) if err != nil { return nil, err } - return &awsAPIService{ - ctx: awsCtx, + return &ecsAPIService{ + ctx: ecsCtx, composeBackend: backend, }, nil } -type awsAPIService struct { - ctx store.AwsContext - composeBackend *aws.Backend +type ecsAPIService struct { + ctx store.EcsContext + composeBackend *ecs.Backend } -func (a *awsAPIService) ContainerService() containers.Service { +func (a *ecsAPIService) ContainerService() containers.Service { return nil } -func (a *awsAPIService) ComposeService() compose.Service { +func (a *ecsAPIService) ComposeService() compose.Service { return a.composeBackend } 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 { - return nil +func (a ecsCloudService) Login(ctx context.Context, params interface{}) error { + return errdefs.ErrNotImplemented } -func (a awsCloudService) Logout(ctx context.Context) error { - return nil +func (a ecsCloudService) Logout(ctx context.Context) error { + 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() createOpts := params.(ContextParams) return contextHelper.createContextData(ctx, createOpts) diff --git a/amazon/context.go b/amazon/context.go index 8df40cd5..0432eed9 100644 --- a/amazon/context.go +++ b/amazon/context.go @@ -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 import ( @@ -30,31 +45,31 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex accessKey := opts.AwsID secretKey := opts.AwsSecret - awsCtx := store.AwsContext{ + ecsCtx := store.EcsContext{ Profile: opts.Profile, Region: opts.Region, } - if h.missingRequiredFlags(awsCtx) { + if h.missingRequiredFlags(ecsCtx) { profilesList, err := h.getProfiles() if err != nil { return nil, "", err } // get profile - _, ok := profilesList[awsCtx.Profile] + _, ok := profilesList[ecsCtx.Profile] if !ok { profile, err := h.chooseProfile(profilesList) if err != nil { return nil, "", err } - awsCtx.Profile = profile + ecsCtx.Profile = profile } // set region - region, err := h.chooseRegion(awsCtx.Region, profilesList[awsCtx.Profile]) + region, err := h.chooseRegion(ecsCtx.Region, profilesList[ecsCtx.Profile]) if err != nil { return nil, "", err } - awsCtx.Region = region + ecsCtx.Region = region accessKey, secretKey, err = h.askCredentials() if err != nil { @@ -62,20 +77,20 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex } } 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 } } - description := awsCtx.Region + description := ecsCtx.Region if opts.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 == "" { return true } @@ -86,8 +101,7 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri p := credentials.SharedCredentialsProvider{Profile: profile} _, err := p.Retrieve() if err == nil { - fmt.Println("credentials already exists!") - return nil + return fmt.Errorf("credentials already exists!") } 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 { return nil, err } - if err != nil { - return nil, err - } for _, section := range credIni.Sections() { if strings.HasPrefix(section.Name(), "profile") { 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) if err != nil { if err == terminal.InterruptErr { - os.Exit(0) + os.Exit(-1) } return "", err } profile := profiles[selected] - if profiles[selected] == "new profile" { - return h.user.Input("profile name", "") } return profile, nil diff --git a/amazon/doc.go b/amazon/doc.go new file mode 100644 index 00000000..1f74174f --- /dev/null +++ b/amazon/doc.go @@ -0,0 +1 @@ +package amazon diff --git a/cli/cmd/context/create.go b/cli/cmd/context/create.go index 933897ad..42520bc0 100644 --- a/cli/cmd/context/create.go +++ b/cli/cmd/context/create.go @@ -18,6 +18,8 @@ package context import ( "context" + "fmt" + "strings" "github.com/spf13/cobra" @@ -29,15 +31,18 @@ type descriptionCreateOpts struct { description string } +var extraCommands []func() *cobra.Command +var extraHelp []string + 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: $ docker context create CONTEXT [flags] -Create Azure Container Instances context: -$ docker context create aci CONTEXT [flags] -(see docker context create aci --help) +%s Docker endpoint config: @@ -59,7 +64,7 @@ namespace-override Overrides the namespace set in the kubernetes config file 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{ Use: "create CONTEXT", @@ -72,10 +77,12 @@ $ docker context create my-context --description "some description" --docker "ho cmd.AddCommand( createAciCommand(), - createAwsCommand(), createLocalCommand(), createExampleCommand(), ) + for _, command := range extraCommands { + cmd.AddCommand(command()) + } flags := cmd.Flags() flags.String("description", "", "Description of the context") diff --git a/cli/cmd/context/createaci.go b/cli/cmd/context/createaci.go index 61e68cd8..9a889e83 100644 --- a/cli/cmd/context/createaci.go +++ b/cli/cmd/context/createaci.go @@ -28,6 +28,15 @@ import ( "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 { var opts azure.ContextParams cmd := &cobra.Command{ diff --git a/cli/cmd/context/createaws.go b/cli/cmd/context/createecs.go similarity index 66% rename from cli/cmd/context/createaws.go rename to cli/cmd/context/createecs.go index 47a8a3a4..f0a154ff 100644 --- a/cli/cmd/context/createaws.go +++ b/cli/cmd/context/createecs.go @@ -1,3 +1,5 @@ +// +build ecs + /* Copyright 2020 Docker, Inc. @@ -28,39 +30,48 @@ import ( "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 cmd := &cobra.Command{ - Use: "aws CONTEXT [flags]", + Use: "ecs CONTEXT [flags]", Short: "Create a context for Amazon ECS", Args: cobra.ExactArgs(1), 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) - cmd.Flags().StringVar(&opts.Profile, "profile", "", "AWS Profile") - cmd.Flags().StringVar(&opts.Region, "region", "", "AWS region") + cmd.Flags().StringVar(&opts.Profile, "profile", "", "Profile") + cmd.Flags().StringVar(&opts.Region, "region", "", "Region") cmd.Flags().StringVar(&opts.AwsID, "key-id", "", "AWS Access Key ID") cmd.Flags().StringVar(&opts.AwsSecret, "secret-key", "", "AWS Secret Access Key") 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) { return errors.Wrapf(errdefs.ErrAlreadyExists, "context %s", contextName) } - contextData, description, err := getAwsContextData(ctx, opts) + contextData, description, err := getEcsContextData(ctx, opts) if err != nil { 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) { - cs, err := client.GetCloudService(ctx, store.AwsContextType) +func getEcsContextData(ctx context.Context, opts amazon.ContextParams) (interface{}, string, error) { + cs, err := client.GetCloudService(ctx, store.EcsContextType) if err != nil { return nil, "", errors.Wrap(err, "cannot connect to AWS backend") } diff --git a/context/store/contextmetadata.go b/context/store/contextmetadata.go index 16fc3c13..54c9baf3 100644 --- a/context/store/contextmetadata.go +++ b/context/store/contextmetadata.go @@ -49,8 +49,8 @@ type AciContext struct { ResourceGroup string `json:",omitempty"` } -// AwsContext is the context for the AWS backend -type AwsContext struct { +// EcsContext is the context for the AWS backend +type EcsContext struct { Profile string `json:",omitempty"` Region string `json:",omitempty"` } diff --git a/context/store/store.go b/context/store/store.go index 17fc4946..7ffad198 100644 --- a/context/store/store.go +++ b/context/store/store.go @@ -36,8 +36,8 @@ const ( DefaultContextName = "default" // DefaultContextType is the type for all moby contexts (not associated with cli backend) DefaultContextType = "moby" - // AwsContextType is the type for ecs contexts (currently a CLI plugin, not associated with cli backend) - AwsContextType = "aws" + // EcsContextType is the type for ecs contexts (currently a CLI plugin, not associated with cli backend) + EcsContextType = "ecs" // AciContextType is the endpoint key in the context endpoints for an ACI // backend AciContextType = "aci" @@ -339,8 +339,8 @@ func getters() map[string]func() interface{} { AciContextType: func() interface{} { return &AciContext{} }, - AwsContextType: func() interface{} { - return &AwsContext{} + EcsContextType: func() interface{} { + return &EcsContext{} }, LocalContextType: func() interface{} { return &LocalContext{} diff --git a/go.mod b/go.mod index a8d97bbd..7aa9448b 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,8 @@ module github.com/docker/api 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 require ( @@ -24,7 +25,7 @@ require ( github.com/containerd/containerd v1.3.5 // indirect 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/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-units v0.4.0 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect diff --git a/go.sum b/go.sum index 6f2cda7d..d4abd721 100644 --- a/go.sum +++ b/go.sum @@ -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/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.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/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=