From 883acb18fac2c732e7fdf05822959bb45977ed00 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Mon, 1 Feb 2016 18:55:59 -0600 Subject: [PATCH 1/9] IAM Role Switching Adds initial IAM Role Switching support and support for AWS CLI Credential and Config files. See: https://github.com/mitchellh/packer/issues/3109 --- builder/amazon/common/access_config.go | 70 +++++++++++++------ builder/amazon/common/cli_config.go | 95 ++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 19 deletions(-) create mode 100644 builder/amazon/common/cli_config.go diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index dccde08d4..a4aed9bd5 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -10,41 +10,73 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/session" "github.com/mitchellh/packer/template/interpolate" ) // AccessConfig is for common configuration related to AWS access type AccessConfig struct { - AccessKey string `mapstructure:"access_key"` - SecretKey string `mapstructure:"secret_key"` - RawRegion string `mapstructure:"region"` - Token string `mapstructure:"token"` + AccessKey string `mapstructure:"access_key"` + SecretKey string `mapstructure:"secret_key"` + RawRegion string `mapstructure:"region"` + Token string `mapstructure:"token"` + ProfileName string `mapstructure:"profile"` } // Config returns a valid aws.Config object for access to AWS services, or // an error if the authentication and region couldn't be resolved func (c *AccessConfig) Config() (*aws.Config, error) { - creds := credentials.NewChainCredentials([]credentials.Provider{ - &credentials.StaticProvider{Value: credentials.Value{ - AccessKeyID: c.AccessKey, - SecretAccessKey: c.SecretKey, - SessionToken: c.Token, - }}, - &credentials.EnvProvider{}, - &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, - &ec2rolecreds.EC2RoleProvider{}, - }) + var creds *credentials.Credentials + + profile := &CLIConfig{} + err := profile.Prepare(c.ProfileName) + if err != nil { + return nil, err + } region, err := c.Region() if err != nil { return nil, err } - return &aws.Config{ - Region: aws.String(region), - Credentials: creds, - MaxRetries: aws.Int(11), - }, nil + config := &aws.Config{ + Region: aws.String(region), + MaxRetries: aws.Int(11), + } + + if c.ProfileName != "" { + creds = c.assumeRoleCreds(config, profile) + } else { + creds = credentials.NewChainCredentials([]credentials.Provider{ + &credentials.StaticProvider{Value: credentials.Value{ + AccessKeyID: c.AccessKey, + SecretAccessKey: c.SecretKey, + SessionToken: c.Token, + }}, + &credentials.EnvProvider{}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, + &ec2rolecreds.EC2RoleProvider{}, + }) + } + return config.WithCredentials(creds), nil +} + +func (c *AccessConfig) assumeRoleCreds(conf *aws.Config, profile *CLIConfig) (*credentials.Credentials) { + src_creds := credentials.NewStaticCredentials( + profile.Source.AccessKeyID, + profile.Source.SecretAccessKey, + profile.Source.SessionToken, + ) + role_cfg := aws.NewConfig().WithCredentials(src_creds) + role_cfg.MergeIn(conf) + sess := session.New(role_cfg) + return stscreds.NewCredentials(sess, *profile.AssumeRoleInput.RoleArn, func(p *stscreds.AssumeRoleProvider) { + p.RoleSessionName = *profile.AssumeRoleInput.RoleSessionName + if extId := *profile.AssumeRoleInput.ExternalId; extId != "" { + p.ExternalID = &extId + } + }) } // Region returns the aws.Region object for access to AWS services, requesting diff --git a/builder/amazon/common/cli_config.go b/builder/amazon/common/cli_config.go new file mode 100644 index 000000000..2b1b2c014 --- /dev/null +++ b/builder/amazon/common/cli_config.go @@ -0,0 +1,95 @@ +package common + +import ( + "fmt" + "os" + "path" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/go-ini/ini" +) + +type CLIConfig struct { + SourceProfile string + + Source credentials.Value + AssumeRoleInput sts.AssumeRoleInput +} + +// Sets params in the struct based on the file section +func (c *CLIConfig) Prepare(name string) (error) { + cfg, err := c.config() + + cfg_profile_name := fmt.Sprintf("profile %s", name) + profile_cfg, err := cfg.GetSection(cfg_profile_name) + if err != nil { + return err + } + + c.SourceProfile = profile_cfg.Key("source_profile").Value(); + if c.SourceProfile == "" { + c.SourceProfile = name + } + + c.AssumeRoleInput.RoleArn = aws.String(profile_cfg.Key("role_arn").Value()) + + host, err := os.Hostname() + if err != nil { + return err + } + + sessName := fmt.Sprintf("packer-%s", host) + c.AssumeRoleInput.RoleSessionName = &sessName + c.AssumeRoleInput.SerialNumber = aws.String(profile_cfg.Key("mfa_serial").Value()) + if extId := aws.String(profile_cfg.Key("external_id").Value()); extId != nil { + c.AssumeRoleInput.ExternalId = extId + } + + creds, err := c.credentials() + cred_cfg, err := creds.GetSection(c.SourceProfile) + if err != nil { + return err + } + + if len(c.SourceProfile) != 0 { + c.Source.AccessKeyID = cred_cfg.Key("aws_access_key_id").Value() + c.Source.SecretAccessKey = cred_cfg.Key("aws_secret_access_key").Value() + c.Source.SessionToken = cred_cfg.Key("aws_session_token").Value() + } + return nil +} + +func (c *CLIConfig) config() (*ini.File, error) { + config_path := os.Getenv("AWS_CONFIG_FILE") + if config_path == "" { + config_path = path.Join(os.Getenv("HOME"), ".aws", "config") + } + ini, err := c.readFile(config_path) + if err != nil { + return nil, err + } + + return ini, nil +} + +func (c *CLIConfig) credentials() (*ini.File, error) { + cred_path := os.Getenv("AWS_SHARED_CREDENTIALS_FILE") + if cred_path == "" { + cred_path = path.Join(os.Getenv("HOME"), ".aws", "credentials") + } + ini, err := c.readFile(cred_path) + if err != nil { + return nil, err + } + return ini, nil +} + +func (c *CLIConfig) readFile(path string) (*ini.File, error) { + cfg, err := ini.Load(path) + if err != nil { + return nil, err + } + return cfg, nil +} \ No newline at end of file From 7975c5e336474bbe68482d3683627741626cf936 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Tue, 2 Feb 2016 08:36:36 -0600 Subject: [PATCH 2/9] Move CLIConfig instantiation inside of ProfileName check. --- builder/amazon/common/access_config.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index a4aed9bd5..83d0499a6 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -29,12 +29,6 @@ type AccessConfig struct { func (c *AccessConfig) Config() (*aws.Config, error) { var creds *credentials.Credentials - profile := &CLIConfig{} - err := profile.Prepare(c.ProfileName) - if err != nil { - return nil, err - } - region, err := c.Region() if err != nil { return nil, err @@ -46,6 +40,11 @@ func (c *AccessConfig) Config() (*aws.Config, error) { } if c.ProfileName != "" { + profile := &CLIConfig{} + err := profile.Prepare(c.ProfileName) + if err != nil { + return nil, err + } creds = c.assumeRoleCreds(config, profile) } else { creds = credentials.NewChainCredentials([]credentials.Provider{ From 4cf9e360853e7513b0c3be2e4f8815638b0cae5d Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Wed, 3 Feb 2016 10:43:48 -0600 Subject: [PATCH 3/9] Changes credential/config file opener to use homedir instead of HOME environmental variable. --- builder/amazon/common/cli_config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builder/amazon/common/cli_config.go b/builder/amazon/common/cli_config.go index 2b1b2c014..9b8e63286 100644 --- a/builder/amazon/common/cli_config.go +++ b/builder/amazon/common/cli_config.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/service/sts" "github.com/go-ini/ini" + "github.com/mitchellh/go-homedir" ) type CLIConfig struct { @@ -64,7 +65,7 @@ func (c *CLIConfig) Prepare(name string) (error) { func (c *CLIConfig) config() (*ini.File, error) { config_path := os.Getenv("AWS_CONFIG_FILE") if config_path == "" { - config_path = path.Join(os.Getenv("HOME"), ".aws", "config") + config_path = path.Join(homedir.Dir(), ".aws", "config") } ini, err := c.readFile(config_path) if err != nil { @@ -76,8 +77,9 @@ func (c *CLIConfig) config() (*ini.File, error) { func (c *CLIConfig) credentials() (*ini.File, error) { cred_path := os.Getenv("AWS_SHARED_CREDENTIALS_FILE") + home := if cred_path == "" { - cred_path = path.Join(os.Getenv("HOME"), ".aws", "credentials") + cred_path = path.Join(homedir.Dir(), ".aws", "credentials") } ini, err := c.readFile(cred_path) if err != nil { From f1ae2a2b5a80a7e29c16a76941d5dbff5972d715 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Wed, 3 Feb 2016 15:53:01 -0600 Subject: [PATCH 4/9] Moved assume credentials to CLIConfig. Added new functions and methods to CLIConfig. Ran gofmt. --- builder/amazon/common/access_config.go | 42 ++---- builder/amazon/common/cli_config.go | 171 ++++++++++++++++--------- 2 files changed, 120 insertions(+), 93 deletions(-) diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index 83d0499a6..a10fc52f3 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -10,8 +10,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" - "github.com/aws/aws-sdk-go/aws/session" "github.com/mitchellh/packer/template/interpolate" ) @@ -33,20 +31,17 @@ func (c *AccessConfig) Config() (*aws.Config, error) { if err != nil { return nil, err } - - config := &aws.Config{ - Region: aws.String(region), - MaxRetries: aws.Int(11), - } - + config := aws.NewConfig().WithRegion(region).WithMaxRetries(11) if c.ProfileName != "" { - profile := &CLIConfig{} - err := profile.Prepare(c.ProfileName) - if err != nil { - return nil, err - } - creds = c.assumeRoleCreds(config, profile) - } else { + profile, err := NewFromProfile(c.ProfileName) + if err != nil { + return nil, err + } + creds, err = profile.CredentialsFromProfile(config) + if err != nil { + return nil, err + } + } else { creds = credentials.NewChainCredentials([]credentials.Provider{ &credentials.StaticProvider{Value: credentials.Value{ AccessKeyID: c.AccessKey, @@ -61,23 +56,6 @@ func (c *AccessConfig) Config() (*aws.Config, error) { return config.WithCredentials(creds), nil } -func (c *AccessConfig) assumeRoleCreds(conf *aws.Config, profile *CLIConfig) (*credentials.Credentials) { - src_creds := credentials.NewStaticCredentials( - profile.Source.AccessKeyID, - profile.Source.SecretAccessKey, - profile.Source.SessionToken, - ) - role_cfg := aws.NewConfig().WithCredentials(src_creds) - role_cfg.MergeIn(conf) - sess := session.New(role_cfg) - return stscreds.NewCredentials(sess, *profile.AssumeRoleInput.RoleArn, func(p *stscreds.AssumeRoleProvider) { - p.RoleSessionName = *profile.AssumeRoleInput.RoleSessionName - if extId := *profile.AssumeRoleInput.ExternalId; extId != "" { - p.ExternalID = &extId - } - }) -} - // Region returns the aws.Region object for access to AWS services, requesting // the region from the instance metadata if possible. func (c *AccessConfig) Region() (string, error) { diff --git a/builder/amazon/common/cli_config.go b/builder/amazon/common/cli_config.go index 9b8e63286..e97a593f5 100644 --- a/builder/amazon/common/cli_config.go +++ b/builder/amazon/common/cli_config.go @@ -2,96 +2,145 @@ package common import ( "fmt" - "os" - "path" + "os" + "path" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/service/sts" - "github.com/go-ini/ini" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/go-ini/ini" "github.com/mitchellh/go-homedir" ) type CLIConfig struct { - SourceProfile string + SourceProfile string - Source credentials.Value - AssumeRoleInput sts.AssumeRoleInput + AssumeRoleInput *sts.AssumeRoleInput + SourceCredentials *credentials.Credentials + + profileCfg *ini.Section + profileCred *ini.Section +} + +// Return a new CLIConfig with stored profile settings +func NewFromProfile(name string) (*CLIConfig, error) { + c := &CLIConfig{} + err := c.Prepare(name) + if err != nil { + return nil, err + } + arn := aws.String(c.profileCfg.Key("role_arn").Value()) + sessName, err := c.getSessionName(aws.String(c.profileCfg.Key("role_session_name").Value())) + if err != nil { + return nil, err + } + c.AssumeRoleInput = &sts.AssumeRoleInput{ + RoleSessionName: sessName, + RoleArn: arn, + } + id := c.profileCfg.Key("external_id").Value() + if id != "" { + c.AssumeRoleInput.ExternalId = aws.String(id) + } + c.SourceCredentials = credentials.NewStaticCredentials( + c.profileCred.Key("aws_access_key_id").Value(), + c.profileCred.Key("aws_secret_access_key").Value(), + c.profileCred.Key("aws_session_token").Value(), + ) + return c, nil +} + +// Return AWS Credentials using current profile. Must supply source config +func (c *CLIConfig) CredentialsFromProfile(conf *aws.Config) (*credentials.Credentials, error) { + srcCfg := aws.NewConfig().Copy(conf).WithCredentials(c.SourceCredentials) + svc := sts.New(session.New(), srcCfg) + res, err := svc.AssumeRole(c.AssumeRoleInput) + if err != nil { + return nil, err + } + return credentials.NewStaticCredentials( + *res.Credentials.AccessKeyId, + *res.Credentials.SecretAccessKey, + *res.Credentials.SessionToken, + ), nil } // Sets params in the struct based on the file section -func (c *CLIConfig) Prepare(name string) (error) { - cfg, err := c.config() - - cfg_profile_name := fmt.Sprintf("profile %s", name) - profile_cfg, err := cfg.GetSection(cfg_profile_name) +func (c *CLIConfig) Prepare(name string) error { + var err error + c.profileCfg, err = configFromName(name) if err != nil { return err } - - c.SourceProfile = profile_cfg.Key("source_profile").Value(); + c.SourceProfile = c.profileCfg.Key("source_profile").Value() if c.SourceProfile == "" { c.SourceProfile = name } - - c.AssumeRoleInput.RoleArn = aws.String(profile_cfg.Key("role_arn").Value()) - - host, err := os.Hostname() + c.profileCred, err = credsFromName(c.SourceProfile) if err != nil { return err } - - sessName := fmt.Sprintf("packer-%s", host) - c.AssumeRoleInput.RoleSessionName = &sessName - c.AssumeRoleInput.SerialNumber = aws.String(profile_cfg.Key("mfa_serial").Value()) - if extId := aws.String(profile_cfg.Key("external_id").Value()); extId != nil { - c.AssumeRoleInput.ExternalId = extId - } - - creds, err := c.credentials() - cred_cfg, err := creds.GetSection(c.SourceProfile) - if err != nil { - return err - } - - if len(c.SourceProfile) != 0 { - c.Source.AccessKeyID = cred_cfg.Key("aws_access_key_id").Value() - c.Source.SecretAccessKey = cred_cfg.Key("aws_secret_access_key").Value() - c.Source.SessionToken = cred_cfg.Key("aws_session_token").Value() - } - return nil + return nil } -func (c *CLIConfig) config() (*ini.File, error) { - config_path := os.Getenv("AWS_CONFIG_FILE") - if config_path == "" { - config_path = path.Join(homedir.Dir(), ".aws", "config") - } - ini, err := c.readFile(config_path) +func (c *CLIConfig) getSessionName(rawName *string) (*string, error) { + if rawName != nil { + host, err := os.Hostname() + if err != nil { + return nil, err + } + return aws.String(fmt.Sprintf("packer-%s", host)), nil + } else { + return rawName, nil + } +} + +func configFromName(name string) (*ini.Section, error) { + filePath := os.Getenv("AWS_CONFIG_FILE") + if filePath == "" { + home, err := homedir.Dir() + if err != nil { + return nil, err + } + filePath = path.Join(home, ".aws", "config") + } + file, err := readFile(filePath) if err != nil { return nil, err } - - return ini, nil -} - -func (c *CLIConfig) credentials() (*ini.File, error) { - cred_path := os.Getenv("AWS_SHARED_CREDENTIALS_FILE") - home := - if cred_path == "" { - cred_path = path.Join(homedir.Dir(), ".aws", "credentials") - } - ini, err := c.readFile(cred_path) + profileName := fmt.Sprintf("profile %s", name) + cfg, err := file.GetSection(profileName) if err != nil { return nil, err } - return ini, nil + return cfg, nil } -func (c *CLIConfig) readFile(path string) (*ini.File, error) { - cfg, err := ini.Load(path) +func credsFromName(name string) (*ini.Section, error) { + filePath := os.Getenv("AWS_SHARED_CREDENTIALS_FILE") + if filePath == "" { + home, err := homedir.Dir() + if err != nil { + return nil, err + } + filePath = path.Join(home, ".aws", "credentials") + } + file, err := readFile(filePath) if err != nil { return nil, err } - return cfg, nil -} \ No newline at end of file + cfg, err := file.GetSection(name) + if err != nil { + return nil, err + } + return cfg, nil +} + +func readFile(path string) (*ini.File, error) { + cfg, err := ini.Load(path) + if err != nil { + return nil, err + } + return cfg, nil +} From 6c3e0daf241306f8b5b357d477a6e4a0f775ac59 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Thu, 4 Feb 2016 14:56:22 -0600 Subject: [PATCH 5/9] Added tests. Updated CLIConfig to pass tests. --- builder/amazon/common/cli_config.go | 33 +++--- builder/amazon/common/cli_config_test.go | 125 +++++++++++++++++++++++ 2 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 builder/amazon/common/cli_config_test.go diff --git a/builder/amazon/common/cli_config.go b/builder/amazon/common/cli_config.go index e97a593f5..88b7a4afd 100644 --- a/builder/amazon/common/cli_config.go +++ b/builder/amazon/common/cli_config.go @@ -14,6 +14,7 @@ import ( ) type CLIConfig struct { + ProfileName string SourceProfile string AssumeRoleInput *sts.AssumeRoleInput @@ -26,18 +27,19 @@ type CLIConfig struct { // Return a new CLIConfig with stored profile settings func NewFromProfile(name string) (*CLIConfig, error) { c := &CLIConfig{} + c.AssumeRoleInput = new(sts.AssumeRoleInput) err := c.Prepare(name) if err != nil { return nil, err } - arn := aws.String(c.profileCfg.Key("role_arn").Value()) - sessName, err := c.getSessionName(aws.String(c.profileCfg.Key("role_session_name").Value())) + sessName, err := c.getSessionName(c.profileCfg.Key("role_session_name").Value()) if err != nil { return nil, err } - c.AssumeRoleInput = &sts.AssumeRoleInput{ - RoleSessionName: sessName, - RoleArn: arn, + c.AssumeRoleInput.RoleSessionName = aws.String(sessName) + arn := c.profileCfg.Key("role_arn").Value() + if arn != "" { + c.AssumeRoleInput.RoleArn = aws.String(arn) } id := c.profileCfg.Key("external_id").Value() if id != "" { @@ -51,8 +53,13 @@ func NewFromProfile(name string) (*CLIConfig, error) { return c, nil } -// Return AWS Credentials using current profile. Must supply source config +// Return AWS Credentials using current profile. Must supply source config. func (c *CLIConfig) CredentialsFromProfile(conf *aws.Config) (*credentials.Credentials, error) { + // If the profile name is equal to the source profile, there is no role to assume so return + // the source credentials as they were captured. + if c.ProfileName == c.SourceProfile { + return c.SourceCredentials, nil + } srcCfg := aws.NewConfig().Copy(conf).WithCredentials(c.SourceCredentials) svc := sts.New(session.New(), srcCfg) res, err := svc.AssumeRole(c.AssumeRoleInput) @@ -69,13 +76,14 @@ func (c *CLIConfig) CredentialsFromProfile(conf *aws.Config) (*credentials.Crede // Sets params in the struct based on the file section func (c *CLIConfig) Prepare(name string) error { var err error - c.profileCfg, err = configFromName(name) + c.ProfileName = name + c.profileCfg, err = configFromName(c.ProfileName) if err != nil { return err } c.SourceProfile = c.profileCfg.Key("source_profile").Value() if c.SourceProfile == "" { - c.SourceProfile = name + c.SourceProfile = c.ProfileName } c.profileCred, err = credsFromName(c.SourceProfile) if err != nil { @@ -84,13 +92,14 @@ func (c *CLIConfig) Prepare(name string) error { return nil } -func (c *CLIConfig) getSessionName(rawName *string) (*string, error) { - if rawName != nil { +func (c *CLIConfig) getSessionName(rawName string) (string, error) { + if rawName == "" { + name := "packer-" host, err := os.Hostname() if err != nil { - return nil, err + return name, err } - return aws.String(fmt.Sprintf("packer-%s", host)), nil + return fmt.Sprintf("%s%s", name, host), nil } else { return rawName, nil } diff --git a/builder/amazon/common/cli_config_test.go b/builder/amazon/common/cli_config_test.go new file mode 100644 index 000000000..049252a00 --- /dev/null +++ b/builder/amazon/common/cli_config_test.go @@ -0,0 +1,125 @@ +package common + +import ( + "io/ioutil" + "os" + "path" + "testing" + + "github.com/aws/aws-sdk-go/aws" +) + +func init() { + os.Setenv("AWS_ACCESS_KEY_ID", "") + os.Setenv("AWS_ACCESS_KEY", "") + os.Setenv("AWS_SECRET_ACCESS_KEY", "") + os.Setenv("AWS_SECRET_KEY", "") + os.Setenv("AWS_CONFIG_FILE", "") + os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "") +} + +func testCLIConfig() *CLIConfig { + return &CLIConfig{} +} + +func TestCLIConfigNewFromProfile(t *testing.T) { + tmpDir := mockConfig(t) + + c, err := NewFromProfile("testing2") + if err != nil { + t.Error(err) + } + if c.AssumeRoleInput.RoleArn != nil { + t.Errorf("RoleArn should be nil. Instead %p", c.AssumeRoleInput.RoleArn) + } + if c.AssumeRoleInput.ExternalId != nil { + t.Errorf("ExternalId should be nil. Instead %p", c.AssumeRoleInput.ExternalId) + } + + mockConfigClose(t, tmpDir) +} + +func TestAssumeRole(t *testing.T) { + tmpDir := mockConfig(t) + + c, err := NewFromProfile("testing1") + if err != nil { + t.Error(err) + } + // Role + e := "arn:aws:iam::123456789011:role/rolename" + a := *c.AssumeRoleInput.RoleArn + if e != a { + t.Errorf("RoleArn value should be %s. Instead %s", e, a) + } + // Session + a = *c.AssumeRoleInput.RoleSessionName + e = "testsession" + if e != a { + t.Errorf("RoleSessionName value should be %s. Instead %s", e, a) + } + + config := aws.NewConfig() + _, err = c.CredentialsFromProfile(config) + if err == nil { + t.Error("Should have errored") + } + mockConfigClose(t, tmpDir) +} + +func mockConfig(t *testing.T) string { + dir, err := ioutil.TempDir("", "packer-test") + if err != nil { + t.Error(err) + } + cfgFile := path.Join(dir, "config") + os.Setenv("AWS_CONFIG_FILE", cfgFile) + + crdFile := path.Join(dir, "credentials") + os.Setenv("AWS_SHARED_CREDENTIALS_FILE", crdFile) + + f, err := os.Create(cfgFile) + if err != nil { + t.Error(err) + } + + cfg := []byte(`[profile testing1] +region=us-west-2 +source_profile=testingcredentials +role_arn = arn:aws:iam::123456789011:role/rolename +role_session_name = testsession + +[profile testing2] +region=us-west-2 + `) + _, err = f.Write(cfg) + if err != nil { + t.Error(err) + } + + g, err := os.Create(crdFile) + if err != nil { + t.Error(err) + } + + crd := []byte(`[testingcredentials] +aws_access_key_id = foo +aws_secret_access_key = bar + +[testing2] +aws_access_key_id = baz +aws_secret_access_key = qux + `) + _, err = g.Write(crd) + if err != nil { + t.Error(err) + } + return dir +} + +func mockConfigClose(t *testing.T, dir string) { + err := os.RemoveAll(dir) + if err != nil { + t.Error(err) + } +} From a6aefa859f3a9cc5e5e2c9264874b4316169b177 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Thu, 4 Feb 2016 15:39:54 -0600 Subject: [PATCH 6/9] Fixes issue in build where multiple processes were trying to access the same file. --- builder/amazon/common/cli_config_test.go | 29 ++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/builder/amazon/common/cli_config_test.go b/builder/amazon/common/cli_config_test.go index 049252a00..1b1ec27c6 100644 --- a/builder/amazon/common/cli_config_test.go +++ b/builder/amazon/common/cli_config_test.go @@ -1,10 +1,13 @@ package common import ( + "crypto/rand" "io/ioutil" + math "math/rand" "os" "path" "testing" + "time" "github.com/aws/aws-sdk-go/aws" ) @@ -68,17 +71,15 @@ func TestAssumeRole(t *testing.T) { } func mockConfig(t *testing.T) string { - dir, err := ioutil.TempDir("", "packer-test") + b := make([]byte, 10) + math.Seed(time.Now().UnixNano()) + c, err := rand.Read(b) if err != nil { t.Error(err) } - cfgFile := path.Join(dir, "config") - os.Setenv("AWS_CONFIG_FILE", cfgFile) + s := string(b[:c]) - crdFile := path.Join(dir, "credentials") - os.Setenv("AWS_SHARED_CREDENTIALS_FILE", crdFile) - - f, err := os.Create(cfgFile) + dir, err := ioutil.TempDir("", s) if err != nil { t.Error(err) } @@ -92,15 +93,12 @@ role_session_name = testsession [profile testing2] region=us-west-2 `) - _, err = f.Write(cfg) - if err != nil { - t.Error(err) - } - - g, err := os.Create(crdFile) + cfgFile := path.Join(dir, "config") + err = ioutil.WriteFile(cfgFile, cfg, 0644) if err != nil { t.Error(err) } + os.Setenv("AWS_CONFIG_FILE", cfgFile) crd := []byte(`[testingcredentials] aws_access_key_id = foo @@ -110,10 +108,13 @@ aws_secret_access_key = bar aws_access_key_id = baz aws_secret_access_key = qux `) - _, err = g.Write(crd) + crdFile := path.Join(dir, "credentials") + err = ioutil.WriteFile(crdFile, crd, 0644) if err != nil { t.Error(err) } + os.Setenv("AWS_SHARED_CREDENTIALS_FILE", crdFile) + return dir } From d25ba245f4ce3cf85dda439ffb8c81a4df4f2625 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Thu, 4 Feb 2016 16:05:49 -0600 Subject: [PATCH 7/9] Fixes bytes to string issues in Windows build. --- builder/amazon/common/cli_config_test.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/builder/amazon/common/cli_config_test.go b/builder/amazon/common/cli_config_test.go index 1b1ec27c6..def93bbb8 100644 --- a/builder/amazon/common/cli_config_test.go +++ b/builder/amazon/common/cli_config_test.go @@ -1,11 +1,11 @@ package common import ( - "crypto/rand" "io/ioutil" - math "math/rand" + "math/rand" "os" "path" + "strconv" "testing" "time" @@ -71,15 +71,10 @@ func TestAssumeRole(t *testing.T) { } func mockConfig(t *testing.T) string { - b := make([]byte, 10) - math.Seed(time.Now().UnixNano()) - c, err := rand.Read(b) - if err != nil { - t.Error(err) - } - s := string(b[:c]) - - dir, err := ioutil.TempDir("", s) + time := rand.NewSource(time.Now().UnixNano()) + r := rand.New(time) + v := r.Intn(9999999999) + dir, err := ioutil.TempDir("", strconv.Itoa(v)) if err != nil { t.Error(err) } From b75d561c56e88b3b930fbb74e0ebb871bd5975ab Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Thu, 4 Feb 2016 16:12:18 -0600 Subject: [PATCH 8/9] Fixes more windows testing errors. "constant 9999999999 overflows int" running test on CI server. Replaced with timestamp to unixnano --- builder/amazon/common/cli_config_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/builder/amazon/common/cli_config_test.go b/builder/amazon/common/cli_config_test.go index def93bbb8..8ef775163 100644 --- a/builder/amazon/common/cli_config_test.go +++ b/builder/amazon/common/cli_config_test.go @@ -2,7 +2,6 @@ package common import ( "io/ioutil" - "math/rand" "os" "path" "strconv" @@ -71,10 +70,8 @@ func TestAssumeRole(t *testing.T) { } func mockConfig(t *testing.T) string { - time := rand.NewSource(time.Now().UnixNano()) - r := rand.New(time) - v := r.Intn(9999999999) - dir, err := ioutil.TempDir("", strconv.Itoa(v)) + time := time.Now().UnixNano() + dir, err := ioutil.TempDir("", strconv.Itoa(time)) if err != nil { t.Error(err) } From e7711c31eaf4c2a1325a176e3db4099e9ed4b676 Mon Sep 17 00:00:00 2001 From: Christopher Gerber Date: Thu, 4 Feb 2016 16:18:57 -0600 Subject: [PATCH 9/9] Wrong function to convert int to string. --- builder/amazon/common/cli_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/cli_config_test.go b/builder/amazon/common/cli_config_test.go index 8ef775163..6a6dc7ed5 100644 --- a/builder/amazon/common/cli_config_test.go +++ b/builder/amazon/common/cli_config_test.go @@ -71,7 +71,7 @@ func TestAssumeRole(t *testing.T) { func mockConfig(t *testing.T) string { time := time.Now().UnixNano() - dir, err := ioutil.TempDir("", strconv.Itoa(time)) + dir, err := ioutil.TempDir("", strconv.FormatInt(time, 10)) if err != nil { t.Error(err) }