mirror of https://github.com/docker/compose.git
Populate ~/.aws/config(credentials) on ecs context create
Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
parent
5b54816a1a
commit
c2af0a136a
|
@ -56,8 +56,6 @@ func createEcsCommand() *cobra.Command {
|
|||
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.AwsID, "key-id", "", "AWS Access Key ID")
|
||||
cmd.Flags().StringVar(&opts.AwsSecret, "secret-key", "", "AWS Secret Access Key")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
|
@ -40,9 +40,6 @@ type ContextParams struct {
|
|||
Description string
|
||||
Region string
|
||||
Profile string
|
||||
|
||||
AwsID string
|
||||
AwsSecret string
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
255
ecs/context.go
255
ecs/context.go
|
@ -20,13 +20,13 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"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"
|
||||
|
@ -44,60 +44,57 @@ func newContextCreateHelper() contextCreateAWSHelper {
|
|||
}
|
||||
}
|
||||
|
||||
func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) {
|
||||
accessKey := opts.AwsID
|
||||
secretKey := opts.AwsSecret
|
||||
|
||||
ecsCtx := store.EcsContext{
|
||||
Profile: opts.Profile,
|
||||
Region: opts.Region,
|
||||
}
|
||||
|
||||
if h.missingRequiredFlags(ecsCtx) {
|
||||
profilesList, err := h.getProfiles()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
// get profile
|
||||
_, ok := profilesList[ecsCtx.Profile]
|
||||
if !ok {
|
||||
profile, err := h.chooseProfile(profilesList)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
ecsCtx.Profile = profile
|
||||
}
|
||||
// set region
|
||||
region, err := h.chooseRegion(ecsCtx.Region, profilesList[ecsCtx.Profile])
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
ecsCtx.Region = region
|
||||
|
||||
accessKey, secretKey, err = h.askCredentials()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
func (h contextCreateAWSHelper) createProfile(name string) error {
|
||||
accessKey, secretKey, err := h.askCredentials()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if accessKey != "" && secretKey != "" {
|
||||
if err := h.saveCredentials(ecsCtx.Profile, accessKey, secretKey); err != nil {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
// choose profile
|
||||
profile, err = h.chooseProfile(profilesList)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
description := ecsCtx.Region
|
||||
if opts.Description != "" {
|
||||
description = fmt.Sprintf("%s (%s)", opts.Description, description)
|
||||
if region == "" {
|
||||
region, err = h.chooseRegion(region, profile)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
return ecsCtx, description, nil
|
||||
}
|
||||
|
||||
func (h contextCreateAWSHelper) missingRequiredFlags(ctx store.EcsContext) bool {
|
||||
if ctx.Profile == "" || ctx.Region == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
ecsCtx, descr := h.createContext(profile, region, opts.Description)
|
||||
return ecsCtx, descr, nil
|
||||
}
|
||||
|
||||
func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID string, secretAccessKey string) error {
|
||||
|
@ -132,75 +129,151 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri
|
|||
return credIni.SaveTo(p.Filename)
|
||||
}
|
||||
|
||||
func (h contextCreateAWSHelper) getProfiles() (map[string]ini.Section, error) {
|
||||
profiles := map[string]ini.Section{"new profile": {}}
|
||||
credIni, err := ini.Load(defaults.SharedConfigFilename())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (h contextCreateAWSHelper) getProfiles() ([]string, error) {
|
||||
profiles := []string{}
|
||||
// parse both .aws/credentials and .aws/config for profiles
|
||||
configFiles := map[string]bool{
|
||||
defaults.SharedCredentialsFilename(): false,
|
||||
defaults.SharedConfigFilename(): true,
|
||||
}
|
||||
for _, section := range credIni.Sections() {
|
||||
if strings.HasPrefix(section.Name(), "profile") {
|
||||
profiles[section.Name()[len("profile "):]] = *section
|
||||
for f, prefix := range configFiles {
|
||||
sections, err := loadIniFile(f, prefix)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
for key := range sections {
|
||||
name := strings.ToLower(key)
|
||||
if !contains(profiles, name) {
|
||||
profiles = append(profiles, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
func (h contextCreateAWSHelper) chooseProfile(section map[string]ini.Section) (string, error) {
|
||||
keys := reflect.ValueOf(section).MapKeys()
|
||||
profiles := make([]string, len(keys))
|
||||
for i := 0; i < len(keys); i++ {
|
||||
profiles[i] = keys[i].String()
|
||||
}
|
||||
func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error) {
|
||||
options := []string{"new profile"}
|
||||
options = append(options, profiles...)
|
||||
|
||||
selected, err := h.user.Select("Select AWS Profile", profiles)
|
||||
selected, err := h.user.Select("Select AWS Profile", options)
|
||||
if err != nil {
|
||||
if err == terminal.InterruptErr {
|
||||
return "", errdefs.ErrCanceled
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
profile := profiles[selected]
|
||||
if profiles[selected] == "new profile" {
|
||||
return h.user.Input("profile name", "")
|
||||
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, section ini.Section) (string, error) {
|
||||
defaultRegion := region
|
||||
if defaultRegion == "" && section.Name() != "" {
|
||||
reg, err := section.GetKey("region")
|
||||
if err == nil {
|
||||
defaultRegion = reg.Value()
|
||||
func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) {
|
||||
suggestion := region
|
||||
|
||||
// only load ~/.aws/config
|
||||
awsConfig := defaults.SharedConfigFilename()
|
||||
configIni, err := ini.Load(awsConfig)
|
||||
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
configIni = ini.Empty()
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
result, err := h.user.Input("Region", defaultRegion)
|
||||
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
|
||||
}
|
||||
return result, nil
|
||||
if region == "" {
|
||||
return "", fmt.Errorf("region cannot be empty")
|
||||
}
|
||||
// save selected/typed region under profile in ~/.aws/config
|
||||
_, err = section.NewKey("region", region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return region, configIni.SaveTo(awsConfig)
|
||||
}
|
||||
|
||||
func (h contextCreateAWSHelper) askCredentials() (string, string, error) {
|
||||
confirm, err := h.user.Confirm("Enter credentials", false)
|
||||
confirm, err := h.user.Confirm("Enter AWS credentials", false)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if confirm {
|
||||
accessKeyID, err := h.user.Input("AWS Access Key ID", "")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
secretAccessKey, err := h.user.Password("Enter AWS Secret Access Key")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// validate password
|
||||
if len(secretAccessKey) < 3 {
|
||||
return "", "", fmt.Errorf("AWS Secret Access Key must have more than 3 characters")
|
||||
}
|
||||
return accessKeyID, secretAccessKey, nil
|
||||
if !confirm {
|
||||
return "", "", nil
|
||||
}
|
||||
return "", "", nil
|
||||
|
||||
accessKeyID, err := h.user.Input("AWS Access Key ID", "")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
secretAccessKey, err := h.user.Password("Enter AWS Secret Access Key")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// validate access ID and password
|
||||
if len(accessKeyID) < 3 || len(secretAccessKey) < 3 {
|
||||
return "", "", fmt.Errorf("AWS Access/Secret Access Key must have more than 3 characters")
|
||||
}
|
||||
return accessKeyID, secretAccessKey, nil
|
||||
}
|
||||
|
||||
func contains(values []string, value string) bool {
|
||||
for _, v := range values {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadIniFile(path string, prefix bool) (map[string]ini.Section, error) {
|
||||
profiles := map[string]ini.Section{}
|
||||
credIni, err := ini.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, section := range credIni.Sections() {
|
||||
if prefix && strings.HasPrefix(section.Name(), "profile ") {
|
||||
profiles[section.Name()[len("profile "):]] = *section
|
||||
} else if !prefix || section.Name() == "default" {
|
||||
profiles[section.Name()] = *section
|
||||
}
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
|
|
@ -152,16 +152,16 @@ 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", localTestProfile, "--region", region)
|
||||
res = c.RunDockerCmd("context", "create", "ecs", contextName, "--profile", "default", "--region", region)
|
||||
} else {
|
||||
profile := contextName
|
||||
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, "--secret-key", secretKey, "--key-id", keyID)
|
||||
res = c.RunDockerCmd("context", "create", "ecs", contextName, "--profile", profile, "--region", region)
|
||||
}
|
||||
res.Assert(t, icmd.Expected{Out: "Successfully created ecs context \"" + contextName + "\""})
|
||||
res = c.RunDockerCmd("context", "use", contextName)
|
||||
|
|
Loading…
Reference in New Issue