Merge pull request #846 from docker/ecs_context

This commit is contained in:
Nicolas De loof 2020-11-03 11:38:06 +01:00 committed by GitHub
commit f66123b34a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 595 additions and 127 deletions

View File

@ -18,6 +18,7 @@ package context
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -45,6 +46,10 @@ func createEcsCommand() *cobra.Command {
Short: "Create a context for Amazon ECS",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.Name = args[0]
if opts.CredsFromEnv && opts.Profile != "" {
return fmt.Errorf("--profile and --from-env flags cannot be set at the same time")
}
if localSimulation {
return runCreateLocalSimulation(cmd.Context(), args[0], opts)
}
@ -54,8 +59,8 @@ func createEcsCommand() *cobra.Command {
addDescriptionFlag(cmd, &opts.Description)
cmd.Flags().BoolVar(&localSimulation, "local-simulation", false, "Create context for ECS local simulation endpoints")
cmd.Flags().StringVar(&opts.Profile, "profile", "", "Profile")
cmd.Flags().StringVar(&opts.Region, "region", "", "Region")
cmd.Flags().StringVar(&opts.Profile, "profile", "", "Use an existing AWS profile")
cmd.Flags().BoolVar(&opts.CredsFromEnv, "from-env", false, "Use AWS environment variables for profile, or credentials and region")
return cmd
}

View File

@ -51,8 +51,8 @@ type AciContext struct {
// EcsContext is the context for the AWS backend
type EcsContext struct {
Profile string `json:",omitempty"`
Region string `json:",omitempty"`
CredentialsFromEnv bool `json:",omitempty"`
Profile string `json:",omitempty"`
}
// AwsContext is the context for the ecs plugin

View File

@ -18,6 +18,7 @@ package ecs
import (
"context"
"fmt"
"github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/containers"
@ -38,9 +39,23 @@ const backendType = store.EcsContextType
// ContextParams options for creating AWS context
type ContextParams struct {
Description string
Region string
Profile string
Name string
Description string
AccessKey string
SecretKey string
Profile string
Region string
CredsFromEnv bool
}
func (c ContextParams) haveRequiredEnvVars() bool {
if c.Profile != "" {
return true
}
if c.AccessKey != "" && c.SecretKey != "" {
return true
}
return false
}
func init() {
@ -60,11 +75,31 @@ func service(ctx context.Context) (backend.Service, error) {
}
func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) {
region := ""
profile := ecsCtx.Profile
if ecsCtx.CredentialsFromEnv {
env := getEnvVars()
if !env.haveRequiredEnvVars() {
return nil, fmt.Errorf("context requires credentials to be passed as environment variables")
}
profile = env.Profile
region = env.Region
}
if region == "" {
r, err := getRegion(profile)
if err != nil {
return nil, err
}
region = r
}
sess, err := session.NewSessionWithOptions(session.Options{
Profile: ecsCtx.Profile,
Profile: profile,
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{
Region: aws.String(ecsCtx.Region),
Region: aws.String(region),
},
})
if err != nil {
@ -74,7 +109,7 @@ func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) {
sdk := newSDK(sess)
return &ecsAPIService{
ctx: ecsCtx,
Region: ecsCtx.Region,
Region: region,
aws: sdk,
}, nil
}

View File

@ -20,100 +20,186 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/pkg/errors"
"gopkg.in/ini.v1"
"github.com/docker/compose-cli/context/store"
"github.com/docker/compose-cli/errdefs"
"github.com/docker/compose-cli/prompt"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/pkg/errors"
"gopkg.in/ini.v1"
)
func getEnvVars() ContextParams {
c := ContextParams{
Profile: os.Getenv("AWS_PROFILE"),
Region: os.Getenv("AWS_REGION"),
}
if c.Region == "" {
defaultRegion := os.Getenv("AWS_DEFAULT_REGION")
if defaultRegion == "" {
defaultRegion = "us-east-1"
}
c.Region = defaultRegion
}
p := credentials.EnvProvider{}
creds, err := p.Retrieve()
if err != nil {
return c
}
c.AccessKey = creds.AccessKeyID
c.SecretKey = creds.SecretAccessKey
return c
}
type contextCreateAWSHelper struct {
user prompt.UI
user prompt.UI
availableRegions func(opts *ContextParams) ([]string, error)
}
func newContextCreateHelper() contextCreateAWSHelper {
return contextCreateAWSHelper{
user: prompt.User{},
user: prompt.User{},
availableRegions: listAvailableRegions,
}
}
func (h contextCreateAWSHelper) createProfile(name string) error {
accessKey, secretKey, err := h.askCredentials()
if err != nil {
return err
}
if accessKey != "" && secretKey != "" {
return h.saveCredentials(name, accessKey, secretKey)
}
return nil
}
func (h contextCreateAWSHelper) createContext(profile, region, description string) (interface{}, string) {
if profile == "default" {
profile = ""
}
description = strings.TrimSpace(
fmt.Sprintf("%s (%s)", description, region))
return store.EcsContext{
Profile: profile,
Region: region,
}, description
}
func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) {
profile := opts.Profile
region := opts.Region
profilesList, err := h.getProfiles()
if err != nil {
return nil, "", err
}
if profile != "" {
// validate profile
if profile != "default" && !contains(profilesList, profile) {
return nil, "", errors.Wrapf(errdefs.ErrNotFound, "profile %q", profile)
if opts.CredsFromEnv {
// Explicit creation from ENV variables
ecsCtx, descr := h.createContext(&opts)
return ecsCtx, descr, nil
} else if opts.AccessKey != "" && opts.SecretKey != "" {
// Explicit creation using keys
err := h.createProfileFromCredentials(&opts)
if err != nil {
return nil, "", err
}
} else if opts.Profile != "" {
// Excplicit creation by selecting a profile
// check profile exists
profilesList, err := getProfiles()
if err != nil {
return nil, "", err
}
if !contains(profilesList, opts.Profile) {
return nil, "", errors.Wrapf(errdefs.ErrNotFound, "profile %q not found", opts.Profile)
}
} else {
// choose profile
profile, err = h.chooseProfile(profilesList)
// interactive
var options []string
var actions []func(params *ContextParams) error
if _, err := os.Stat(getAWSConfigFile()); err == nil {
// User has .aws/config file, so we can offer to select one of his profiles
options = append(options, "An existing AWS profile")
actions = append(actions, h.selectFromLocalProfile)
}
options = append(options, "AWS secret and token credentials")
actions = append(actions, h.createProfileFromCredentials)
options = append(options, "AWS environment variables")
actions = append(actions, func(params *ContextParams) error {
opts.CredsFromEnv = true
return nil
})
selected, err := h.user.Select("Create a Docker context using:", options)
if err != nil {
if err == terminal.InterruptErr {
return nil, "", errdefs.ErrCanceled
}
return nil, "", err
}
err = actions[selected](&opts)
if err != nil {
return nil, "", err
}
}
if region == "" {
region, err = h.chooseRegion(region, profile)
if err != nil {
return nil, "", err
}
}
ecsCtx, descr := h.createContext(profile, region, opts.Description)
ecsCtx, descr := h.createContext(&opts)
return ecsCtx, descr, nil
}
func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID string, secretAccessKey string) error {
p := credentials.SharedCredentialsProvider{Profile: profile}
_, err := p.Retrieve()
if err == nil {
return fmt.Errorf("credentials already exist")
func (h contextCreateAWSHelper) createContext(c *ContextParams) (interface{}, string) {
var description string
if c.CredsFromEnv {
if c.Description == "" {
description = "credentials read from environment"
}
return store.EcsContext{
CredentialsFromEnv: c.CredsFromEnv,
Profile: c.Profile,
}, description
}
if err.(awserr.Error).Code() == "SharedCredsLoad" && err.(awserr.Error).Message() == "failed to load shared credentials file" {
_, err := os.Create(p.Filename)
if c.Region != "" {
description = strings.TrimSpace(
fmt.Sprintf("%s (%s)", c.Description, c.Region))
}
return store.EcsContext{
Profile: c.Profile,
}, description
}
func (h contextCreateAWSHelper) selectFromLocalProfile(opts *ContextParams) error {
profilesList, err := getProfiles()
if err != nil {
return err
}
opts.Profile, err = h.chooseProfile(profilesList)
return err
}
func (h contextCreateAWSHelper) createProfileFromCredentials(opts *ContextParams) error {
if opts.AccessKey == "" || opts.SecretKey == "" {
fmt.Println("Retrieve or create AWS Access Key and Secret on https://console.aws.amazon.com/iam/home?#security_credential")
accessKey, secretKey, err := h.askCredentials()
if err != nil {
return err
}
opts.AccessKey = accessKey
opts.SecretKey = secretKey
}
if opts.Region == "" {
err := h.chooseRegion(opts)
if err != nil {
return err
}
}
credIni, err := ini.Load(p.Filename)
// save as a profile
if opts.Profile == "" {
opts.Profile = "default"
}
// context name used as profile name
err := h.saveCredentials(opts.Profile, opts.AccessKey, opts.SecretKey)
if err != nil {
return err
}
return h.saveRegion(opts.Profile, opts.Region)
}
func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID string, secretAccessKey string) error {
file := getAWSCredentialsFile()
err := os.MkdirAll(filepath.Dir(file), 0700)
if err != nil {
return err
}
credIni := ini.Empty()
section, err := credIni.NewSection(profile)
if err != nil {
return err
@ -126,15 +212,47 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri
if err != nil {
return err
}
return credIni.SaveTo(p.Filename)
return credIni.SaveTo(file)
}
func (h contextCreateAWSHelper) getProfiles() ([]string, error) {
func (h contextCreateAWSHelper) saveRegion(profile, region string) error {
if region == "" {
return nil
}
// loads ~/.aws/config
awsConfig := getAWSConfigFile()
configIni, err := ini.Load(awsConfig)
if err != nil {
if !os.IsNotExist(err) {
return err
}
configIni = ini.Empty()
}
profile = fmt.Sprintf("profile %s", profile)
section, err := configIni.GetSection(profile)
if err != nil {
if !strings.Contains(err.Error(), "does not exist") {
return err
}
section, err = configIni.NewSection(profile)
if err != nil {
return err
}
}
// save region under profile section in ~/.aws/config
_, err = section.NewKey("region", region)
if err != nil {
return err
}
return configIni.SaveTo(awsConfig)
}
func getProfiles() ([]string, error) {
profiles := []string{}
// parse both .aws/credentials and .aws/config for profiles
configFiles := map[string]bool{
defaults.SharedCredentialsFilename(): false,
defaults.SharedConfigFilename(): true,
getAWSCredentialsFile(): false,
getAWSConfigFile(): true,
}
for f, prefix := range configFiles {
sections, err := loadIniFile(f, prefix)
@ -151,11 +269,15 @@ func (h contextCreateAWSHelper) getProfiles() ([]string, error) {
}
}
}
sort.Slice(profiles, func(i, j int) bool {
return profiles[i] < profiles[j]
})
return profiles, nil
}
func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error) {
options := []string{"new profile"}
options := []string{}
options = append(options, profiles...)
selected, err := h.user.Select("Select AWS Profile", options)
@ -166,78 +288,86 @@ func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error)
return "", err
}
profile := options[selected]
if options[selected] == "new profile" {
suggestion := ""
if !contains(profiles, "default") {
suggestion = "default"
}
name, err := h.user.Input("profile name", suggestion)
if err != nil {
return "", err
}
if name == "" {
return "", fmt.Errorf("profile name cannot be empty")
}
return name, h.createProfile(name)
}
return profile, nil
}
func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) {
suggestion := region
func getRegion(profile string) (string, error) {
if profile == "" {
profile = "default"
}
// only load ~/.aws/config
awsConfig := defaults.SharedConfigFilename()
configIni, err := ini.Load(awsConfig)
if err != nil {
if !os.IsNotExist(err) {
return "", err
}
configIni = ini.Empty()
}
getProfileRegion := func(p string) string {
r := ""
section, err := configIni.GetSection(p)
if err == nil {
reg, err := section.GetKey("region")
if err == nil {
r = reg.Value()
}
}
return r
}
if profile != "default" {
profile = fmt.Sprintf("profile %s", profile)
}
section, err := configIni.GetSection(profile)
if err != nil {
if !strings.Contains(err.Error(), "does not exist") {
return "", err
}
section, err = configIni.NewSection(profile)
if err != nil {
return "", err
}
}
reg, err := section.GetKey("region")
if err == nil {
suggestion = reg.Value()
}
// promp user for region
region, err = h.user.Input("Region", suggestion)
if err != nil {
return "", err
region := getProfileRegion(profile)
if region == "" {
region = getProfileRegion("default")
}
if region == "" {
return "", fmt.Errorf("region cannot be empty")
// fallback to AWS default
region = "us-east-1"
}
// save selected/typed region under profile in ~/.aws/config
_, err = section.NewKey("region", region)
return region, nil
}
func (h contextCreateAWSHelper) chooseRegion(opts *ContextParams) error {
regions, err := h.availableRegions(opts)
if err != nil {
return "", err
return err
}
return region, configIni.SaveTo(awsConfig)
// promp user for region
selected, err := h.user.Select("Region", regions)
if err != nil {
return err
}
opts.Region = regions[selected]
return nil
}
func listAvailableRegions(opts *ContextParams) ([]string, error) {
// Setup SDK with credentials, will also validate those
session, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{
Credentials: credentials.NewStaticCredentials(opts.AccessKey, opts.SecretKey, ""),
Region: aws.String("us-east-1"),
},
})
if err != nil {
return nil, err
}
desc, err := ec2.New(session).DescribeRegions(&ec2.DescribeRegionsInput{})
if err != nil {
return nil, err
}
var regions []string
for _, r := range desc.Regions {
regions = append(regions, aws.StringValue(r.RegionName))
}
return regions, nil
}
func (h contextCreateAWSHelper) askCredentials() (string, string, error) {
confirm, err := h.user.Confirm("Enter AWS credentials", false)
if err != nil {
return "", "", err
}
if !confirm {
return "", "", nil
}
accessKeyID, err := h.user.Input("AWS Access Key ID", "")
if err != nil {
return "", "", err
@ -277,3 +407,19 @@ func loadIniFile(path string, prefix bool) (map[string]ini.Section, error) {
}
return profiles, nil
}
func getAWSConfigFile() string {
awsConfig, ok := os.LookupEnv("AWS_CONFIG_FILE")
if !ok {
awsConfig = defaults.SharedConfigFilename()
}
return awsConfig
}
func getAWSCredentialsFile() string {
awsConfig, ok := os.LookupEnv("AWS_SHARED_CREDENTIALS_FILE")
if !ok {
awsConfig = defaults.SharedCredentialsFilename()
}
return awsConfig
}

173
ecs/context_test.go Normal file
View File

@ -0,0 +1,173 @@
/*
Copyright 2020 Docker Compose CLI 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.
*/
package ecs
import (
"context"
"os"
"testing"
"github.com/docker/compose-cli/context/store"
"github.com/docker/compose-cli/prompt"
"github.com/golang/mock/gomock"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
"gotest.tools/v3/golden"
)
func TestCreateContextDataFromEnv(t *testing.T) {
c := contextCreateAWSHelper{
user: nil,
}
data, desc, err := c.createContextData(context.TODO(), ContextParams{
Name: "test",
CredsFromEnv: true,
})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).CredentialsFromEnv, true)
assert.Equal(t, desc, "credentials read from environment")
}
func TestCreateContextDataByKeys(t *testing.T) {
dir := fs.NewDir(t, "aws")
os.Setenv("AWS_CONFIG_FILE", dir.Join("config")) // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", dir.Join("credentials")) // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
c := contextCreateAWSHelper{
user: nil,
}
data, _, err := c.createContextData(context.TODO(), ContextParams{
Name: "test",
AccessKey: "ABCD",
SecretKey: "X&123",
Region: "eu-west-3",
})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "default")
s := golden.Get(t, dir.Join("config"))
golden.Assert(t, string(s), "context/by-keys/config.golden")
s = golden.Get(t, dir.Join("credentials"))
golden.Assert(t, string(s), "context/by-keys/credentials.golden")
}
func TestCreateContextDataFromProfile(t *testing.T) {
os.Setenv("AWS_CONFIG_FILE", "testdata/context/by-profile/config.golden") // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context/by-profile/credentials.golden") // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
c := contextCreateAWSHelper{
user: nil,
}
data, _, err := c.createContextData(context.TODO(), ContextParams{
Name: "test",
Profile: "foo",
})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "foo")
}
func TestCreateContextDataFromEnvInteractive(t *testing.T) {
dir := fs.NewDir(t, "aws")
os.Setenv("AWS_CONFIG_FILE", dir.Join("config")) // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", dir.Join("credentials")) // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ui := prompt.NewMockUI(ctrl)
c := contextCreateAWSHelper{
user: ui,
}
ui.EXPECT().Select("Create a Docker context using:", gomock.Any()).Return(1, nil)
data, _, err := c.createContextData(context.TODO(), ContextParams{})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).CredentialsFromEnv, true)
}
func TestCreateContextDataByKeysInteractive(t *testing.T) {
dir := fs.NewDir(t, "aws")
os.Setenv("AWS_CONFIG_FILE", dir.Join("config")) // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", dir.Join("credentials")) // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ui := prompt.NewMockUI(ctrl)
c := contextCreateAWSHelper{
user: ui,
availableRegions: func(opts *ContextParams) ([]string, error) {
return []string{"us-east-1", "eu-west-3"}, nil
},
}
ui.EXPECT().Select("Create a Docker context using:", gomock.Any()).Return(0, nil)
ui.EXPECT().Input("AWS Access Key ID", gomock.Any()).Return("ABCD", nil)
ui.EXPECT().Password("Enter AWS Secret Access Key").Return("X&123", nil)
ui.EXPECT().Select("Region", []string{"us-east-1", "eu-west-3"}).Return(1, nil)
data, _, err := c.createContextData(context.TODO(), ContextParams{})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "default")
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "default")
s := golden.Get(t, dir.Join("config"))
golden.Assert(t, string(s), "context/by-keys/config.golden")
s = golden.Get(t, dir.Join("credentials"))
golden.Assert(t, string(s), "context/by-keys/credentials.golden")
}
func TestCreateContextDataByProfileInteractive(t *testing.T) {
os.Setenv("AWS_CONFIG_FILE", "testdata/context/by-profile/config.golden") // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context/by-profile/credentials.golden") // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ui := prompt.NewMockUI(ctrl)
c := contextCreateAWSHelper{
user: ui,
}
ui.EXPECT().Select("Create a Docker context using:", gomock.Any()).Return(0, nil)
ui.EXPECT().Select("Select AWS Profile", []string{"default", "foo"}).Return(1, nil)
data, _, err := c.createContextData(context.TODO(), ContextParams{})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "foo")
}

