1//go:build go1.9 2// +build go1.9 3 4package endpoints 5 6import ( 7 "bytes" 8 "encoding/json" 9 "io/ioutil" 10 "log" 11 "net/url" 12 "os" 13 "path/filepath" 14 "reflect" 15 "regexp" 16 "strconv" 17 "strings" 18 "testing" 19) 20 21func TestUnmarshalRegionRegex(t *testing.T) { 22 var input = []byte(` 23{ 24 "regionRegex": "^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$" 25}`) 26 27 p := partition{} 28 err := json.Unmarshal(input, &p) 29 if err != nil { 30 t.Fatalf("expect no error, got %v", err) 31 } 32 33 expectRegexp, err := regexp.Compile(`^(us|eu|ap|sa|ca)\-\w+\-\d+$`) 34 if err != nil { 35 t.Fatalf("expect no error, got %v", err) 36 } 37 38 if e, a := expectRegexp.String(), p.RegionRegex.Regexp.String(); e != a { 39 t.Errorf("expect %v, got %v", e, a) 40 } 41} 42 43func TestUnmarshalRegion(t *testing.T) { 44 var input = []byte(` 45{ 46 "aws-global": { 47 "description": "AWS partition-global endpoint" 48 }, 49 "us-east-1": { 50 "description": "US East (N. Virginia)" 51 } 52}`) 53 54 rs := regions{} 55 err := json.Unmarshal(input, &rs) 56 if err != nil { 57 t.Fatalf("expect no error, got %v", err) 58 } 59 60 if e, a := 2, len(rs); e != a { 61 t.Errorf("expect %v len, got %v", e, a) 62 } 63 r, ok := rs["aws-global"] 64 if !ok { 65 t.Errorf("expect found, was not") 66 } 67 if e, a := "AWS partition-global endpoint", r.Description; e != a { 68 t.Errorf("expect %v, got %v", e, a) 69 } 70 71 r, ok = rs["us-east-1"] 72 if !ok { 73 t.Errorf("expect found, was not") 74 } 75 if e, a := "US East (N. Virginia)", r.Description; e != a { 76 t.Errorf("expect %v, got %v", e, a) 77 } 78} 79 80func TestUnmarshalServices(t *testing.T) { 81 var input = []byte(` 82{ 83 "acm": { 84 "endpoints": { 85 "us-east-1": {} 86 } 87 }, 88 "apigateway": { 89 "isRegionalized": true, 90 "endpoints": { 91 "us-east-1": {}, 92 "us-west-2": {} 93 } 94 }, 95 "notRegionalized": { 96 "isRegionalized": false, 97 "endpoints": { 98 "us-east-1": {}, 99 "us-west-2": {} 100 } 101 } 102}`) 103 104 ss := services{} 105 err := json.Unmarshal(input, &ss) 106 if err != nil { 107 t.Fatalf("expect no error, got %v", err) 108 } 109 110 if e, a := 3, len(ss); e != a { 111 t.Errorf("expect %v len, got %v", e, a) 112 } 113 s, ok := ss["acm"] 114 if !ok { 115 t.Errorf("expect found, was not") 116 } 117 if e, a := 1, len(s.Endpoints); e != a { 118 t.Errorf("expect %v len, got %v", e, a) 119 } 120 if e, a := boxedBoolUnset, s.IsRegionalized; e != a { 121 t.Errorf("expect %v, got %v", e, a) 122 } 123 124 s, ok = ss["apigateway"] 125 if !ok { 126 t.Errorf("expect found, was not") 127 } 128 if e, a := 2, len(s.Endpoints); e != a { 129 t.Errorf("expect %v len, got %v", e, a) 130 } 131 if e, a := boxedTrue, s.IsRegionalized; e != a { 132 t.Errorf("expect %v, got %v", e, a) 133 } 134 135 s, ok = ss["notRegionalized"] 136 if !ok { 137 t.Errorf("expect found, was not") 138 } 139 if e, a := 2, len(s.Endpoints); e != a { 140 t.Errorf("expect %v len, got %v", e, a) 141 } 142 if e, a := boxedFalse, s.IsRegionalized; e != a { 143 t.Errorf("expect %v, got %v", e, a) 144 } 145} 146 147func TestUnmarshalEndpoints(t *testing.T) { 148 var inputs = []byte(` 149{ 150 "aws-global": { 151 "hostname": "cloudfront.amazonaws.com", 152 "protocols": [ 153 "http", 154 "https" 155 ], 156 "signatureVersions": [ "v4" ], 157 "credentialScope": { 158 "region": "us-east-1", 159 "service": "serviceName" 160 }, 161 "sslCommonName": "commonName" 162 }, 163 "us-east-1": {} 164}`) 165 166 es := serviceEndpoints{} 167 err := json.Unmarshal(inputs, &es) 168 if err != nil { 169 t.Fatalf("expect no error, got %v", err) 170 } 171 172 if e, a := 2, len(es); e != a { 173 t.Errorf("expect %v len, got %v", e, a) 174 } 175 s, ok := es[endpointKey{Region: "aws-global"}] 176 if !ok { 177 t.Errorf("expect found, was not") 178 } 179 if e, a := "cloudfront.amazonaws.com", s.Hostname; e != a { 180 t.Errorf("expect %v, got %v", e, a) 181 } 182 if e, a := []string{"http", "https"}, s.Protocols; !reflect.DeepEqual(e, a) { 183 t.Errorf("expect %v, got %v", e, a) 184 } 185 if e, a := []string{"v4"}, s.SignatureVersions; !reflect.DeepEqual(e, a) { 186 t.Errorf("expect %v, got %v", e, a) 187 } 188 if e, a := (credentialScope{"us-east-1", "serviceName"}), s.CredentialScope; e != a { 189 t.Errorf("expect %v, got %v", e, a) 190 } 191 if e, a := "commonName", s.SSLCommonName; e != a { 192 t.Errorf("expect %v, got %v", e, a) 193 } 194} 195 196func TestEndpointResolve(t *testing.T) { 197 defs := []endpoint{ 198 { 199 Hostname: "{service}.{region}.{dnsSuffix}", 200 SignatureVersions: []string{"v2"}, 201 SSLCommonName: "sslCommonName", 202 }, 203 { 204 Hostname: "other-hostname", 205 Protocols: []string{"http"}, 206 CredentialScope: credentialScope{ 207 Region: "signing_region", 208 Service: "signing_service", 209 }, 210 }, 211 } 212 213 e := endpoint{ 214 Hostname: "{service}.{region}.{dnsSuffix}", 215 Protocols: []string{"http", "https"}, 216 SignatureVersions: []string{"v4"}, 217 SSLCommonName: "new sslCommonName", 218 } 219 220 resolved, err := e.resolve("service", "partitionID", "region", dnsSuffixTemplateKey, "dnsSuffix", 221 defs, Options{}, 222 ) 223 if err != nil { 224 t.Errorf("expected no error, got %v", err) 225 } 226 227 if e, a := "https://service.region.dnsSuffix", resolved.URL; e != a { 228 t.Errorf("expect %v, got %v", e, a) 229 } 230 if e, a := "signing_service", resolved.SigningName; e != a { 231 t.Errorf("expect %v, got %v", e, a) 232 } 233 if e, a := "signing_region", resolved.SigningRegion; e != a { 234 t.Errorf("expect %v, got %v", e, a) 235 } 236 if e, a := "v4", resolved.SigningMethod; e != a { 237 t.Errorf("expect %v, got %v", e, a) 238 } 239 240 // Check Invalid Region Identifier Format 241 _, err = e.resolve("service", "partitionID", "notvalid.com", dnsSuffixTemplateKey, "dnsSuffix", 242 defs, Options{}, 243 ) 244 if err == nil { 245 t.Errorf("expected err, got nil") 246 } 247} 248 249func TestEndpointMergeIn(t *testing.T) { 250 expected := endpoint{ 251 Hostname: "other hostname", 252 Protocols: []string{"http"}, 253 SignatureVersions: []string{"v4"}, 254 SSLCommonName: "ssl common name", 255 CredentialScope: credentialScope{ 256 Region: "region", 257 Service: "service", 258 }, 259 } 260 261 actual := endpoint{} 262 actual.mergeIn(endpoint{ 263 Hostname: "other hostname", 264 Protocols: []string{"http"}, 265 SignatureVersions: []string{"v4"}, 266 SSLCommonName: "ssl common name", 267 CredentialScope: credentialScope{ 268 Region: "region", 269 Service: "service", 270 }, 271 }) 272 273 if e, a := expected, actual; !reflect.DeepEqual(e, a) { 274 t.Errorf("expect %v, got %v", e, a) 275 } 276} 277 278func TestResolveEndpoint(t *testing.T) { 279 resolved, err := testPartitions.EndpointFor("service2", "us-west-2") 280 281 if err != nil { 282 t.Fatalf("expect no error, got %v", err) 283 } 284 if e, a := "https://service2.us-west-2.amazonaws.com", resolved.URL; e != a { 285 t.Errorf("expect %v, got %v", e, a) 286 } 287 if e, a := "us-west-2", resolved.SigningRegion; e != a { 288 t.Errorf("expect %v, got %v", e, a) 289 } 290 if e, a := "service2", resolved.SigningName; e != a { 291 t.Errorf("expect %v, got %v", e, a) 292 } 293 if resolved.SigningNameDerived { 294 t.Errorf("expect the signing name not to be derived, but was") 295 } 296} 297 298func TestResolveEndpoint_DisableSSL(t *testing.T) { 299 resolved, err := testPartitions.EndpointFor("service2", "us-west-2", DisableSSLOption) 300 301 if err != nil { 302 t.Fatalf("expect no error, got %v", err) 303 } 304 if e, a := "http://service2.us-west-2.amazonaws.com", resolved.URL; e != a { 305 t.Errorf("expect %v, got %v", e, a) 306 } 307 if e, a := "us-west-2", resolved.SigningRegion; e != a { 308 t.Errorf("expect %v, got %v", e, a) 309 } 310 if e, a := "service2", resolved.SigningName; e != a { 311 t.Errorf("expect %v, got %v", e, a) 312 } 313 if resolved.SigningNameDerived { 314 t.Errorf("expect the signing name not to be derived, but was") 315 } 316} 317 318func TestResolveEndpoint_UseDualStack_UseDualStackEndpoint(t *testing.T) { 319 cases := map[string]struct { 320 Service string 321 Region string 322 323 Options func(*Options) 324 325 ExpectedURL string 326 ExpectedSigningName string 327 ExpectedSigningRegion string 328 ExpectSigningNameDerived bool 329 330 ExpectErr bool 331 }{ 332 "deprecated UseDualStack does not apply to services that are not s3 or s3-control": { 333 Service: "ec2", 334 Region: "us-west-2", 335 Options: UseDualStackOption, 336 ExpectedURL: "https://ec2.us-west-2.amazonaws.com", 337 ExpectedSigningName: "ec2", 338 ExpectedSigningRegion: "us-west-2", 339 ExpectSigningNameDerived: true, 340 }, 341 "deprecated UseDualStack allowed for s3": { 342 Service: "s3", 343 Region: "us-west-2", 344 Options: UseDualStackOption, 345 ExpectedURL: "https://s3.dualstack.us-west-2.amazonaws.com", 346 ExpectedSigningName: "s3", 347 ExpectedSigningRegion: "us-west-2", 348 ExpectSigningNameDerived: true, 349 }, 350 "deprecated UseDualStack allowed for s3-control": { 351 Service: "s3-control", 352 Region: "us-west-2", 353 Options: UseDualStackOption, 354 ExpectedURL: "https://s3-control.dualstack.us-west-2.amazonaws.com", 355 ExpectedSigningName: "s3-control", 356 ExpectedSigningRegion: "us-west-2", 357 ExpectSigningNameDerived: true, 358 }, 359 "UseDualStackEndpoint applies to all services": { 360 Service: "ec2", 361 Region: "us-west-2", 362 Options: UseDualStackEndpointOption, 363 ExpectedURL: "https://api.ec2.us-west-2.aws", 364 ExpectedSigningName: "ec2", 365 ExpectedSigningRegion: "us-west-2", 366 ExpectSigningNameDerived: true, 367 }, 368 "UseDualStackEndpoint applies to s3": { 369 Service: "s3", 370 Region: "us-west-2", 371 Options: UseDualStackEndpointOption, 372 ExpectedURL: "https://s3.dualstack.us-west-2.amazonaws.com", 373 ExpectedSigningName: "s3", 374 ExpectedSigningRegion: "us-west-2", 375 ExpectSigningNameDerived: true, 376 }, 377 "UseDualStackEndpoint applies to s3-control": { 378 Service: "s3-control", 379 Region: "us-west-2", 380 Options: UseDualStackEndpointOption, 381 ExpectedURL: "https://s3-control.dualstack.us-west-2.amazonaws.com", 382 ExpectedSigningName: "s3-control", 383 ExpectedSigningRegion: "us-west-2", 384 ExpectSigningNameDerived: true, 385 }, 386 "UseDualStackEndpoint (disabled) setting has higher precedence then UseDualStack for s3": { 387 Service: "s3", 388 Region: "us-west-2", 389 Options: func(options *Options) { 390 options.UseDualStack = true 391 options.UseDualStackEndpoint = DualStackEndpointStateDisabled 392 }, 393 ExpectedURL: "https://s3.us-west-2.amazonaws.com", 394 ExpectedSigningName: "s3", 395 ExpectedSigningRegion: "us-west-2", 396 ExpectSigningNameDerived: true, 397 }, 398 "UseDualStackEndpoint (disabled) setting has higher precedence then UseDualStack for s3-control": { 399 Service: "s3-control", 400 Region: "us-west-2", 401 Options: func(options *Options) { 402 options.UseDualStack = true 403 options.UseDualStackEndpoint = DualStackEndpointStateDisabled 404 }, 405 ExpectedURL: "https://s3-control.us-west-2.amazonaws.com", 406 ExpectedSigningName: "s3-control", 407 ExpectedSigningRegion: "us-west-2", 408 ExpectSigningNameDerived: true, 409 }, 410 "UseDualStackEndpoint in partition with no partition or service defaults": { 411 Service: "service1", 412 Region: "cn-north-2", 413 Options: UseDualStackEndpointOption, 414 ExpectErr: true, 415 }, 416 } 417 418 for name, tt := range cases { 419 t.Run(name, func(t *testing.T) { 420 if tt.Options == nil { 421 tt.Options = func(options *Options) {} 422 } 423 424 resolved, err := AwsPartition().EndpointFor(tt.Service, tt.Region, tt.Options) 425 if tt.ExpectErr != (err != nil) { 426 t.Fatalf("ExpectErr=%v, got err=%v", tt.ExpectErr, err) 427 } 428 429 assertEndpoint(t, resolved, tt.ExpectedURL, tt.ExpectedSigningName, tt.ExpectedSigningRegion) 430 431 if e, a := tt.ExpectSigningNameDerived, resolved.SigningNameDerived; e != a { 432 t.Errorf("ExpectSigningNameDerived(%v) != SigningNameDerived(%v)", e, a) 433 } 434 }) 435 } 436} 437 438func TestResolveEndpoint_HTTPProtocol(t *testing.T) { 439 resolved, err := testPartitions.EndpointFor("httpService", "us-west-2") 440 441 if err != nil { 442 t.Fatalf("expect no error, got %v", err) 443 } 444 if e, a := "http://httpService.us-west-2.amazonaws.com", resolved.URL; e != a { 445 t.Errorf("expect %v, got %v", e, a) 446 } 447 if e, a := "us-west-2", resolved.SigningRegion; e != a { 448 t.Errorf("expect %v, got %v", e, a) 449 } 450 if e, a := "httpService", resolved.SigningName; e != a { 451 t.Errorf("expect %v, got %v", e, a) 452 } 453 if !resolved.SigningNameDerived { 454 t.Errorf("expect the signing name to be derived") 455 } 456} 457 458func TestResolveEndpoint_UnknownService(t *testing.T) { 459 _, err := testPartitions.EndpointFor("unknownservice", "us-west-2") 460 461 if err == nil { 462 t.Errorf("expect error, got none") 463 } 464 465 _, ok := err.(UnknownServiceError) 466 if !ok { 467 t.Errorf("expect error to be UnknownServiceError") 468 } 469} 470 471func TestResolveEndpoint_ResolveUnknownService(t *testing.T) { 472 resolved, err := testPartitions.EndpointFor("unknown-service", "us-region-1", 473 ResolveUnknownServiceOption) 474 475 if err != nil { 476 t.Fatalf("expect no error, got %v", err) 477 } 478 479 if e, a := "https://unknown-service.us-region-1.amazonaws.com", resolved.URL; e != a { 480 t.Errorf("expect %v, got %v", e, a) 481 } 482 if e, a := "us-region-1", resolved.SigningRegion; e != a { 483 t.Errorf("expect %v, got %v", e, a) 484 } 485 if e, a := "unknown-service", resolved.SigningName; e != a { 486 t.Errorf("expect %v, got %v", e, a) 487 } 488 if !resolved.SigningNameDerived { 489 t.Errorf("expect the signing name to be derived") 490 } 491} 492 493func TestResolveEndpoint_UnknownMatchedRegion(t *testing.T) { 494 resolved, err := testPartitions.EndpointFor("s3", "us-region-1") 495 496 if err != nil { 497 t.Fatalf("expect no error, got %v", err) 498 } 499 if e, a := "https://s3.us-region-1.amazonaws.com", resolved.URL; e != a { 500 t.Errorf("expect %v, got %v", e, a) 501 } 502 if e, a := "us-region-1", resolved.SigningRegion; e != a { 503 t.Errorf("expect %v, got %v", e, a) 504 } 505 if e, a := "s3", resolved.SigningName; e != a { 506 t.Errorf("expect %v, got %v", e, a) 507 } 508} 509 510func TestResolveEndpoint_UnknownRegion(t *testing.T) { 511 resolved, err := testPartitions.EndpointFor("s3", "unknownregion") 512 513 if err != nil { 514 t.Fatalf("expect no error, got %v", err) 515 } 516 if e, a := "https://s3.unknownregion.amazonaws.com", resolved.URL; e != a { 517 t.Errorf("expect %v, got %v", e, a) 518 } 519 if e, a := "unknownregion", resolved.SigningRegion; e != a { 520 t.Errorf("expect %v, got %v", e, a) 521 } 522 if e, a := "s3", resolved.SigningName; e != a { 523 t.Errorf("expect %v, got %v", e, a) 524 } 525} 526 527func TestResolveEndpoint_StrictPartitionUnknownEndpoint(t *testing.T) { 528 _, err := testPartitions[0].EndpointFor("s3", "unknownregion", StrictMatchingOption) 529 530 if err == nil { 531 t.Errorf("expect error, got none") 532 } 533 534 _, ok := err.(UnknownEndpointError) 535 if !ok { 536 t.Errorf("expect error to be UnknownEndpointError") 537 } 538} 539 540func TestResolveEndpoint_StrictPartitionsUnknownEndpoint(t *testing.T) { 541 _, err := testPartitions.EndpointFor("s3", "us-region-1", StrictMatchingOption) 542 543 if err == nil { 544 t.Errorf("expect error, got none") 545 } 546 547 _, ok := err.(UnknownEndpointError) 548 if !ok { 549 t.Errorf("expect error to be UnknownEndpointError") 550 } 551} 552 553func TestResolveEndpoint_NotRegionalized(t *testing.T) { 554 resolved, err := testPartitions.EndpointFor("globalService", "us-west-2") 555 556 if err != nil { 557 t.Fatalf("expect no error, got %v", err) 558 } 559 if e, a := "https://globalService.amazonaws.com", resolved.URL; e != a { 560 t.Errorf("expect %v, got %v", e, a) 561 } 562 if e, a := "us-east-1", resolved.SigningRegion; e != a { 563 t.Errorf("expect %v, got %v", e, a) 564 } 565 if e, a := "globalService", resolved.SigningName; e != a { 566 t.Errorf("expect %v, got %v", e, a) 567 } 568 if !resolved.SigningNameDerived { 569 t.Errorf("expect the signing name to be derived") 570 } 571} 572 573func TestResolveEndpoint_AwsGlobal(t *testing.T) { 574 resolved, err := testPartitions.EndpointFor("globalService", "aws-global") 575 576 if err != nil { 577 t.Fatalf("expect no error, got %v", err) 578 } 579 if e, a := "https://globalService.amazonaws.com", resolved.URL; e != a { 580 t.Errorf("expect %v, got %v", e, a) 581 } 582 if e, a := "us-east-1", resolved.SigningRegion; e != a { 583 t.Errorf("expect %v, got %v", e, a) 584 } 585 if e, a := "globalService", resolved.SigningName; e != a { 586 t.Errorf("expect %v, got %v", e, a) 587 } 588 if !resolved.SigningNameDerived { 589 t.Errorf("expect the signing name to be derived") 590 } 591} 592 593func TestEndpointFor_RegionalFlag(t *testing.T) { 594 // AwsPartition resolver for STS regional endpoints in AWS Partition 595 resolver := AwsPartition() 596 597 cases := map[string]struct { 598 service, region string 599 regional bool 600 ExpectURL, ExpectSigningMethod, ExpectSigningRegion string 601 ExpectSigningNameDerived bool 602 }{ 603 "acm/ap-northeast-1/regional": { 604 service: "acm", 605 region: "ap-northeast-1", 606 regional: true, 607 ExpectURL: "https://acm.ap-northeast-1.amazonaws.com", 608 ExpectSigningMethod: "v4", 609 ExpectSigningNameDerived: true, 610 ExpectSigningRegion: "ap-northeast-1", 611 }, 612 "acm/ap-northeast-1/legacy": { 613 service: "acm", 614 region: "ap-northeast-1", 615 regional: false, 616 ExpectURL: "https://acm.ap-northeast-1.amazonaws.com", 617 ExpectSigningMethod: "v4", 618 ExpectSigningNameDerived: true, 619 ExpectSigningRegion: "ap-northeast-1", 620 }, 621 } 622 623 for name, c := range cases { 624 t.Run(name, func(t *testing.T) { 625 var optionSlice []func(o *Options) 626 optionSlice = append(optionSlice, func(o *Options) { 627 if c.regional { 628 o.STSRegionalEndpoint = RegionalSTSEndpoint 629 } 630 }) 631 632 actual, err := resolver.EndpointFor(c.service, c.region, optionSlice...) 633 if err != nil { 634 t.Fatalf("failed to resolve endpoint, %v", err) 635 } 636 637 if e, a := c.ExpectURL, actual.URL; e != a { 638 t.Errorf("expect %v, got %v", e, a) 639 } 640 641 if e, a := c.ExpectSigningMethod, actual.SigningMethod; e != a { 642 t.Errorf("expect %v, got %v", e, a) 643 } 644 645 if e, a := c.ExpectSigningNameDerived, actual.SigningNameDerived; e != a { 646 t.Errorf("expect %v, got %v", e, a) 647 } 648 649 if e, a := c.ExpectSigningRegion, actual.SigningRegion; e != a { 650 t.Errorf("expect %v, got %v", e, a) 651 } 652 653 }) 654 } 655} 656 657func TestEndpointFor_EmptyRegion(t *testing.T) { 658 // skip this test for partitions outside `aws` partition 659 if DefaultPartitions()[0].id != "aws" { 660 t.Skip() 661 } 662 663 cases := map[string]struct { 664 Service string 665 Region string 666 RealRegion string 667 ExpectErr string 668 }{ 669 // Legacy services that previous accepted empty region 670 "budgets": {Service: "budgets", RealRegion: "aws-global"}, 671 "ce": {Service: "ce", RealRegion: "aws-global"}, 672 "chime": {Service: "chime", RealRegion: "aws-global"}, 673 "ec2metadata": {Service: "ec2metadata", RealRegion: "aws-global"}, 674 "iam": {Service: "iam", RealRegion: "aws-global"}, 675 "importexport": {Service: "importexport", RealRegion: "aws-global"}, 676 "organizations": {Service: "organizations", RealRegion: "aws-global"}, 677 "route53": {Service: "route53", RealRegion: "aws-global"}, 678 "sts": {Service: "sts", RealRegion: "aws-global"}, 679 "support": {Service: "support", RealRegion: "aws-global"}, 680 "waf": {Service: "waf", RealRegion: "aws-global"}, 681 682 // Other services 683 "s3": {Service: "s3", Region: "us-east-1", RealRegion: "us-east-1"}, 684 "s3 no region": {Service: "s3", ExpectErr: "could not resolve endpoint"}, 685 } 686 687 for name, c := range cases { 688 t.Run(name, func(t *testing.T) { 689 actual, err := DefaultResolver().EndpointFor(c.Service, c.Region) 690 if len(c.ExpectErr) != 0 { 691 if e, a := c.ExpectErr, err.Error(); !strings.Contains(a, e) { 692 t.Errorf("expect %q error in %q", e, a) 693 } 694 return 695 } 696 if err != nil { 697 t.Fatalf("expect no error got, %v", err) 698 } 699 700 expect, err := DefaultResolver().EndpointFor(c.Service, c.RealRegion) 701 if err != nil { 702 t.Fatalf("failed to get endpoint for default resolver") 703 } 704 if e, a := expect.URL, actual.URL; e != a { 705 t.Errorf("expect %v URL, got %v", e, a) 706 } 707 if e, a := expect.SigningRegion, actual.SigningRegion; e != a { 708 t.Errorf("expect %v signing region, got %v", e, a) 709 } 710 711 }) 712 } 713} 714 715func TestRegionValidator(t *testing.T) { 716 cases := []struct { 717 Region string 718 Valid bool 719 }{ 720 0: { 721 Region: "us-east-1", 722 Valid: true, 723 }, 724 1: { 725 Region: "invalid.com", 726 Valid: false, 727 }, 728 2: { 729 Region: "@invalid.com/%23", 730 Valid: false, 731 }, 732 3: { 733 Region: "local", 734 Valid: true, 735 }, 736 4: { 737 Region: "9-west-1", 738 Valid: true, 739 }, 740 } 741 742 for i, tt := range cases { 743 t.Run(strconv.Itoa(i), func(t *testing.T) { 744 if e, a := tt.Valid, validateInputRegion(tt.Region); e != a { 745 t.Errorf("expected %v, got %v", e, a) 746 } 747 }) 748 } 749} 750 751func TestResolveEndpoint_FipsAwsGlobal(t *testing.T) { 752 resolved, err := AwsPartition().EndpointFor("route53", "fips-aws-global") 753 754 if err != nil { 755 t.Fatalf("expect no error, got %v", err) 756 } 757 if e, a := "https://route53-fips.amazonaws.com", resolved.URL; e != a { 758 t.Errorf("expect %v, got %v", e, a) 759 } 760 if e, a := "us-east-1", resolved.SigningRegion; e != a { 761 t.Errorf("expect %v, got %v", e, a) 762 } 763 if e, a := "route53", resolved.SigningName; e != a { 764 t.Errorf("expect %v, got %v", e, a) 765 } 766 if !resolved.SigningNameDerived { 767 t.Errorf("expect the signing name to be derived") 768 } 769} 770 771func TestEC2MetadataService(t *testing.T) { 772 unmodelled := partition{ 773 ID: "unmodelled", 774 Name: "partition with unmodelled ec2metadata", 775 Services: map[string]service{ 776 "foo": { 777 Endpoints: serviceEndpoints{ 778 endpointKey{Region: "us-west-2"}: endpoint{ 779 Hostname: "foo.us-west-2.amazonaws.com", 780 Protocols: []string{"http"}, 781 SignatureVersions: []string{"v4"}, 782 }, 783 }, 784 }, 785 }, 786 Regions: map[string]region{ 787 "us-west-2": {Description: "us-west-2 region"}, 788 }, 789 } 790 791 modelled := partition{ 792 ID: "modelled", 793 Name: "partition with modelled ec2metadata", 794 Services: map[string]service{ 795 "ec2metadata": { 796 Endpoints: serviceEndpoints{ 797 endpointKey{Region: "us-west-2"}: endpoint{ 798 Hostname: "custom.localhost/latest", 799 Protocols: []string{"http"}, 800 SignatureVersions: []string{"v4"}, 801 }, 802 }, 803 }, 804 "foo": { 805 Endpoints: serviceEndpoints{ 806 endpointKey{Region: "us-west-2"}: endpoint{ 807 Hostname: "foo.us-west-2.amazonaws.com", 808 Protocols: []string{"http"}, 809 SignatureVersions: []string{"v4"}, 810 }, 811 }, 812 }, 813 }, 814 Regions: map[string]region{ 815 "us-west-2": {Description: "us-west-2 region"}, 816 }, 817 } 818 819 uServices := unmodelled.Partition().Services() 820 821 if s, ok := uServices[Ec2metadataServiceID]; !ok { 822 t.Errorf("expect ec2metadata to be present") 823 } else { 824 if regions := s.Regions(); len(regions) != 0 { 825 t.Errorf("expect no regions for ec2metadata, got %v", len(regions)) 826 } 827 if resolved, err := unmodelled.EndpointFor(Ec2metadataServiceID, "us-west-2"); err != nil { 828 t.Errorf("expect no error, got %v", err) 829 } else if e, a := ec2MetadataEndpointIPv4, resolved.URL; e != a { 830 t.Errorf("expect %v, got %v", e, a) 831 } 832 } 833 834 if s, ok := uServices["foo"]; !ok { 835 t.Errorf("expect foo to be present") 836 } else if regions := s.Regions(); len(regions) == 0 { 837 t.Errorf("expect region endpoints for foo. got none") 838 } 839 840 mServices := modelled.Partition().Services() 841 842 if s, ok := mServices[Ec2metadataServiceID]; !ok { 843 t.Errorf("expect ec2metadata to be present") 844 } else if regions := s.Regions(); len(regions) == 0 { 845 t.Errorf("expect region for ec2metadata, got none") 846 } else { 847 if resolved, err := modelled.EndpointFor(Ec2metadataServiceID, "us-west-2"); err != nil { 848 t.Errorf("expect no error, got %v", err) 849 } else if e, a := "http://custom.localhost/latest", resolved.URL; e != a { 850 t.Errorf("expect %v, got %v", e, a) 851 } 852 } 853 854 if s, ok := mServices["foo"]; !ok { 855 t.Errorf("expect foo to be present") 856 } else if regions := s.Regions(); len(regions) == 0 { 857 t.Errorf("expect region endpoints for foo, got none") 858 } 859} 860 861func TestEndpointVariants(t *testing.T) { 862 modelFile, err := os.Open(filepath.Join("testdata", "variants_model.json")) 863 if err != nil { 864 t.Fatal(err) 865 } 866 defer modelFile.Close() 867 868 resolver, err := DecodeModel(modelFile) 869 if err != nil { 870 t.Fatal(err) 871 } 872 873 type testCase struct { 874 Service string `json:"service"` 875 Region string `json:"region"` 876 FIPS bool `json:"FIPS"` 877 DualStack bool `json:"DualStack"` 878 Endpoint string `json:"Endpoint"` 879 } 880 881 casesBytes, err := ioutil.ReadFile(filepath.Join("testdata", "variants_cases.json")) 882 if err != nil { 883 t.Fatal(err) 884 } 885 886 var cases []testCase 887 if err := json.Unmarshal(casesBytes, &cases); err != nil { 888 panic(err) 889 } 890 891 for i, tt := range cases { 892 t.Run(strconv.Itoa(i), func(t *testing.T) { 893 options := Options{} 894 895 if tt.FIPS { 896 options.UseFIPSEndpoint = FIPSEndpointStateEnabled 897 } 898 if tt.DualStack { 899 options.UseDualStackEndpoint = DualStackEndpointStateEnabled 900 } 901 902 resolvedEndpoint, err := resolver.EndpointFor(tt.Service, tt.Region, func(o *Options) { 903 *o = options 904 }) 905 if err != nil { 906 t.Errorf("expect no error, got %v", err) 907 return 908 } 909 910 parsed, err := url.Parse(resolvedEndpoint.URL) 911 if err != nil { 912 t.Errorf("expect no error, got %v", err) 913 return 914 } 915 916 if e, a := parsed.Host, tt.Endpoint; e != a { 917 t.Errorf("expect %v, got %v", e, a) 918 } 919 }) 920 } 921} 922 923func TestLogDeprecated(t *testing.T) { 924 partitions := partitions{ 925 partition{ 926 ID: "aws", 927 RegionRegex: regionRegex{ 928 Regexp: regexp.MustCompile("^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$"), 929 }, 930 Defaults: map[defaultKey]endpoint{ 931 {}: { 932 Hostname: "foo.{region}.bar.tld", 933 Protocols: []string{"https", "http"}, 934 }, 935 { 936 Variant: fipsVariant, 937 }: { 938 Hostname: "foo-fips.{region}.bar.tld", 939 }, 940 }, 941 Services: map[string]service{ 942 "service": { 943 Endpoints: map[endpointKey]endpoint{ 944 { 945 Region: "foo", 946 }: {}, 947 { 948 Region: "bar", 949 }: { 950 Deprecated: boxedTrue, 951 }, 952 { 953 Region: "bar", 954 Variant: fipsVariant, 955 }: { 956 Deprecated: boxedTrue, 957 }, 958 }, 959 }, 960 }, 961 }, 962 } 963 964 cases := []struct { 965 Region string 966 Options Options 967 Expected ResolvedEndpoint 968 SetupLogger func() (Logger, func(*testing.T)) 969 WantErr bool 970 }{ 971 { 972 Region: "foo", 973 Expected: ResolvedEndpoint{ 974 URL: "https://foo.foo.bar.tld", 975 PartitionID: "aws", 976 SigningName: "service", 977 SigningRegion: "foo", 978 SigningMethod: "v4", 979 SigningNameDerived: true, 980 }, 981 }, 982 { 983 Region: "bar", 984 Options: Options{ 985 LogDeprecated: true, 986 }, 987 Expected: ResolvedEndpoint{ 988 URL: "https://foo.bar.bar.tld", 989 PartitionID: "aws", 990 SigningName: "service", 991 SigningRegion: "bar", 992 SigningMethod: "v4", 993 SigningNameDerived: true, 994 }, 995 }, 996 { 997 Region: "bar", 998 Options: Options{ 999 LogDeprecated: true, 1000 UseFIPSEndpoint: FIPSEndpointStateEnabled, 1001 }, 1002 Expected: ResolvedEndpoint{ 1003 URL: "https://foo-fips.bar.bar.tld", 1004 PartitionID: "aws", 1005 SigningName: "service", 1006 SigningRegion: "bar", 1007 SigningMethod: "v4", 1008 SigningNameDerived: true, 1009 }, 1010 }, 1011 { 1012 Region: "bar", 1013 Options: Options{ 1014 LogDeprecated: true, 1015 }, 1016 SetupLogger: func() (Logger, func(*testing.T)) { 1017 buffer := bytes.NewBuffer(nil) 1018 logger := log.New(buffer, "", 0) 1019 return LoggerFunc(func(i ...interface{}) { 1020 logger.Println(i...) 1021 }), func(t *testing.T) { 1022 if e, a := "endpoint identifier \"bar\", url \"https://foo.bar.bar.tld\" marked as deprecated\n", buffer.String(); e != a { 1023 t.Errorf("expect %v, got %v", e, a) 1024 } 1025 } 1026 }, 1027 Expected: ResolvedEndpoint{ 1028 URL: "https://foo.bar.bar.tld", 1029 PartitionID: "aws", 1030 SigningName: "service", 1031 SigningRegion: "bar", 1032 SigningMethod: "v4", 1033 SigningNameDerived: true, 1034 }, 1035 }, 1036 { 1037 Region: "bar", 1038 Options: Options{ 1039 LogDeprecated: true, 1040 UseFIPSEndpoint: FIPSEndpointStateEnabled, 1041 }, 1042 SetupLogger: func() (Logger, func(*testing.T)) { 1043 buffer := bytes.NewBuffer(nil) 1044 logger := log.New(buffer, "", 0) 1045 return LoggerFunc(func(i ...interface{}) { 1046 logger.Println(i...) 1047 }), func(t *testing.T) { 1048 if e, a := "endpoint identifier \"bar\", url \"https://foo-fips.bar.bar.tld\" marked as deprecated\n", buffer.String(); e != a { 1049 t.Errorf("expect %v, got %v", e, a) 1050 } 1051 } 1052 }, 1053 Expected: ResolvedEndpoint{ 1054 URL: "https://foo-fips.bar.bar.tld", 1055 PartitionID: "aws", 1056 SigningName: "service", 1057 SigningRegion: "bar", 1058 SigningMethod: "v4", 1059 SigningNameDerived: true, 1060 }, 1061 }, 1062 } 1063 1064 for i, tt := range cases { 1065 t.Run(strconv.Itoa(i), func(t *testing.T) { 1066 var verifyLog func(*testing.T) 1067 if tt.SetupLogger != nil { 1068 tt.Options.Logger, verifyLog = tt.SetupLogger() 1069 } 1070 1071 endpoint, err := partitions.EndpointFor("service", tt.Region, func(options *Options) { 1072 *options = tt.Options 1073 }) 1074 if (err != nil) != tt.WantErr { 1075 t.Errorf("WantErr(%v), got error %v", tt.WantErr, err) 1076 } 1077 1078 if !reflect.DeepEqual(tt.Expected, endpoint) { 1079 t.Errorf("expect %v, got %v", tt.Expected, endpoint) 1080 } 1081 1082 if verifyLog != nil { 1083 verifyLog(t) 1084 } 1085 }) 1086 } 1087} 1088 1089func TestPartitionVariantMerging(t *testing.T) { 1090 partition := partition{ 1091 ID: "aws-iso", 1092 Name: "AWS ISO (US)", 1093 DNSSuffix: "c2s.ic.gov", 1094 RegionRegex: regionRegex{ 1095 Regexp: func() *regexp.Regexp { 1096 reg, _ := regexp.Compile("^us\\-iso\\-\\w+\\-\\d+$") 1097 return reg 1098 }(), 1099 }, 1100 Defaults: endpointDefaults{ 1101 {}: { 1102 Hostname: "{service}.{region}.{dnsSuffix}", 1103 Protocols: []string{"https"}, 1104 SignatureVersions: []string{"v4"}, 1105 }, 1106 {Variant: dualStackVariant}: { 1107 DNSSuffix: "dualstack.foo.bar", 1108 Hostname: "{service}.{region}.{dnsSuffix}", 1109 Protocols: []string{"https"}, 1110 SignatureVersions: []string{"v4"}, 1111 }, 1112 }, 1113 Regions: regions{ 1114 "us-iso-east-1": region{ 1115 Description: "US ISO East", 1116 }, 1117 "us-iso-west-1": region{ 1118 Description: "US ISO WEST", 1119 }, 1120 }, 1121 Services: services{ 1122 "service1": {}, 1123 "service2": { 1124 Defaults: map[defaultKey]endpoint{ 1125 {}: { 1126 CredentialScope: credentialScope{ 1127 Service: "service-two", 1128 }, 1129 }, 1130 {Variant: fipsVariant}: { 1131 Hostname: "{service}-fips.{region}.{dnsSuffix}", 1132 DNSSuffix: "foo.bar", 1133 CredentialScope: credentialScope{ 1134 Service: "service-two", 1135 }, 1136 }, 1137 }, 1138 }, 1139 }, 1140 } 1141 1142 cases := []struct { 1143 Service string 1144 Region string 1145 Options Options 1146 WantErr bool 1147 ExpectedEndpoint ResolvedEndpoint 1148 }{ 1149 { 1150 Service: "service1", 1151 Region: "us-iso-east-1", 1152 Options: Options{ 1153 UseFIPSEndpoint: FIPSEndpointStateEnabled, 1154 }, 1155 WantErr: true, 1156 }, 1157 { 1158 Service: "service1", 1159 Region: "us-iso-east-1", 1160 Options: Options{ 1161 UseDualStackEndpoint: DualStackEndpointStateEnabled, 1162 }, 1163 ExpectedEndpoint: ResolvedEndpoint{ 1164 URL: "https://service1.us-iso-east-1.dualstack.foo.bar", 1165 PartitionID: "aws-iso", 1166 SigningRegion: "us-iso-east-1", 1167 SigningName: "service1", 1168 SigningNameDerived: true, 1169 SigningMethod: "v4", 1170 }, 1171 }, 1172 { 1173 Service: "service1", 1174 Region: "us-iso-east-1", 1175 ExpectedEndpoint: ResolvedEndpoint{ 1176 URL: "https://service1.us-iso-east-1.c2s.ic.gov", 1177 PartitionID: "aws-iso", 1178 SigningRegion: "us-iso-east-1", 1179 SigningName: "service1", 1180 SigningNameDerived: true, 1181 SigningMethod: "v4", 1182 }, 1183 }, 1184 { 1185 Service: "service2", 1186 Region: "us-iso-east-1", 1187 Options: Options{ 1188 UseFIPSEndpoint: FIPSEndpointStateEnabled, 1189 }, 1190 ExpectedEndpoint: ResolvedEndpoint{ 1191 URL: "https://service2-fips.us-iso-east-1.foo.bar", 1192 PartitionID: "aws-iso", 1193 SigningRegion: "us-iso-east-1", 1194 SigningName: "service-two", 1195 SigningMethod: "v4", 1196 }, 1197 }, 1198 { 1199 Service: "service2", 1200 Region: "us-iso-east-1", 1201 Options: Options{ 1202 UseDualStackEndpoint: DualStackEndpointStateEnabled, 1203 }, 1204 ExpectedEndpoint: ResolvedEndpoint{ 1205 URL: "https://service2.us-iso-east-1.dualstack.foo.bar", 1206 PartitionID: "aws-iso", 1207 SigningRegion: "us-iso-east-1", 1208 SigningName: "service-two", 1209 SigningMethod: "v4", 1210 }, 1211 }, 1212 } 1213 1214 for i, tt := range cases { 1215 t.Run(strconv.Itoa(i), func(t *testing.T) { 1216 resolved, err := partition.EndpointFor(tt.Service, tt.Region, func(options *Options) { 1217 *options = tt.Options 1218 }) 1219 if (err != nil) != tt.WantErr { 1220 t.Errorf("WantErr(%v) got Err(%v)", tt.WantErr, err) 1221 return 1222 } 1223 if tt.WantErr { 1224 return 1225 } 1226 if e, a := tt.ExpectedEndpoint, resolved; !reflect.DeepEqual(e, a) { 1227 t.Errorf("expect %v, got %v", e, a) 1228 } 1229 }) 1230 } 1231 1232} 1233