github.com/aavshr/aws-sdk-go@v1.41.3/aws/ec2metadata/api_test.go (about) 1 //go:build go1.7 2 // +build go1.7 3 4 package ec2metadata_test 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "net/http/httptest" 13 "path" 14 "reflect" 15 "strings" 16 "sync" 17 "sync/atomic" 18 "testing" 19 "time" 20 21 "github.com/aavshr/aws-sdk-go/aws" 22 "github.com/aavshr/aws-sdk-go/aws/awserr" 23 "github.com/aavshr/aws-sdk-go/aws/ec2metadata" 24 "github.com/aavshr/aws-sdk-go/aws/request" 25 "github.com/aavshr/aws-sdk-go/awstesting/unit" 26 "github.com/aavshr/aws-sdk-go/internal/sdktesting" 27 ) 28 29 const instanceIdentityDocument = `{ 30 "devpayProductCodes" : null, 31 "marketplaceProductCodes" : [ "1abc2defghijklm3nopqrs4tu" ], 32 "availabilityZone" : "us-east-1d", 33 "privateIp" : "10.158.112.84", 34 "version" : "2010-08-31", 35 "region" : "us-east-1", 36 "instanceId" : "i-1234567890abcdef0", 37 "billingProducts" : null, 38 "instanceType" : "t1.micro", 39 "accountId" : "123456789012", 40 "pendingTime" : "2015-11-19T16:32:11Z", 41 "imageId" : "ami-5fb8c835", 42 "kernelId" : "aki-919dcaf8", 43 "ramdiskId" : null, 44 "architecture" : "x86_64" 45 }` 46 47 const validIamInfo = `{ 48 "Code" : "Success", 49 "LastUpdated" : "2016-03-17T12:27:32Z", 50 "InstanceProfileArn" : "arn:aws:iam::123456789012:instance-profile/my-instance-profile", 51 "InstanceProfileId" : "AIPAABCDEFGHIJKLMN123" 52 }` 53 54 const unsuccessfulIamInfo = `{ 55 "Code" : "Failed", 56 "LastUpdated" : "2016-03-17T12:27:32Z", 57 "InstanceProfileArn" : "arn:aws:iam::123456789012:instance-profile/my-instance-profile", 58 "InstanceProfileId" : "AIPAABCDEFGHIJKLMN123" 59 }` 60 61 const ( 62 ttlHeader = "x-aws-ec2-metadata-token-ttl-seconds" 63 tokenHeader = "x-aws-ec2-metadata-token" 64 ) 65 66 type testType int 67 68 const ( 69 SecureTestType testType = iota 70 InsecureTestType 71 BadRequestTestType 72 NotFoundRequestTestType 73 InvalidTokenRequestTestType 74 ServerErrorForTokenTestType 75 pageNotFoundForTokenTestType 76 pageNotFoundWith401TestType 77 ) 78 79 type testServer struct { 80 t *testing.T 81 82 tokens []string 83 activeToken atomic.Value 84 data string 85 } 86 87 type operationListProvider struct { 88 operationsPerformed []string 89 } 90 91 func getTokenRequiredParams(t *testing.T, fn http.HandlerFunc) http.HandlerFunc { 92 return func(w http.ResponseWriter, r *http.Request) { 93 if e, a := "PUT", r.Method; e != a { 94 t.Errorf("expect %v, http method got %v", e, a) 95 http.Error(w, "wrong method", 400) 96 return 97 } 98 if len(r.Header.Get(ttlHeader)) == 0 { 99 t.Errorf("expect ttl header to be present in the request headers, got none") 100 http.Error(w, "wrong method", 400) 101 return 102 } 103 104 fn(w, r) 105 } 106 } 107 108 func newTestServer(t *testing.T, testType testType, testServer *testServer) *httptest.Server { 109 mux := http.NewServeMux() 110 switch testType { 111 case SecureTestType: 112 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.secureGetTokenHandler)) 113 mux.HandleFunc("/", testServer.secureGetLatestHandler) 114 case InsecureTestType: 115 mux.HandleFunc("/latest/api/token", testServer.insecureGetTokenHandler) 116 mux.HandleFunc("/", testServer.insecureGetLatestHandler) 117 case BadRequestTestType: 118 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.badRequestGetTokenHandler)) 119 mux.HandleFunc("/", testServer.badRequestGetLatestHandler) 120 case NotFoundRequestTestType: 121 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.secureGetTokenHandler)) 122 mux.HandleFunc("/", testServer.notFoundRequestGetLatestHandler) 123 case InvalidTokenRequestTestType: 124 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.secureGetTokenHandler)) 125 mux.HandleFunc("/", testServer.unauthorizedGetLatestHandler) 126 case ServerErrorForTokenTestType: 127 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.serverErrorGetTokenHandler)) 128 mux.HandleFunc("/", testServer.insecureGetLatestHandler) 129 case pageNotFoundForTokenTestType: 130 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.pageNotFoundGetTokenHandler)) 131 mux.HandleFunc("/", testServer.insecureGetLatestHandler) 132 case pageNotFoundWith401TestType: 133 mux.HandleFunc("/latest/api/token", getTokenRequiredParams(t, testServer.pageNotFoundGetTokenHandler)) 134 mux.HandleFunc("/", testServer.unauthorizedGetLatestHandler) 135 136 } 137 138 return httptest.NewServer(mux) 139 } 140 141 func (s *testServer) secureGetTokenHandler(w http.ResponseWriter, r *http.Request) { 142 token := s.tokens[0] 143 144 // set the active token 145 s.activeToken.Store(token) 146 147 // rotate the token 148 if len(s.tokens) > 1 { 149 s.tokens = s.tokens[1:] 150 } 151 152 // set the header and response body 153 w.Header().Set(ttlHeader, r.Header.Get(ttlHeader)) 154 if activeToken, ok := s.activeToken.Load().(string); ok { 155 w.Write([]byte(activeToken)) 156 } else { 157 s.t.Fatalf("Expected activeToken to be of type string, got %v", activeToken) 158 } 159 } 160 161 func (s *testServer) secureGetLatestHandler(w http.ResponseWriter, r *http.Request) { 162 if s.activeToken.Load() == nil { 163 s.t.Errorf("expect token to have been requested, was not") 164 http.Error(w, "", 401) 165 return 166 } 167 168 if e, a := s.activeToken.Load(), r.Header.Get(tokenHeader); e != a { 169 s.t.Errorf("expect %v token, got %v", e, a) 170 http.Error(w, "", 401) 171 return 172 } 173 174 w.Header().Set(ttlHeader, r.Header.Get(ttlHeader)) 175 w.Write([]byte(s.data)) 176 } 177 178 func (s *testServer) insecureGetTokenHandler(w http.ResponseWriter, r *http.Request) { 179 http.Error(w, "", 404) 180 } 181 182 func (s *testServer) insecureGetLatestHandler(w http.ResponseWriter, r *http.Request) { 183 if len(r.Header.Get(tokenHeader)) != 0 { 184 s.t.Errorf("Request token found, expected none") 185 http.Error(w, "", 400) 186 return 187 } 188 189 w.Write([]byte(s.data)) 190 } 191 192 func (s *testServer) badRequestGetTokenHandler(w http.ResponseWriter, r *http.Request) { 193 http.Error(w, "", 400) 194 } 195 196 func (s *testServer) badRequestGetLatestHandler(w http.ResponseWriter, r *http.Request) { 197 s.t.Errorf("Expected no call to this handler, incorrect behavior found") 198 } 199 200 func (s *testServer) notFoundRequestGetLatestHandler(w http.ResponseWriter, r *http.Request) { 201 http.Error(w, "not found error", 404) 202 } 203 204 func (s *testServer) serverErrorGetTokenHandler(w http.ResponseWriter, r *http.Request) { 205 http.Error(w, "", 403) 206 } 207 208 func (s *testServer) pageNotFoundGetTokenHandler(w http.ResponseWriter, r *http.Request) { 209 http.Error(w, "Page not found error", 404) 210 } 211 212 func (s *testServer) unauthorizedGetLatestHandler(w http.ResponseWriter, r *http.Request) { 213 http.Error(w, "", 401) 214 } 215 216 func (opListProvider *operationListProvider) addToOperationPerformedList(r *request.Request) { 217 opListProvider.operationsPerformed = append(opListProvider.operationsPerformed, r.Operation.Name) 218 } 219 220 func TestEndpoint(t *testing.T) { 221 restoreEnvFn := sdktesting.StashEnv() 222 defer restoreEnvFn() 223 224 c := ec2metadata.New(unit.Session) 225 op := &request.Operation{ 226 Name: "GetMetadata", 227 HTTPMethod: "GET", 228 HTTPPath: path.Join("/latest", "meta-data", "testpath"), 229 } 230 231 req := c.NewRequest(op, nil, nil) 232 if e, a := "http://169.254.169.254/latest/meta-data/testpath", req.HTTPRequest.URL.String(); e != a { 233 t.Errorf("expect %v, got %v", e, a) 234 } 235 } 236 237 func TestGetMetadata(t *testing.T) { 238 cases := map[string]struct { 239 tokens []string 240 NewServer func(t *testing.T, tokens []string) *httptest.Server 241 expectedData string 242 expectedError string 243 expectedOperationsAttempted []string 244 }{ 245 "Insecure server success case": { 246 NewServer: func(t *testing.T, tokens []string) *httptest.Server { 247 testType := InsecureTestType 248 Ts := &testServer{ 249 t: t, 250 tokens: tokens, 251 data: "IMDSProfileForGoSDK", 252 } 253 return newTestServer(t, testType, Ts) 254 }, 255 expectedData: "IMDSProfileForGoSDK", 256 expectedOperationsAttempted: []string{"GetToken", "GetMetadata", "GetMetadata"}, 257 }, 258 "Secure server success case": { 259 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 260 NewServer: func(t *testing.T, tokens []string) *httptest.Server { 261 testType := SecureTestType 262 Ts := &testServer{ 263 t: t, 264 tokens: tokens, 265 data: "IMDSProfileForGoSDK", 266 } 267 return newTestServer(t, testType, Ts) 268 }, 269 expectedData: "IMDSProfileForGoSDK", 270 expectedError: "", 271 expectedOperationsAttempted: []string{"GetToken", "GetMetadata", "GetMetadata"}, 272 }, 273 "Bad token request case": { 274 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 275 NewServer: func(t *testing.T, tokens []string) *httptest.Server { 276 testType := BadRequestTestType 277 Ts := &testServer{ 278 t: t, 279 tokens: tokens, 280 data: "IMDSProfileForGoSDK", 281 } 282 return newTestServer(t, testType, Ts) 283 }, 284 expectedError: "400", 285 expectedOperationsAttempted: []string{"GetToken", "GetToken"}, 286 }, 287 "Not found no retry request case": { 288 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 289 NewServer: func(t *testing.T, tokens []string) *httptest.Server { 290 testType := NotFoundRequestTestType 291 Ts := &testServer{ 292 t: t, 293 tokens: tokens, 294 data: "IMDSProfileForGoSDK", 295 } 296 return newTestServer(t, testType, Ts) 297 }, 298 expectedError: "404", 299 expectedOperationsAttempted: []string{"GetToken", "GetMetadata", "GetMetadata"}, 300 }, 301 "invalid token request case": { 302 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 303 NewServer: func(t *testing.T, tokens []string) *httptest.Server { 304 testType := InvalidTokenRequestTestType 305 Ts := &testServer{ 306 t: t, 307 tokens: tokens, 308 data: "IMDSProfileForGoSDK", 309 } 310 return newTestServer(t, testType, Ts) 311 }, 312 expectedError: "401", 313 expectedOperationsAttempted: []string{"GetToken", "GetMetadata", "GetToken", "GetMetadata"}, 314 }, 315 "ServerErrorForTokenTestType": { 316 NewServer: func(t *testing.T, tokens []string) *httptest.Server { 317 testType := ServerErrorForTokenTestType 318 Ts := &testServer{ 319 t: t, 320 tokens: []string{}, 321 data: "IMDSProfileForGoSDK", 322 } 323 return newTestServer(t, testType, Ts) 324 }, 325 expectedData: "IMDSProfileForGoSDK", 326 expectedOperationsAttempted: []string{"GetToken", "GetMetadata", "GetMetadata"}, 327 }, 328 } 329 330 for name, x := range cases { 331 t.Run(name, func(t *testing.T) { 332 333 server := x.NewServer(t, x.tokens) 334 defer server.Close() 335 336 op := &operationListProvider{} 337 338 c := ec2metadata.New(unit.Session, &aws.Config{ 339 Endpoint: aws.String(server.URL), 340 }) 341 c.Handlers.CompleteAttempt.PushBack(op.addToOperationPerformedList) 342 343 tokenCounter := -1 344 c.Handlers.Send.PushBack(func(r *request.Request) { 345 switch r.Operation.Name { 346 case "GetToken": 347 tokenCounter++ 348 349 case "GetMetadata": 350 curToken := r.HTTPRequest.Header.Get("x-aws-ec2-metadata-token") 351 if len(curToken) != 0 && curToken != x.tokens[tokenCounter] { 352 t.Errorf("expect %v token, got %v", x.tokens[tokenCounter], curToken) 353 } 354 } 355 }) 356 357 resp, err := c.GetMetadata("some/path") 358 359 // token should stay alive, since default duration is 26000 seconds 360 resp, err = c.GetMetadata("some/path") 361 362 if len(x.expectedError) != 0 { 363 if err == nil { 364 t.Fatalf("expect %v error, got none", x.expectedError) 365 } 366 if e, a := x.expectedError, err.Error(); !strings.Contains(a, e) { 367 t.Fatalf("expect %v error, got %v", e, a) 368 } 369 } else if err != nil { 370 t.Fatalf("expect no error, got %v", err) 371 } 372 373 if e, a := x.expectedData, resp; e != a { 374 t.Fatalf("expect %v, got %v", e, a) 375 } 376 377 if e, a := x.expectedOperationsAttempted, op.operationsPerformed; !reflect.DeepEqual(e, a) { 378 t.Errorf("expect %v operations, got %v", e, a) 379 } 380 381 }) 382 } 383 } 384 385 func TestGetUserData_Error(t *testing.T) { 386 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 387 reader := strings.NewReader(`<?xml version="1.0" encoding="iso-8859-1"?> 388 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 389 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 390 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 391 <head> 392 <title>404 - Not Found</title> 393 </head> 394 <body> 395 <h1>404 - Not Found</h1> 396 </body> 397 </html>`) 398 w.Header().Set("Content-Type", "text/html") 399 w.Header().Set("Content-Length", fmt.Sprintf("%d", reader.Len())) 400 w.WriteHeader(http.StatusNotFound) 401 io.Copy(w, reader) 402 })) 403 404 defer server.Close() 405 c := ec2metadata.New(unit.Session, &aws.Config{ 406 Endpoint: aws.String(server.URL), 407 }) 408 409 resp, err := c.GetUserData() 410 if err == nil { 411 t.Fatalf("expect error") 412 } 413 if len(resp) != 0 { 414 t.Fatalf("expect empty, got %v", resp) 415 } 416 417 if requestFailedError, ok := err.(awserr.RequestFailure); ok { 418 if e, a := http.StatusNotFound, requestFailedError.StatusCode(); e != a { 419 t.Fatalf("expect %v, got %v", e, a) 420 } 421 } 422 } 423 424 func TestGetRegion(t *testing.T) { 425 cases := map[string]struct { 426 NewServer func(t *testing.T) *httptest.Server 427 expectedData string 428 expectedError string 429 expectedOperationsPerformed []string 430 }{ 431 "Insecure server success case": { 432 NewServer: func(t *testing.T) *httptest.Server { 433 testType := InsecureTestType 434 Ts := &testServer{ 435 t: t, 436 data: instanceIdentityDocument, 437 } 438 return newTestServer(t, testType, Ts) 439 }, 440 expectedData: "us-east-1", 441 expectedOperationsPerformed: []string{"GetToken", "GetDynamicData"}, 442 }, 443 "Secure server success case": { 444 NewServer: func(t *testing.T) *httptest.Server { 445 testType := SecureTestType 446 Ts := &testServer{ 447 t: t, 448 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 449 data: instanceIdentityDocument, 450 } 451 return newTestServer(t, testType, Ts) 452 }, 453 expectedData: "us-east-1", 454 expectedOperationsPerformed: []string{"GetToken", "GetDynamicData"}, 455 }, 456 "Bad request case": { 457 NewServer: func(t *testing.T) *httptest.Server { 458 testType := BadRequestTestType 459 Ts := &testServer{ 460 t: t, 461 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 462 data: instanceIdentityDocument, 463 } 464 return newTestServer(t, testType, Ts) 465 }, 466 expectedError: "400", 467 expectedOperationsPerformed: []string{"GetToken", "GetDynamicData"}, 468 }, 469 "ServerErrorForTokenTestType": { 470 NewServer: func(t *testing.T) *httptest.Server { 471 testType := ServerErrorForTokenTestType 472 Ts := &testServer{ 473 t: t, 474 tokens: []string{}, 475 data: instanceIdentityDocument, 476 } 477 return newTestServer(t, testType, Ts) 478 }, 479 expectedData: "us-east-1", 480 expectedOperationsPerformed: []string{"GetToken", "GetDynamicData"}, 481 }, 482 } 483 484 for name, x := range cases { 485 t.Run(name, func(t *testing.T) { 486 487 server := x.NewServer(t) 488 defer server.Close() 489 490 op := &operationListProvider{} 491 492 c := ec2metadata.New(unit.Session, &aws.Config{ 493 Endpoint: aws.String(server.URL), 494 }) 495 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 496 497 resp, err := c.Region() 498 499 if len(x.expectedError) != 0 { 500 if err == nil { 501 t.Fatalf("expect %v error, got none", x.expectedError) 502 } 503 if e, a := x.expectedError, err.Error(); !strings.Contains(a, e) { 504 t.Fatalf("expect %v error, got %v", e, a) 505 } 506 } else if err != nil { 507 t.Fatalf("expect no error, got %v", err) 508 } 509 510 if e, a := x.expectedData, resp; e != a { 511 t.Fatalf("expect %v, got %v", e, a) 512 } 513 514 if e, a := x.expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 515 t.Fatalf("expect %v operations, got %v", e, a) 516 } 517 }) 518 } 519 } 520 521 func TestMetadataIAMInfo_success(t *testing.T) { 522 cases := map[string]struct { 523 NewServer func(t *testing.T) *httptest.Server 524 expectedData string 525 expectedError string 526 expectedOperationsPerformed []string 527 }{ 528 "Insecure server success case": { 529 NewServer: func(t *testing.T) *httptest.Server { 530 testType := InsecureTestType 531 Ts := &testServer{ 532 t: t, 533 data: validIamInfo, 534 } 535 return newTestServer(t, testType, Ts) 536 }, 537 expectedData: validIamInfo, 538 expectedOperationsPerformed: []string{"GetToken", "GetMetadata"}, 539 }, 540 "Secure server success case": { 541 NewServer: func(t *testing.T) *httptest.Server { 542 testType := SecureTestType 543 Ts := &testServer{ 544 t: t, 545 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 546 data: validIamInfo, 547 } 548 return newTestServer(t, testType, Ts) 549 }, 550 expectedData: validIamInfo, 551 expectedOperationsPerformed: []string{"GetToken", "GetMetadata"}, 552 }, 553 } 554 555 for name, x := range cases { 556 t.Run(name, func(t *testing.T) { 557 558 server := x.NewServer(t) 559 defer server.Close() 560 561 op := &operationListProvider{} 562 563 c := ec2metadata.New(unit.Session, &aws.Config{ 564 Endpoint: aws.String(server.URL), 565 }) 566 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 567 568 iamInfo, err := c.IAMInfo() 569 570 if len(x.expectedError) != 0 { 571 if err == nil { 572 t.Fatalf("expect %v error, got none", x.expectedError) 573 } 574 if e, a := x.expectedError, err.Error(); !strings.Contains(a, e) { 575 t.Fatalf("expect %v error, got %v", e, a) 576 } 577 } else if err != nil { 578 t.Fatalf("expect no error, got %v", err) 579 } 580 581 if e, a := "Success", iamInfo.Code; e != a { 582 t.Fatalf("expect %v, got %v", e, a) 583 } 584 if e, a := "arn:aws:iam::123456789012:instance-profile/my-instance-profile", iamInfo.InstanceProfileArn; e != a { 585 t.Fatalf("expect %v, got %v", e, a) 586 } 587 if e, a := "AIPAABCDEFGHIJKLMN123", iamInfo.InstanceProfileID; e != a { 588 t.Fatalf("expect %v, got %v", e, a) 589 } 590 591 if e, a := x.expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 592 t.Fatalf("expect %v operations, got %v", e, a) 593 } 594 }) 595 } 596 } 597 598 func TestMetadataIAMInfo_failure(t *testing.T) { 599 cases := map[string]struct { 600 NewServer func(t *testing.T) *httptest.Server 601 expectedData string 602 expectedError string 603 expectedOperationsPerformed []string 604 }{ 605 "Insecure server success case": { 606 NewServer: func(t *testing.T) *httptest.Server { 607 testType := InsecureTestType 608 Ts := &testServer{ 609 t: t, 610 tokens: nil, 611 data: unsuccessfulIamInfo, 612 } 613 return newTestServer(t, testType, Ts) 614 }, 615 expectedData: unsuccessfulIamInfo, 616 expectedOperationsPerformed: []string{"GetToken", "GetMetadata"}, 617 }, 618 "Secure server success case": { 619 NewServer: func(t *testing.T) *httptest.Server { 620 testType := SecureTestType 621 Ts := &testServer{ 622 t: t, 623 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 624 data: unsuccessfulIamInfo, 625 } 626 return newTestServer(t, testType, Ts) 627 }, 628 expectedData: unsuccessfulIamInfo, 629 expectedOperationsPerformed: []string{"GetToken", "GetMetadata"}, 630 }, 631 } 632 633 for name, x := range cases { 634 t.Run(name, func(t *testing.T) { 635 636 server := x.NewServer(t) 637 defer server.Close() 638 639 op := &operationListProvider{} 640 641 c := ec2metadata.New(unit.Session, &aws.Config{ 642 Endpoint: aws.String(server.URL), 643 }) 644 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 645 646 iamInfo, err := c.IAMInfo() 647 if err == nil { 648 t.Fatalf("expect error") 649 } 650 if e, a := "", iamInfo.Code; e != a { 651 t.Fatalf("expect %v, got %v", e, a) 652 } 653 if e, a := "", iamInfo.InstanceProfileArn; e != a { 654 t.Fatalf("expect %v, got %v", e, a) 655 } 656 if e, a := "", iamInfo.InstanceProfileID; e != a { 657 t.Fatalf("expect %v, got %v", e, a) 658 } 659 if e, a := x.expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 660 t.Fatalf("expect %v operations, got %v", e, a) 661 } 662 }) 663 } 664 } 665 666 func TestMetadataNotAvailable(t *testing.T) { 667 c := ec2metadata.New(unit.Session) 668 c.Handlers.Send.Clear() 669 c.Handlers.Send.PushBack(func(r *request.Request) { 670 r.HTTPResponse = &http.Response{ 671 StatusCode: int(0), 672 Status: http.StatusText(int(0)), 673 Body: ioutil.NopCloser(bytes.NewReader([]byte{})), 674 } 675 r.Error = awserr.New(request.ErrCodeRequestError, "send request failed", nil) 676 r.Retryable = aws.Bool(true) // network errors are retryable 677 }) 678 679 if c.Available() { 680 t.Fatalf("expect not available") 681 } 682 } 683 684 func TestMetadataErrorResponse(t *testing.T) { 685 c := ec2metadata.New(unit.Session) 686 c.Handlers.Send.Clear() 687 c.Handlers.Send.PushBack(func(r *request.Request) { 688 r.HTTPResponse = &http.Response{ 689 StatusCode: http.StatusBadRequest, 690 Status: http.StatusText(http.StatusBadRequest), 691 Body: ioutil.NopCloser(strings.NewReader("error message text")), 692 } 693 r.Retryable = aws.Bool(false) // network errors are retryable 694 }) 695 696 data, err := c.GetMetadata("uri/path") 697 if e, a := "error message text", err.Error(); !strings.Contains(a, e) { 698 t.Fatalf("expect %v to be in %v", e, a) 699 } 700 if len(data) != 0 { 701 t.Fatalf("expect empty, got %v", data) 702 } 703 704 } 705 706 func TestEC2RoleProviderInstanceIdentity(t *testing.T) { 707 cases := map[string]struct { 708 NewServer func(t *testing.T) *httptest.Server 709 expectedData string 710 expectedOperationsPerformed []string 711 }{ 712 "Insecure server success case": { 713 NewServer: func(t *testing.T) *httptest.Server { 714 testType := InsecureTestType 715 Ts := &testServer{ 716 t: t, 717 tokens: nil, 718 data: instanceIdentityDocument, 719 } 720 return newTestServer(t, testType, Ts) 721 }, 722 expectedData: instanceIdentityDocument, 723 expectedOperationsPerformed: []string{"GetToken", "GetDynamicData"}, 724 }, 725 "Secure server success case": { 726 NewServer: func(t *testing.T) *httptest.Server { 727 testType := SecureTestType 728 Ts := &testServer{ 729 t: t, 730 tokens: []string{"firstToken", "secondToken", "thirdToken"}, 731 data: instanceIdentityDocument, 732 } 733 return newTestServer(t, testType, Ts) 734 }, 735 expectedData: instanceIdentityDocument, 736 expectedOperationsPerformed: []string{"GetToken", "GetDynamicData"}, 737 }, 738 } 739 740 for name, x := range cases { 741 t.Run(name, func(t *testing.T) { 742 743 server := x.NewServer(t) 744 defer server.Close() 745 746 op := &operationListProvider{} 747 748 c := ec2metadata.New(unit.Session, &aws.Config{ 749 Endpoint: aws.String(server.URL), 750 }) 751 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 752 doc, err := c.GetInstanceIdentityDocument() 753 754 if err != nil { 755 t.Fatalf("expected no error, got %v", err) 756 } 757 758 if e, a := doc.AccountID, "123456789012"; e != a { 759 t.Fatalf("expect %v, got %v", e, a) 760 } 761 if e, a := doc.AvailabilityZone, "us-east-1d"; e != a { 762 t.Fatalf("expect %v, got %v", e, a) 763 } 764 if e, a := doc.Region, "us-east-1"; e != a { 765 t.Fatalf("expect %v, got %v", e, a) 766 } 767 if e, a := x.expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 768 t.Fatalf("expect %v operations, got %v", e, a) 769 } 770 }) 771 } 772 } 773 774 func TestEC2MetadataRetryFailure(t *testing.T) { 775 mux := http.NewServeMux() 776 777 mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, r *http.Request) { 778 if r.Method == "PUT" && r.Header.Get(ttlHeader) != "" { 779 w.Header().Set(ttlHeader, "200") 780 http.Error(w, "service unavailable", http.StatusServiceUnavailable) 781 return 782 } 783 http.Error(w, "bad request", http.StatusBadRequest) 784 }) 785 786 // meta-data endpoint for this test, just returns the token 787 mux.HandleFunc("/latest/meta-data/", func(w http.ResponseWriter, r *http.Request) { 788 w.Write([]byte("profile_name")) 789 }) 790 791 server := httptest.NewServer(mux) 792 defer server.Close() 793 794 c := ec2metadata.New(unit.Session, &aws.Config{ 795 Endpoint: aws.String(server.URL), 796 }) 797 798 c.Handlers.AfterRetry.PushBack(func(i *request.Request) { 799 t.Logf("%v received, retrying operation %v", i.HTTPResponse.StatusCode, i.Operation.Name) 800 }) 801 c.Handlers.Complete.PushBack(func(i *request.Request) { 802 t.Logf("%v operation exited with status %v", i.Operation.Name, i.HTTPResponse.StatusCode) 803 }) 804 805 resp, err := c.GetMetadata("some/path") 806 if err != nil { 807 t.Fatalf("Expected none, got error %v", err) 808 } 809 if resp != "profile_name" { 810 t.Fatalf("Expected response to be profile_name, got %v", resp) 811 } 812 813 resp, err = c.GetMetadata("some/path") 814 if err != nil { 815 t.Fatalf("Expected none, got error %v", err) 816 } 817 if resp != "profile_name" { 818 t.Fatalf("Expected response to be profile_name, got %v", resp) 819 } 820 } 821 822 func TestEC2MetadataRetryOnce(t *testing.T) { 823 var secureDataFlow bool 824 var retry = true 825 mux := http.NewServeMux() 826 827 mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, r *http.Request) { 828 if r.Method == "PUT" && r.Header.Get(ttlHeader) != "" { 829 w.Header().Set(ttlHeader, "200") 830 for retry { 831 retry = false 832 http.Error(w, "service unavailable", http.StatusServiceUnavailable) 833 return 834 } 835 w.Write([]byte("token")) 836 secureDataFlow = true 837 return 838 } 839 http.Error(w, "bad request", http.StatusBadRequest) 840 }) 841 842 // meta-data endpoint for this test, just returns the token 843 mux.HandleFunc("/latest/meta-data/", func(w http.ResponseWriter, r *http.Request) { 844 w.Write([]byte(r.Header.Get(tokenHeader))) 845 }) 846 847 var tokenRetryCount int 848 849 server := httptest.NewServer(mux) 850 defer server.Close() 851 c := ec2metadata.New(unit.Session, &aws.Config{ 852 Endpoint: aws.String(server.URL), 853 }) 854 855 // Handler on client that logs if retried 856 c.Handlers.AfterRetry.PushBack(func(i *request.Request) { 857 t.Logf("%v received, retrying operation %v", i.HTTPResponse.StatusCode, i.Operation.Name) 858 tokenRetryCount++ 859 }) 860 861 _, err := c.GetMetadata("some/path") 862 863 if tokenRetryCount != 1 { 864 t.Fatalf("Expected number of retries for fetching token to be 1, got %v", tokenRetryCount) 865 } 866 867 if !secureDataFlow { 868 t.Fatalf("Expected secure data flow to be %v, got %v", secureDataFlow, !secureDataFlow) 869 } 870 871 if err != nil { 872 t.Fatalf("Expected none, got error %v", err) 873 } 874 } 875 876 func TestEC2Metadata_Concurrency(t *testing.T) { 877 ts := &testServer{ 878 t: t, 879 tokens: []string{"firstToken"}, 880 data: "IMDSProfileForSDKGo", 881 } 882 883 server := newTestServer(t, SecureTestType, ts) 884 defer server.Close() 885 886 c := ec2metadata.New(unit.Session, &aws.Config{ 887 Endpoint: aws.String(server.URL), 888 }) 889 890 var wg sync.WaitGroup 891 wg.Add(10) 892 for i := 0; i < 10; i++ { 893 go func() { 894 defer wg.Done() 895 for j := 0; j < 10; j++ { 896 resp, err := c.GetMetadata("some/data") 897 if err != nil { 898 t.Errorf("expect no error, got %v", err) 899 } 900 901 if e, a := "IMDSProfileForSDKGo", resp; e != a { 902 t.Errorf("expect %v, got %v", e, a) 903 } 904 } 905 }() 906 } 907 wg.Wait() 908 } 909 910 func TestRequestOnMetadata(t *testing.T) { 911 ts := &testServer{ 912 t: t, 913 tokens: []string{"firstToken", "secondToken"}, 914 data: "profile_name", 915 } 916 server := newTestServer(t, SecureTestType, ts) 917 defer server.Close() 918 919 c := ec2metadata.New(unit.Session, &aws.Config{ 920 Endpoint: aws.String(server.URL), 921 }) 922 req := c.NewRequest(&request.Operation{ 923 Name: "Ec2Metadata request", 924 HTTPMethod: "GET", 925 HTTPPath: "/latest/foo", 926 Paginator: nil, 927 BeforePresignFn: nil, 928 }, nil, nil) 929 930 op := &operationListProvider{} 931 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 932 err := req.Send() 933 934 if err != nil { 935 t.Fatalf("expect no error, got %v", err) 936 } 937 938 if len(op.operationsPerformed) < 1 { 939 t.Fatalf("Expected atleast one operation GetToken to be called on EC2Metadata client") 940 return 941 } 942 943 if op.operationsPerformed[0] != "GetToken" { 944 t.Fatalf("Expected GetToken operation to be called") 945 } 946 947 } 948 949 func TestExhaustiveRetryToFetchToken(t *testing.T) { 950 ts := &testServer{ 951 t: t, 952 tokens: []string{"firstToken", "secondToken"}, 953 data: "IMDSProfileForSDKGo", 954 } 955 956 server := newTestServer(t, pageNotFoundForTokenTestType, ts) 957 defer server.Close() 958 959 op := &operationListProvider{} 960 961 c := ec2metadata.New(unit.Session, &aws.Config{ 962 Endpoint: aws.String(server.URL), 963 }) 964 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 965 966 resp, err := c.GetMetadata("/some/path") 967 if err != nil { 968 t.Fatalf("Expected no error, got %v", err) 969 } 970 if e, a := "IMDSProfileForSDKGo", resp; e != a { 971 t.Fatalf("Expected %v, got %v", e, a) 972 } 973 974 resp, err = c.GetMetadata("/some/path") 975 if err != nil { 976 t.Fatalf("Expected no error, got %v", err) 977 } 978 if e, a := "IMDSProfileForSDKGo", resp; e != a { 979 t.Fatalf("Expected %v, got %v", e, a) 980 } 981 982 resp, err = c.GetMetadata("/some/path") 983 if err != nil { 984 t.Fatalf("Expected no error, got %v", err) 985 } 986 if e, a := "IMDSProfileForSDKGo", resp; e != a { 987 t.Fatalf("Expected %v, got %v", e, a) 988 } 989 990 resp, err = c.GetMetadata("/some/path") 991 expectedOperationsPerformed := []string{"GetToken", "GetMetadata", "GetMetadata", "GetMetadata", "GetMetadata"} 992 if err != nil { 993 t.Fatalf("Expected no error, got %v", err) 994 } 995 if e, a := "IMDSProfileForSDKGo", resp; e != a { 996 t.Fatalf("Expected %v, got %v", e, a) 997 } 998 if e, a := expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 999 t.Fatalf("expect %v operations, got %v", e, a) 1000 } 1001 } 1002 1003 func TestExhaustiveRetryWith401(t *testing.T) { 1004 ts := &testServer{ 1005 t: t, 1006 tokens: []string{"firstToken", "secondToken"}, 1007 data: "IMDSProfileForSDKGo", 1008 } 1009 1010 server := newTestServer(t, pageNotFoundWith401TestType, ts) 1011 defer server.Close() 1012 1013 op := &operationListProvider{} 1014 1015 c := ec2metadata.New(unit.Session, &aws.Config{ 1016 Endpoint: aws.String(server.URL), 1017 }) 1018 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 1019 1020 resp, err := c.GetMetadata("/some/path") 1021 if err == nil { 1022 t.Fatalf("Expected %v error, got none", err) 1023 } 1024 if e, a := "", resp; e != a { 1025 t.Fatalf("Expected %v, got %v", e, a) 1026 } 1027 resp, err = c.GetMetadata("/some/path") 1028 if err == nil { 1029 t.Fatalf("Expected %v error, got none", err) 1030 } 1031 if e, a := "", resp; e != a { 1032 t.Fatalf("Expected %v, got %v", e, a) 1033 } 1034 resp, err = c.GetMetadata("/some/path") 1035 if err == nil { 1036 t.Fatalf("Expected %v error, got none", err) 1037 } 1038 if e, a := "", resp; e != a { 1039 t.Fatalf("Expected %v, got %v", e, a) 1040 } 1041 resp, err = c.GetMetadata("/some/path") 1042 1043 expectedOperationsPerformed := []string{"GetToken", "GetMetadata", "GetToken", "GetMetadata", "GetToken", "GetMetadata", "GetToken", "GetMetadata"} 1044 1045 if err == nil { 1046 t.Fatalf("Expected %v error, got none", err) 1047 } 1048 if e, a := "", resp; e != a { 1049 t.Fatalf("Expected %v, got %v", e, a) 1050 } 1051 if e, a := expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 1052 t.Fatalf("expect %v operations, got %v", e, a) 1053 } 1054 } 1055 1056 func TestRequestTimeOut(t *testing.T) { 1057 mux := http.NewServeMux() 1058 done := make(chan bool) 1059 mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, r *http.Request) { 1060 // wait to read from channel done 1061 <-done 1062 }) 1063 1064 mux.HandleFunc("/latest/", func(w http.ResponseWriter, r *http.Request) { 1065 if len(r.Header.Get(tokenHeader)) != 0 { 1066 http.Error(w, "", 400) 1067 return 1068 } 1069 w.Write([]byte("IMDSProfileForSDKGo")) 1070 }) 1071 1072 server := httptest.NewServer(mux) 1073 defer server.Close() 1074 defer close(done) 1075 1076 op := &operationListProvider{} 1077 1078 c := ec2metadata.New(unit.Session, &aws.Config{ 1079 Endpoint: aws.String(server.URL), 1080 }) 1081 // for test, change the timeout to 100 ms 1082 c.Config.HTTPClient.Timeout = 100 * time.Millisecond 1083 1084 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 1085 1086 start := time.Now() 1087 resp, err := c.GetMetadata("/some/path") 1088 1089 if e, a := 1*time.Second, time.Since(start); e < a { 1090 t.Fatalf("expected duration of test to be less than %v, got %v", e, a) 1091 } 1092 1093 if e, a := "IMDSProfileForSDKGo", resp; e != a { 1094 t.Fatalf("Expected %v, got %v", e, a) 1095 } 1096 1097 if err != nil { 1098 t.Fatalf("Expected no error, got %v", err) 1099 } 1100 1101 expectedOperationsPerformed := []string{"GetToken", "GetMetadata"} 1102 if e, a := expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 1103 t.Fatalf("expect %v operations, got %v", e, a) 1104 } 1105 1106 start = time.Now() 1107 resp, err = c.GetMetadata("/some/path") 1108 if e, a := 1*time.Second, time.Since(start); e < a { 1109 t.Fatalf("expected duration of test to be less than %v, got %v", e, a) 1110 } 1111 1112 if e, a := "IMDSProfileForSDKGo", resp; e != a { 1113 t.Fatalf("Expected %v, got %v", e, a) 1114 } 1115 1116 if err != nil { 1117 t.Fatalf("Expected no error, got %v", err) 1118 } 1119 1120 expectedOperationsPerformed = []string{"GetToken", "GetMetadata", "GetMetadata"} 1121 if e, a := expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 1122 t.Fatalf("expect %v operations, got %v", e, a) 1123 } 1124 } 1125 1126 func TestTokenExpiredBehavior(t *testing.T) { 1127 tokens := []string{"firstToken", "secondToken", "thirdToken"} 1128 var activeToken string 1129 mux := http.NewServeMux() 1130 1131 mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, r *http.Request) { 1132 if r.Method == "PUT" && r.Header.Get(ttlHeader) != "" { 1133 // set ttl to 0, so TTL is expired. 1134 w.Header().Set(ttlHeader, "0") 1135 activeToken = tokens[0] 1136 if len(tokens) > 1 { 1137 tokens = tokens[1:] 1138 } 1139 1140 w.Write([]byte(activeToken)) 1141 return 1142 } 1143 http.Error(w, "bad request", http.StatusBadRequest) 1144 }) 1145 1146 // meta-data endpoint for this test, just returns the token 1147 mux.HandleFunc("/latest/meta-data/", func(w http.ResponseWriter, r *http.Request) { 1148 w.Header().Set(ttlHeader, r.Header.Get(ttlHeader)) 1149 w.Write([]byte(r.Header.Get(tokenHeader))) 1150 }) 1151 1152 server := httptest.NewServer(mux) 1153 defer server.Close() 1154 1155 op := &operationListProvider{} 1156 1157 c := ec2metadata.New(unit.Session, &aws.Config{ 1158 Endpoint: aws.String(server.URL), 1159 }) 1160 c.Handlers.Complete.PushBack(op.addToOperationPerformedList) 1161 1162 resp, err := c.GetMetadata("/some/path") 1163 if err != nil { 1164 t.Fatalf("Expected no error, got %v", err) 1165 } 1166 if e, a := activeToken, resp; e != a { 1167 t.Fatalf("Expected %v, got %v", e, a) 1168 } 1169 1170 // store the token received before 1171 var firstToken = activeToken 1172 1173 resp, err = c.GetMetadata("/some/path") 1174 if err != nil { 1175 t.Fatalf("Expected no error, got %v", err) 1176 } 1177 if e, a := activeToken, resp; e != a { 1178 t.Fatalf("Expected %v, got %v", e, a) 1179 } 1180 1181 // Since TTL is 0, we should have received a new token 1182 if firstToken == activeToken { 1183 t.Fatalf("Expected token should have expired, and not the same") 1184 } 1185 1186 expectedOperationsPerformed := []string{"GetToken", "GetMetadata", "GetToken", "GetMetadata"} 1187 1188 if e, a := expectedOperationsPerformed, op.operationsPerformed; !reflect.DeepEqual(e, a) { 1189 t.Fatalf("expect %v operations, got %v", e, a) 1190 } 1191 }