View File

@ -0,0 +1,3 @@
[profile default]
region = eu-west-3

View File

@ -0,0 +1,4 @@
[default]
aws_access_key_id = ABCD
aws_secret_access_key = X&123

View File

@ -0,0 +1,3 @@
[profile foo]
region = eu-west-3

View File

@ -0,0 +1,4 @@
[foo]
aws_access_key_id = ABCD
aws_secret_access_key = X&123

1
go.sum
View File

@ -748,6 +748,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -20,6 +20,8 @@ import (
"github.com/AlecAivazis/survey/v2"
)
//go:generate mockgen -destination=./prompt_mock.go -self_package "github.com/docker/compose-cli/prompt" -package=prompt . UI
// UI - prompt user input
type UI interface {
Select(message string, options []string) (int, error)

93
prompt/prompt_mock.go Normal file
View File

@ -0,0 +1,93 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/docker/compose-cli/prompt (interfaces: UI)
// Package prompt is a generated GoMock package.
package prompt
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockUI is a mock of UI interface
type MockUI struct {
ctrl *gomock.Controller
recorder *MockUIMockRecorder
}
// MockUIMockRecorder is the mock recorder for MockUI
type MockUIMockRecorder struct {
mock *MockUI
}
// NewMockUI creates a new mock instance
func NewMockUI(ctrl *gomock.Controller) *MockUI {
mock := &MockUI{ctrl: ctrl}
mock.recorder = &MockUIMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockUI) EXPECT() *MockUIMockRecorder {
return m.recorder
}
// Confirm mocks base method
func (m *MockUI) Confirm(arg0 string, arg1 bool) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Confirm", arg0, arg1)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Confirm indicates an expected call of Confirm
func (mr *MockUIMockRecorder) Confirm(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Confirm", reflect.TypeOf((*MockUI)(nil).Confirm), arg0, arg1)
}
// Input mocks base method
func (m *MockUI) Input(arg0, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Input", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Input indicates an expected call of Input
func (mr *MockUIMockRecorder) Input(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Input", reflect.TypeOf((*MockUI)(nil).Input), arg0, arg1)
}
// Password mocks base method
func (m *MockUI) Password(arg0 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Password", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Password indicates an expected call of Password
func (mr *MockUIMockRecorder) Password(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Password", reflect.TypeOf((*MockUI)(nil).Password), arg0)
}
// Select mocks base method
func (m *MockUI) Select(arg0 string, arg1 []string) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Select", arg0, arg1)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Select indicates an expected call of Select
func (mr *MockUIMockRecorder) Select(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockUI)(nil).Select), arg0, arg1)
}

View File

@ -168,16 +168,15 @@ func setupTest(t *testing.T) (*E2eCLI, string) {
if localTestProfile != "" {
region := os.Getenv("TEST_AWS_REGION")
assert.Check(t, region != "")
res = c.RunDockerCmd("context", "create", "ecs", contextName, "--profile", "default", "--region", region)
res = c.RunDockerCmd("context", "create", "ecs", contextName, "--from-env")
} else {
profile := "default"
region := os.Getenv("AWS_DEFAULT_REGION")
secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
keyID := os.Getenv("AWS_ACCESS_KEY_ID")
assert.Check(t, keyID != "")
assert.Check(t, secretKey != "")
assert.Check(t, region != "")
res = c.RunDockerCmd("context", "create", "ecs", contextName, "--profile", profile, "--region", region)
res = c.RunDockerCmd("context", "create", "ecs", contextName, "--from-env")
}
res.Assert(t, icmd.Expected{Out: "Successfully created ecs context \"" + contextName + "\""})
res = c.RunDockerCmd("context", "use", contextName)