revisit context creation

Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
aiordache 2020-10-28 10:54:10 +01:00
parent 2dcd1a5826
commit 902b660de1
3 changed files with 87 additions and 85 deletions

View File

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

View File

@ -18,6 +18,7 @@ package ecs
import ( import (
"context" "context"
"fmt"
"github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/containers"
@ -61,6 +62,13 @@ func service(ctx context.Context) (backend.Service, error) {
} }
func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) { func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) {
if ecsCtx.CredentialsFromEnv {
creds := getEnvVars()
if !creds.HaveRequiredCredentials() {
return nil, fmt.Errorf(`context requires credentials to be passed as environment variable.`)
}
}
sess, err := session.NewSessionWithOptions(session.Options{ sess, err := session.NewSessionWithOptions(session.Options{
Profile: ecsCtx.Profile, Profile: ecsCtx.Profile,
SharedConfigState: session.SharedConfigEnable, SharedConfigState: session.SharedConfigEnable,

View File

@ -39,10 +39,11 @@ type contextElements struct {
SessionToken string SessionToken string
Profile string Profile string
Region string Region string
CredsFromEnv bool
} }
func (c contextElements) HaveRequiredProps() bool { func (c contextElements) HaveRequiredCredentials() bool {
if c.AccessKey != "" && c.SecretKey != "" { //} && c.Region != "" { if c.AccessKey != "" && c.SecretKey != "" {
return true return true
} }
return false return false
@ -58,6 +59,24 @@ func newContextCreateHelper() contextCreateAWSHelper {
} }
} }
func getEnvVars() contextElements {
c := contextElements{}
profile := os.Getenv("AWS_PROFILE")
if profile != "" {
c.Profile = profile
}
p := credentials.EnvProvider{}
creds, err := p.Retrieve()
if err != nil {
return c
}
c.AccessKey = creds.AccessKeyID
c.SecretKey = creds.SecretAccessKey
c.SessionToken = creds.SessionToken
return c
}
func (h contextCreateAWSHelper) createProfile(name string, c *contextElements) error { func (h contextCreateAWSHelper) createProfile(name string, c *contextElements) error {
if c != nil { if c != nil {
if c.AccessKey != "" && c.SecretKey != "" { if c.AccessKey != "" && c.SecretKey != "" {
@ -83,48 +102,34 @@ func (h contextCreateAWSHelper) createProfile(name string, c *contextElements) e
return nil return nil
} }
func (h contextCreateAWSHelper) createContext(profile, region, description string) (interface{}, string) { func (h contextCreateAWSHelper) createContext(c *contextElements, description string) (interface{}, string) {
if profile == "default" { if c.Profile == "default" {
profile = "" c.Profile = ""
} }
description = strings.TrimSpace( description = strings.TrimSpace(
fmt.Sprintf("%s (%s)", description, region)) fmt.Sprintf("%s (%s)", description, c.Region))
if c.CredsFromEnv {
return store.EcsContext{ return store.EcsContext{
Profile: profile, CredentialsFromEnv: c.CredsFromEnv,
Region: region, Profile: c.Profile,
Region: c.Region,
}, description }, description
} }
return store.EcsContext{
func (h contextCreateAWSHelper) ParseEnvVars(c *contextElements) { Profile: c.Profile,
profile := os.Getenv("AWS_PROFILE") Region: c.Region,
if profile != "" { }, description
c.Profile = profile
}
region := os.Getenv("AWS_REGION")
if region != "" {
c.Region = region
}
p := credentials.EnvProvider{}
creds, err := p.Retrieve()
if err != nil {
return
}
c.AccessKey = creds.AccessKeyID
c.SecretKey = creds.SecretAccessKey
c.SessionToken = creds.SessionToken
} }
func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) { func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) {
creds := contextElements{} creds := contextElements{}
h.ParseEnvVars(&creds) options := []string{
"Use AWS credentials set via environment variables",
options := []string{} "Create a new profile with AWS credentials",
if creds.HaveRequiredProps() { "Select from existing local AWS profiles",
options = append(options, "existing AWS environment variables (detected credentials)")
} }
options = append(options, "enter AWS credentials", "use an AWS profile or create a new profile (advanced)") //if creds.HaveRequiredProps() {
selected, err := h.user.Select("Would you like to create your context based on", options) selected, err := h.user.Select("Would you like to create your context based on", options)
if err != nil { if err != nil {
if err == terminal.InterruptErr { if err == terminal.InterruptErr {
@ -132,14 +137,6 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
} }
return nil, "", err return nil, "", err
} }
if len(options) == 2 {
selected = selected + 1
}
if selected != 0 {
creds = contextElements{}
}
if creds.Region == "" { if creds.Region == "" {
creds.Region = opts.Region creds.Region = opts.Region
} }
@ -149,19 +146,20 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
switch selected { switch selected {
case 0: case 0:
creds.CredsFromEnv = true
// confirm region profile should target
if creds.Region == "" { if creds.Region == "" {
// prompt for the region to use with this context
creds.Region, err = h.chooseRegion(creds.Region, creds.Profile) creds.Region, err = h.chooseRegion(creds.Region, creds.Profile)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
} }
if creds.Profile == "" { /*if creds.Profile == "" {
creds.Profile = opts.Name creds.Profile = opts.Name
} }
fmt.Printf("Saving credentials under profile %s\n", creds.Profile) fmt.Printf("Saving credentials under profile %s\n", creds.Profile)
h.createProfile(creds.Profile, &creds) h.createProfile(creds.Profile, &creds)*/
case 1: case 1:
accessKey, secretKey, err := h.askCredentials() accessKey, secretKey, err := h.askCredentials()
if err != nil { if err != nil {
@ -201,8 +199,8 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
} }
} }
os.Exit(0) //os.Exit(0)
ecsCtx, descr := h.createContext(creds.Profile, creds.Region, opts.Description) ecsCtx, descr := h.createContext(&creds, opts.Description)
return ecsCtx, descr, nil return ecsCtx, descr, nil
} }
@ -264,7 +262,7 @@ func (h contextCreateAWSHelper) getProfiles() ([]string, error) {
} }
func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error) { func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error) {
options := []string{"new profile"} options := []string{}
options = append(options, profiles...) options = append(options, profiles...)
selected, err := h.user.Select("Select AWS Profile", options) selected, err := h.user.Select("Select AWS Profile", options)
@ -275,55 +273,55 @@ func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error)
return "", err return "", err
} }
profile := options[selected] 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, nil)
}
return profile, nil return profile, nil
} }
func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) { func (h contextCreateAWSHelper) getRegionSuggestion(region string, profile string) (string, error) {
suggestion := region
if profile == "" { if profile == "" {
profile = "default" profile = "default"
} }
// only load ~/.aws/config // only load ~/.aws/config
awsConfig := defaults.SharedConfigFilename() awsConfig := defaults.SharedConfigFilename()
configIni, err := ini.Load(awsConfig) configIni, err := ini.Load(awsConfig)
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return "", err return "", err
} }
configIni = ini.Empty() configIni = ini.Empty()
} }
var f func(string, string) string
f = func(r string, p string) string {
section, err := configIni.GetSection(p)
if err == nil {
reg, err := section.GetKey("region")
if err == nil {
r = reg.Value()
}
}
if r == "" {
switch p {
case "":
return "us-east-1"
case "default":
return f(r, "")
}
return f(r, "default")
}
return r
}
if profile != "default" { if profile != "default" {
profile = fmt.Sprintf("profile %s", profile) profile = fmt.Sprintf("profile %s", profile)
} }
section, err := configIni.GetSection(profile) return f(region, profile), nil
if err != nil {
if !strings.Contains(err.Error(), "does not exist") {
return "", err
} }
section, err = configIni.NewSection(profile)
func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) {
suggestion, err := h.getRegionSuggestion(region, profile)
if err != nil { if err != nil {
return "", err return "", err
} }
}
reg, err := section.GetKey("region")
if err == nil {
suggestion = reg.Value()
}
// promp user for region // promp user for region
region, err = h.user.Input("Region", suggestion) region, err = h.user.Input("Region", suggestion)
if err != nil { if err != nil {
@ -332,12 +330,7 @@ func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (str
if region == "" { if region == "" {
return "", fmt.Errorf("region cannot be empty") return "", fmt.Errorf("region cannot be empty")
} }
// save selected/typed region under profile in ~/.aws/config return region, nil
_, err = section.NewKey("region", region)
if err != nil {
return "", err
}
return region, configIni.SaveTo(awsConfig)
} }
func (h contextCreateAWSHelper) askCredentials() (string, string, error) { func (h contextCreateAWSHelper) askCredentials() (string, string, error) {