github.com/leeprovoost/terraform@v0.6.10-0.20160119085442-96f3f76118e7/builtin/providers/aws/config_test.go (about) 1 package aws 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "os" 10 "testing" 11 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 ) 14 15 func TestAWSConfig_shouldError(t *testing.T) { 16 resetEnv := unsetEnv(t) 17 defer resetEnv() 18 cfg := Config{} 19 20 c := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 21 _, err := c.Get() 22 if awsErr, ok := err.(awserr.Error); ok { 23 if awsErr.Code() != "NoCredentialProviders" { 24 t.Fatalf("Expected NoCredentialProviders error") 25 } 26 } 27 if err == nil { 28 t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config") 29 } 30 } 31 32 func TestAWSConfig_shouldBeStatic(t *testing.T) { 33 simple := []struct { 34 Key, Secret, Token string 35 }{ 36 { 37 Key: "test", 38 Secret: "secret", 39 }, { 40 Key: "test", 41 Secret: "test", 42 Token: "test", 43 }, 44 } 45 46 for _, c := range simple { 47 cfg := Config{ 48 AccessKey: c.Key, 49 SecretKey: c.Secret, 50 Token: c.Token, 51 } 52 53 creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 54 if creds == nil { 55 t.Fatalf("Expected a static creds provider to be returned") 56 } 57 v, err := creds.Get() 58 if err != nil { 59 t.Fatalf("Error gettings creds: %s", err) 60 } 61 if v.AccessKeyID != c.Key { 62 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) 63 } 64 if v.SecretAccessKey != c.Secret { 65 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) 66 } 67 if v.SessionToken != c.Token { 68 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) 69 } 70 } 71 } 72 73 // TestAWSConfig_shouldIAM is designed to test the scenario of running Terraform 74 // from an EC2 instance, without environment variables or manually supplied 75 // credentials. 76 func TestAWSConfig_shouldIAM(t *testing.T) { 77 // clear AWS_* environment variables 78 resetEnv := unsetEnv(t) 79 defer resetEnv() 80 81 // capture the test server's close method, to call after the test returns 82 ts := awsEnv(t) 83 defer ts() 84 85 // An empty config, no key supplied 86 cfg := Config{} 87 88 creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 89 if creds == nil { 90 t.Fatalf("Expected a static creds provider to be returned") 91 } 92 93 v, err := creds.Get() 94 if err != nil { 95 t.Fatalf("Error gettings creds: %s", err) 96 } 97 if v.AccessKeyID != "somekey" { 98 t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID) 99 } 100 if v.SecretAccessKey != "somesecret" { 101 t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey) 102 } 103 if v.SessionToken != "sometoken" { 104 t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken) 105 } 106 } 107 108 // TestAWSConfig_shouldIAM is designed to test the scenario of running Terraform 109 // from an EC2 instance, without environment variables or manually supplied 110 // credentials. 111 func TestAWSConfig_shouldIgnoreIAM(t *testing.T) { 112 resetEnv := unsetEnv(t) 113 defer resetEnv() 114 // capture the test server's close method, to call after the test returns 115 ts := awsEnv(t) 116 defer ts() 117 simple := []struct { 118 Key, Secret, Token string 119 }{ 120 { 121 Key: "test", 122 Secret: "secret", 123 }, { 124 Key: "test", 125 Secret: "test", 126 Token: "test", 127 }, 128 } 129 130 for _, c := range simple { 131 cfg := Config{ 132 AccessKey: c.Key, 133 SecretKey: c.Secret, 134 Token: c.Token, 135 } 136 137 creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 138 if creds == nil { 139 t.Fatalf("Expected a static creds provider to be returned") 140 } 141 v, err := creds.Get() 142 if err != nil { 143 t.Fatalf("Error gettings creds: %s", err) 144 } 145 if v.AccessKeyID != c.Key { 146 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) 147 } 148 if v.SecretAccessKey != c.Secret { 149 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) 150 } 151 if v.SessionToken != c.Token { 152 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) 153 } 154 } 155 } 156 157 var credentialsFileContents = `[myprofile] 158 aws_access_key_id = accesskey 159 aws_secret_access_key = secretkey 160 ` 161 162 func TestAWSConfig_shouldBeShared(t *testing.T) { 163 file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred") 164 if err != nil { 165 t.Fatalf("Error writing temporary credentials file: %s", err) 166 } 167 _, err = file.WriteString(credentialsFileContents) 168 if err != nil { 169 t.Fatalf("Error writing temporary credentials to file: %s", err) 170 } 171 err = file.Close() 172 if err != nil { 173 t.Fatalf("Error closing temporary credentials file: %s", err) 174 } 175 176 defer os.Remove(file.Name()) 177 178 resetEnv := unsetEnv(t) 179 defer resetEnv() 180 181 if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil { 182 t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) 183 } 184 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil { 185 t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 186 } 187 188 creds := getCreds("", "", "", "myprofile", file.Name()) 189 if creds == nil { 190 t.Fatalf("Expected a provider chain to be returned") 191 } 192 v, err := creds.Get() 193 if err != nil { 194 t.Fatalf("Error gettings creds: %s", err) 195 } 196 197 if v.AccessKeyID != "accesskey" { 198 t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) 199 } 200 201 if v.SecretAccessKey != "secretkey" { 202 t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) 203 } 204 } 205 206 func TestAWSConfig_shouldBeENV(t *testing.T) { 207 // need to set the environment variables to a dummy string, as we don't know 208 // what they may be at runtime without hardcoding here 209 s := "some_env" 210 resetEnv := setEnv(s, t) 211 212 defer resetEnv() 213 214 cfg := Config{} 215 creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 216 if creds == nil { 217 t.Fatalf("Expected a static creds provider to be returned") 218 } 219 v, err := creds.Get() 220 if err != nil { 221 t.Fatalf("Error gettings creds: %s", err) 222 } 223 if v.AccessKeyID != s { 224 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID) 225 } 226 if v.SecretAccessKey != s { 227 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey) 228 } 229 if v.SessionToken != s { 230 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken) 231 } 232 } 233 234 // unsetEnv unsets enviornment variables for testing a "clean slate" with no 235 // credentials in the environment 236 func unsetEnv(t *testing.T) func() { 237 // Grab any existing AWS keys and preserve. In some tests we'll unset these, so 238 // we need to have them and restore them after 239 e := getEnv() 240 if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil { 241 t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err) 242 } 243 if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil { 244 t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err) 245 } 246 if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil { 247 t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err) 248 } 249 if err := os.Unsetenv("AWS_PROFILE"); err != nil { 250 t.Fatalf("Error unsetting env var AWS_TOKEN: %s", err) 251 } 252 if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil { 253 t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 254 } 255 256 return func() { 257 // re-set all the envs we unset above 258 if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { 259 t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) 260 } 261 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { 262 t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) 263 } 264 if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { 265 t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) 266 } 267 if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { 268 t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) 269 } 270 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil { 271 t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 272 } 273 } 274 } 275 276 func setEnv(s string, t *testing.T) func() { 277 e := getEnv() 278 // Set all the envs to a dummy value 279 if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil { 280 t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err) 281 } 282 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil { 283 t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err) 284 } 285 if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil { 286 t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err) 287 } 288 if err := os.Setenv("AWS_PROFILE", s); err != nil { 289 t.Fatalf("Error setting env var AWS_PROFILE: %s", err) 290 } 291 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { 292 t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) 293 } 294 295 return func() { 296 // re-set all the envs we unset above 297 if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { 298 t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) 299 } 300 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { 301 t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) 302 } 303 if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { 304 t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) 305 } 306 if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { 307 t.Fatalf("Error setting env var AWS_PROFILE: %s", err) 308 } 309 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { 310 t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) 311 } 312 } 313 } 314 315 // awsEnv establishes a httptest server to mock out the internal AWS Metadata 316 // service. IAM Credentials are retrieved by the EC2RoleProvider, which makes 317 // API calls to this internal URL. By replacing the server with a test server, 318 // we can simulate an AWS environment 319 func awsEnv(t *testing.T) func() { 320 routes := routes{} 321 if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil { 322 t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) 323 } 324 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 325 w.Header().Set("Content-Type", "text/plain") 326 w.Header().Add("Server", "MockEC2") 327 for _, e := range routes.Endpoints { 328 if r.RequestURI == e.Uri { 329 fmt.Fprintln(w, e.Body) 330 } 331 } 332 })) 333 334 os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") 335 return ts.Close 336 } 337 338 func getEnv() *currentEnv { 339 // Grab any existing AWS keys and preserve. In some tests we'll unset these, so 340 // we need to have them and restore them after 341 return ¤tEnv{ 342 Key: os.Getenv("AWS_ACCESS_KEY_ID"), 343 Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"), 344 Token: os.Getenv("AWS_SESSION_TOKEN"), 345 Profile: os.Getenv("AWS_TOKEN"), 346 CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), 347 } 348 } 349 350 // struct to preserve the current environment 351 type currentEnv struct { 352 Key, Secret, Token, Profile, CredsFilename string 353 } 354 355 type routes struct { 356 Endpoints []*endpoint `json:"endpoints"` 357 } 358 type endpoint struct { 359 Uri string `json:"uri"` 360 Body string `json:"body"` 361 } 362 363 const aws_routes = ` 364 { 365 "endpoints": [ 366 { 367 "uri": "/latest/meta-data/iam/security-credentials", 368 "body": "test_role" 369 }, 370 { 371 "uri": "/latest/meta-data/iam/security-credentials/test_role", 372 "body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}" 373 } 374 ] 375 } 376 `