Remove region from contexts and cleanup

Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
aiordache 2020-10-30 17:42:57 +01:00
parent 879afa85c0
commit de0be8650e
4 changed files with 101 additions and 120 deletions

View File

@ -55,9 +55,8 @@ func createEcsCommand() *cobra.Command {
addDescriptionFlag(cmd, &opts.Description) addDescriptionFlag(cmd, &opts.Description)
cmd.Flags().BoolVar(&localSimulation, "local-simulation", false, "Create context for ECS local simulation endpoints") 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.Profile, "profile", "", "Use an existing AWS profile")
cmd.Flags().StringVar(&opts.Region, "region", "", "Region") cmd.Flags().BoolVar(&opts.CredsFromEnv, "from-env", false, "Use AWS environment variables for profile, or credentials and region")
cmd.Flags().BoolVar(&opts.CredsFromEnv, "from-env", false, "Use credentials and region from environment")
return cmd return cmd
} }

View File

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

View File

@ -43,20 +43,19 @@ type ContextParams struct {
Description string Description string
AccessKey string AccessKey string
SecretKey string SecretKey string
SessionToken string
Profile string Profile string
Region string Region string
CredsFromEnv bool CredsFromEnv bool
} }
func (c ContextParams) haveRequiredCredentials() bool { func (c ContextParams) haveRequiredEnvVars() bool {
if c.AccessKey == "" || c.SecretKey == "" { if c.Profile != "" {
return false return true
} }
if c.Region == "" && c.Profile == "" { if c.AccessKey != "" && c.SecretKey != "" {
return false return true
} }
return true return false
} }
func init() { func init() {
@ -76,29 +75,26 @@ func service(ctx context.Context) (backend.Service, error) {
} }
func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) { func getEcsAPIService(ecsCtx store.EcsContext) (*ecsAPIService, error) {
var region string region := ""
var profile string profile := ecsCtx.Profile
if ecsCtx.CredentialsFromEnv { if ecsCtx.CredentialsFromEnv {
creds := getEnvVars() env := getEnvVars()
if !creds.haveRequiredCredentials() { if !env.haveRequiredEnvVars() {
return nil, fmt.Errorf(`context requires credentials to be passed as environment variable`) return nil, fmt.Errorf("context requires credentials to be passed as environment variables")
}
region = creds.Region
profile = creds.Profile
} else {
// get region
profile = ecsCtx.Profile
if ecsCtx.Region != "" {
region = ecsCtx.Region
} else {
r, _, err := getRegion(ecsCtx.Profile)
if err != nil {
return nil, err
}
region = r
} }
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{ sess, err := session.NewSessionWithOptions(session.Options{
Profile: profile, Profile: profile,
SharedConfigState: session.SharedConfigEnable, SharedConfigState: session.SharedConfigEnable,

View File

@ -34,22 +34,16 @@ import (
) )
func getEnvVars() ContextParams { func getEnvVars() ContextParams {
c := ContextParams{} c := ContextParams{
Profile: os.Getenv("AWS_PROFILE"),
//check profile env vars Region: os.Getenv("AWS_REGION"),
profile := os.Getenv("AWS_PROFILE")
if profile != "" {
c.Profile = profile
} }
// check REGION env vars
region := os.Getenv("AWS_REGION")
c.Region = region
if c.Region == "" { if c.Region == "" {
region = os.Getenv("AWS_DEFAULT_REGION") defaultRegion := os.Getenv("AWS_DEFAULT_REGION")
if region == "" { if defaultRegion == "" {
region = "us-east-1" defaultRegion = "us-east-1"
} }
c.Region = region c.Region = defaultRegion
} }
p := credentials.EnvProvider{} p := credentials.EnvProvider{}
@ -59,7 +53,6 @@ func getEnvVars() ContextParams {
} }
c.AccessKey = creds.AccessKeyID c.AccessKey = creds.AccessKeyID
c.SecretKey = creds.SecretAccessKey c.SecretKey = creds.SecretAccessKey
c.SessionToken = creds.SessionToken
return c return c
} }
@ -73,31 +66,6 @@ func newContextCreateHelper() contextCreateAWSHelper {
} }
} }
func (h contextCreateAWSHelper) createProfile(name string, c *ContextParams) error {
if c != nil {
if c.AccessKey != "" && c.SecretKey != "" {
return h.saveCredentials(name, c.AccessKey, c.SecretKey)
}
accessKey, secretKey, err := h.askCredentials()
if err != nil {
return err
}
c.AccessKey = accessKey
c.SecretKey = secretKey
return h.saveCredentials(name, c.AccessKey, c.SecretKey)
}
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(c *ContextParams) (interface{}, string) { func (h contextCreateAWSHelper) createContext(c *ContextParams) (interface{}, string) {
if c.Profile == "default" { if c.Profile == "default" {
c.Profile = "" c.Profile = ""
@ -111,7 +79,6 @@ func (h contextCreateAWSHelper) createContext(c *ContextParams) (interface{}, st
return store.EcsContext{ return store.EcsContext{
CredentialsFromEnv: c.CredsFromEnv, CredentialsFromEnv: c.CredsFromEnv,
Profile: c.Profile, Profile: c.Profile,
Region: c.Region,
}, description }, description
} }
@ -121,7 +88,6 @@ func (h contextCreateAWSHelper) createContext(c *ContextParams) (interface{}, st
} }
return store.EcsContext{ return store.EcsContext{
Profile: c.Profile, Profile: c.Profile,
Region: c.Region,
}, description }, description
} }
@ -130,28 +96,8 @@ func (h contextCreateAWSHelper) selectFromLocalProfile(opts *ContextParams) erro
if err != nil { if err != nil {
return err return err
} }
// choose profile
opts.Profile, err = h.chooseProfile(profilesList) opts.Profile, err = h.chooseProfile(profilesList)
if err != nil { return err
return err
}
if opts.Region == "" {
region, isDefinedInProfile, err := getRegion(opts.Profile)
if err != nil {
return err
}
if isDefinedInProfile {
opts.Region = region
} else {
fmt.Println("No region defined in the profile. Choose the region to use.")
opts.Region, err = h.chooseRegion(opts.Region, opts.Profile)
if err != nil {
return err
}
}
}
return nil
} }
func (h contextCreateAWSHelper) createProfileFromCredentials(opts *ContextParams) error { func (h contextCreateAWSHelper) createProfileFromCredentials(opts *ContextParams) error {
@ -171,8 +117,21 @@ func (h contextCreateAWSHelper) createProfileFromCredentials(opts *ContextParams
if opts.Profile == "" { if opts.Profile == "" {
opts.Profile = opts.Name opts.Profile = opts.Name
} }
fmt.Printf("Saving credentials under profile %s\n", opts.Profile) // check profile does not already exist
return h.createProfile(opts.Profile, opts) profilesList, err := getProfiles()
if err != nil {
return err
}
if contains(profilesList, opts.Profile) {
return fmt.Errorf("profile %q already exists", opts.Profile)
}
fmt.Printf("Saving to profile %q\n", opts.Profile)
// context name used as profile name
err = h.saveCredentials(opts.Name, opts.AccessKey, opts.SecretKey)
if err != nil {
return err
}
return h.saveRegion(opts.Name, opts.Region)
} }
func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) { func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) {
@ -181,12 +140,12 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
return ecsCtx, descr, nil return ecsCtx, descr, nil
} }
options := []string{ options := []string{
"Use AWS credentials from environment", "An existing AWS profile",
"Select from existing AWS profiles", "A new AWS profile",
"Create new profile from AWS credentials", "AWS environment variables",
} }
selected, err := h.user.Select("Would you like to create your context based on", options) selected, err := h.user.Select("Create a Docker context using:", options)
if err != nil { if err != nil {
if err == terminal.InterruptErr { if err == terminal.InterruptErr {
return nil, "", errdefs.ErrCanceled return nil, "", errdefs.ErrCanceled
@ -196,12 +155,12 @@ func (h contextCreateAWSHelper) createContextData(_ context.Context, opts Contex
switch selected { switch selected {
case 0: case 0:
opts.CredsFromEnv = true
case 1:
err = h.selectFromLocalProfile(&opts) err = h.selectFromLocalProfile(&opts)
case 1:
case 2:
err = h.createProfileFromCredentials(&opts) err = h.createProfileFromCredentials(&opts)
case 2:
opts.CredsFromEnv = true
} }
if err != nil { if err != nil {
return nil, "", err return nil, "", err
@ -242,6 +201,38 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri
return credIni.SaveTo(p.Filename) return credIni.SaveTo(p.Filename)
} }
func (h contextCreateAWSHelper) saveRegion(profile, region string) error {
if region == "" {
return nil
}
// loads ~/.aws/config
awsConfig := defaults.SharedConfigFilename()
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) { func getProfiles() ([]string, error) {
profiles := []string{} profiles := []string{}
// parse both .aws/credentials and .aws/config for profiles // parse both .aws/credentials and .aws/config for profiles
@ -282,7 +273,7 @@ func (h contextCreateAWSHelper) chooseProfile(profiles []string) (string, error)
return profile, nil return profile, nil
} }
func getRegion(profile string) (string, bool, error) { func getRegion(profile string) (string, error) {
if profile == "" { if profile == "" {
profile = "default" profile = "default"
} }
@ -291,13 +282,12 @@ func getRegion(profile string) (string, bool, error) {
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 "", false, err return "", err
} }
configIni = ini.Empty() configIni = ini.Empty()
} }
var f func(string) (string, string) getProfileRegion := func(p string) string {
f = func(p string) (string, string) {
r := "" r := ""
section, err := configIni.GetSection(p) section, err := configIni.GetSection(p)
if err == nil { if err == nil {
@ -306,29 +296,26 @@ func getRegion(profile string) (string, bool, error) {
r = reg.Value() r = reg.Value()
} }
} }
if r == "" { return r
switch p {
case "":
return "us-east-1", ""
case "default":
return f("")
}
return f("default")
}
return r, p
} }
if profile != "default" { if profile != "default" {
profile = fmt.Sprintf("profile %s", profile) profile = fmt.Sprintf("profile %s", profile)
} }
region, p := f(profile) region := getProfileRegion(profile)
return region, p == profile, nil if region == "" {
region = getProfileRegion("default")
if region == "" {
return "us-east-1", nil
}
return region, nil
}
return region, nil
} }
func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) { func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) {
suggestion := region suggestion := region
if suggestion == "" { if suggestion == "" {
region, _, err := getRegion(profile) region, err := getRegion(profile)
if err != nil { if err != nil {
return "", err return "", err
} }