sigs.k8s.io/external-dns@v0.14.1/provider/awssd/aws_sd_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package awssd 18 19 import ( 20 "context" 21 "errors" 22 "math/rand" 23 "reflect" 24 "strconv" 25 "testing" 26 "time" 27 28 "github.com/aws/aws-sdk-go/aws" 29 "github.com/aws/aws-sdk-go/aws/request" 30 sd "github.com/aws/aws-sdk-go/service/servicediscovery" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 34 "sigs.k8s.io/external-dns/endpoint" 35 "sigs.k8s.io/external-dns/internal/testutils" 36 "sigs.k8s.io/external-dns/plan" 37 ) 38 39 // Compile time check for interface conformance 40 var _ AWSSDClient = &AWSSDClientStub{} 41 42 var ( 43 ErrNamespaceNotFound = errors.New("Namespace not found") 44 ) 45 46 type AWSSDClientStub struct { 47 // map[namespace_id]namespace 48 namespaces map[string]*sd.Namespace 49 50 // map[namespace_id] => map[service_id]instance 51 services map[string]map[string]*sd.Service 52 53 // map[service_id] => map[inst_id]instance 54 instances map[string]map[string]*sd.Instance 55 } 56 57 func (s *AWSSDClientStub) CreateService(input *sd.CreateServiceInput) (*sd.CreateServiceOutput, error) { 58 srv := &sd.Service{ 59 Id: aws.String(strconv.Itoa(rand.Intn(10000))), 60 DnsConfig: input.DnsConfig, 61 Name: input.Name, 62 Description: input.Description, 63 CreateDate: aws.Time(time.Now()), 64 CreatorRequestId: input.CreatorRequestId, 65 } 66 67 nsServices, ok := s.services[*input.NamespaceId] 68 if !ok { 69 nsServices = make(map[string]*sd.Service) 70 s.services[*input.NamespaceId] = nsServices 71 } 72 nsServices[*srv.Id] = srv 73 74 return &sd.CreateServiceOutput{ 75 Service: srv, 76 }, nil 77 } 78 79 func (s *AWSSDClientStub) DeregisterInstance(input *sd.DeregisterInstanceInput) (*sd.DeregisterInstanceOutput, error) { 80 serviceInstances := s.instances[*input.ServiceId] 81 delete(serviceInstances, *input.InstanceId) 82 83 return &sd.DeregisterInstanceOutput{}, nil 84 } 85 86 func (s *AWSSDClientStub) GetService(input *sd.GetServiceInput) (*sd.GetServiceOutput, error) { 87 for _, entry := range s.services { 88 srv, ok := entry[*input.Id] 89 if ok { 90 return &sd.GetServiceOutput{ 91 Service: srv, 92 }, nil 93 } 94 } 95 96 return nil, errors.New("service not found") 97 } 98 99 func (s *AWSSDClientStub) DiscoverInstancesWithContext(ctx context.Context, input *sd.DiscoverInstancesInput, opts ...request.Option) (*sd.DiscoverInstancesOutput, error) { 100 instances := make([]*sd.HttpInstanceSummary, 0) 101 102 var foundNs bool 103 for _, ns := range s.namespaces { 104 if aws.StringValue(ns.Name) == aws.StringValue(input.NamespaceName) { 105 foundNs = true 106 107 for _, srv := range s.services[*ns.Id] { 108 if aws.StringValue(srv.Name) == aws.StringValue(input.ServiceName) { 109 for _, inst := range s.instances[*srv.Id] { 110 instances = append(instances, instanceToHTTPInstanceSummary(inst)) 111 } 112 } 113 } 114 } 115 } 116 117 if !foundNs { 118 return nil, ErrNamespaceNotFound 119 } 120 121 return &sd.DiscoverInstancesOutput{ 122 Instances: instances, 123 }, nil 124 } 125 126 func (s *AWSSDClientStub) ListNamespacesPages(input *sd.ListNamespacesInput, fn func(*sd.ListNamespacesOutput, bool) bool) error { 127 namespaces := make([]*sd.NamespaceSummary, 0) 128 129 filter := input.Filters[0] 130 131 for _, ns := range s.namespaces { 132 if filter != nil && *filter.Name == sd.NamespaceFilterNameType { 133 if *ns.Type != *filter.Values[0] { 134 // skip namespaces not matching filter 135 continue 136 } 137 } 138 namespaces = append(namespaces, namespaceToNamespaceSummary(ns)) 139 } 140 141 fn(&sd.ListNamespacesOutput{ 142 Namespaces: namespaces, 143 }, true) 144 145 return nil 146 } 147 148 func (s *AWSSDClientStub) ListServicesPages(input *sd.ListServicesInput, fn func(*sd.ListServicesOutput, bool) bool) error { 149 services := make([]*sd.ServiceSummary, 0) 150 151 // get namespace filter 152 filter := input.Filters[0] 153 if filter == nil || *filter.Name != sd.ServiceFilterNameNamespaceId { 154 return errors.New("missing namespace filter") 155 } 156 nsID := filter.Values[0] 157 158 for _, srv := range s.services[*nsID] { 159 services = append(services, serviceToServiceSummary(srv)) 160 } 161 162 fn(&sd.ListServicesOutput{ 163 Services: services, 164 }, true) 165 166 return nil 167 } 168 169 func (s *AWSSDClientStub) RegisterInstance(input *sd.RegisterInstanceInput) (*sd.RegisterInstanceOutput, error) { 170 srvInstances, ok := s.instances[*input.ServiceId] 171 if !ok { 172 srvInstances = make(map[string]*sd.Instance) 173 s.instances[*input.ServiceId] = srvInstances 174 } 175 176 srvInstances[*input.InstanceId] = &sd.Instance{ 177 Id: input.InstanceId, 178 Attributes: input.Attributes, 179 CreatorRequestId: input.CreatorRequestId, 180 } 181 182 return &sd.RegisterInstanceOutput{}, nil 183 } 184 185 func (s *AWSSDClientStub) UpdateService(input *sd.UpdateServiceInput) (*sd.UpdateServiceOutput, error) { 186 out, err := s.GetService(&sd.GetServiceInput{Id: input.Id}) 187 if err != nil { 188 return nil, err 189 } 190 191 origSrv := out.Service 192 updateSrv := input.Service 193 194 origSrv.Description = updateSrv.Description 195 origSrv.DnsConfig.DnsRecords = updateSrv.DnsConfig.DnsRecords 196 197 return &sd.UpdateServiceOutput{}, nil 198 } 199 200 func (s *AWSSDClientStub) DeleteService(input *sd.DeleteServiceInput) (*sd.DeleteServiceOutput, error) { 201 out, err := s.GetService(&sd.GetServiceInput{Id: input.Id}) 202 if err != nil { 203 return nil, err 204 } 205 206 service := out.Service 207 namespace := s.services[*service.NamespaceId] 208 delete(namespace, *input.Id) 209 210 return &sd.DeleteServiceOutput{}, nil 211 } 212 213 func newTestAWSSDProvider(api AWSSDClient, domainFilter endpoint.DomainFilter, namespaceTypeFilter, ownerID string) *AWSSDProvider { 214 return &AWSSDProvider{ 215 client: api, 216 dryRun: false, 217 namespaceFilter: domainFilter, 218 namespaceTypeFilter: newSdNamespaceFilter(namespaceTypeFilter), 219 cleanEmptyService: true, 220 ownerID: ownerID, 221 } 222 } 223 224 // nolint: deadcode 225 // used for unit test 226 func instanceToHTTPInstanceSummary(instance *sd.Instance) *sd.HttpInstanceSummary { 227 if instance == nil { 228 return nil 229 } 230 231 return &sd.HttpInstanceSummary{ 232 InstanceId: instance.Id, 233 Attributes: instance.Attributes, 234 } 235 } 236 237 func TestAWSSDProvider_Records(t *testing.T) { 238 namespaces := map[string]*sd.Namespace{ 239 "private": { 240 Id: aws.String("private"), 241 Name: aws.String("private.com"), 242 Type: aws.String(sd.NamespaceTypeDnsPrivate), 243 }, 244 } 245 246 services := map[string]map[string]*sd.Service{ 247 "private": { 248 "a-srv": { 249 Id: aws.String("a-srv"), 250 Name: aws.String("service1"), 251 Description: aws.String("owner-id"), 252 DnsConfig: &sd.DnsConfig{ 253 NamespaceId: aws.String("private"), 254 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 255 DnsRecords: []*sd.DnsRecord{{ 256 Type: aws.String(sd.RecordTypeA), 257 TTL: aws.Int64(100), 258 }}, 259 }, 260 }, 261 "alias-srv": { 262 Id: aws.String("alias-srv"), 263 Name: aws.String("service2"), 264 Description: aws.String("owner-id"), 265 DnsConfig: &sd.DnsConfig{ 266 NamespaceId: aws.String("private"), 267 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 268 DnsRecords: []*sd.DnsRecord{{ 269 Type: aws.String(sd.RecordTypeA), 270 TTL: aws.Int64(100), 271 }}, 272 }, 273 }, 274 "cname-srv": { 275 Id: aws.String("cname-srv"), 276 Name: aws.String("service3"), 277 Description: aws.String("owner-id"), 278 DnsConfig: &sd.DnsConfig{ 279 NamespaceId: aws.String("private"), 280 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 281 DnsRecords: []*sd.DnsRecord{{ 282 Type: aws.String(sd.RecordTypeCname), 283 TTL: aws.Int64(80), 284 }}, 285 }, 286 }, 287 }, 288 } 289 290 instances := map[string]map[string]*sd.Instance{ 291 "a-srv": { 292 "1.2.3.4": { 293 Id: aws.String("1.2.3.4"), 294 Attributes: map[string]*string{ 295 sdInstanceAttrIPV4: aws.String("1.2.3.4"), 296 }, 297 }, 298 "1.2.3.5": { 299 Id: aws.String("1.2.3.5"), 300 Attributes: map[string]*string{ 301 sdInstanceAttrIPV4: aws.String("1.2.3.5"), 302 }, 303 }, 304 }, 305 "alias-srv": { 306 "load-balancer.us-east-1.elb.amazonaws.com": { 307 Id: aws.String("load-balancer.us-east-1.elb.amazonaws.com"), 308 Attributes: map[string]*string{ 309 sdInstanceAttrAlias: aws.String("load-balancer.us-east-1.elb.amazonaws.com"), 310 }, 311 }, 312 }, 313 "cname-srv": { 314 "cname.target.com": { 315 Id: aws.String("cname.target.com"), 316 Attributes: map[string]*string{ 317 sdInstanceAttrCname: aws.String("cname.target.com"), 318 }, 319 }, 320 }, 321 } 322 323 expectedEndpoints := []*endpoint.Endpoint{ 324 {DNSName: "service1.private.com", Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5"}, RecordType: endpoint.RecordTypeA, RecordTTL: 100, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}}, 325 {DNSName: "service2.private.com", Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}}, 326 {DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 80, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}}, 327 } 328 329 api := &AWSSDClientStub{ 330 namespaces: namespaces, 331 services: services, 332 instances: instances, 333 } 334 335 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 336 337 endpoints, _ := provider.Records(context.Background()) 338 339 assert.True(t, testutils.SameEndpoints(expectedEndpoints, endpoints), "expected and actual endpoints don't match, expected=%v, actual=%v", expectedEndpoints, endpoints) 340 } 341 342 func TestAWSSDProvider_ApplyChanges(t *testing.T) { 343 namespaces := map[string]*sd.Namespace{ 344 "private": { 345 Id: aws.String("private"), 346 Name: aws.String("private.com"), 347 Type: aws.String(sd.NamespaceTypeDnsPrivate), 348 }, 349 } 350 351 api := &AWSSDClientStub{ 352 namespaces: namespaces, 353 services: make(map[string]map[string]*sd.Service), 354 instances: make(map[string]map[string]*sd.Instance), 355 } 356 357 expectedEndpoints := []*endpoint.Endpoint{ 358 {DNSName: "service1.private.com", Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5"}, RecordType: endpoint.RecordTypeA, RecordTTL: 60}, 359 {DNSName: "service2.private.com", Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 80}, 360 {DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100}, 361 } 362 363 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 364 365 ctx := context.Background() 366 367 // apply creates 368 provider.ApplyChanges(ctx, &plan.Changes{ 369 Create: expectedEndpoints, 370 }) 371 372 // make sure services were created 373 assert.Len(t, api.services["private"], 3) 374 existingServices, _ := provider.ListServicesByNamespaceID(namespaces["private"].Id) 375 assert.NotNil(t, existingServices["service1"]) 376 assert.NotNil(t, existingServices["service2"]) 377 assert.NotNil(t, existingServices["service3"]) 378 379 // make sure instances were registered 380 endpoints, _ := provider.Records(ctx) 381 assert.True(t, testutils.SameEndpoints(expectedEndpoints, endpoints), "expected and actual endpoints don't match, expected=%v, actual=%v", expectedEndpoints, endpoints) 382 383 ctx = context.Background() 384 // apply deletes 385 provider.ApplyChanges(ctx, &plan.Changes{ 386 Delete: expectedEndpoints, 387 }) 388 389 // make sure all instances are gone 390 endpoints, _ = provider.Records(ctx) 391 assert.Empty(t, endpoints) 392 } 393 394 func TestAWSSDProvider_ListNamespaces(t *testing.T) { 395 namespaces := map[string]*sd.Namespace{ 396 "private": { 397 Id: aws.String("private"), 398 Name: aws.String("private.com"), 399 Type: aws.String(sd.NamespaceTypeDnsPrivate), 400 }, 401 "public": { 402 Id: aws.String("public"), 403 Name: aws.String("public.com"), 404 Type: aws.String(sd.NamespaceTypeDnsPublic), 405 }, 406 } 407 408 api := &AWSSDClientStub{ 409 namespaces: namespaces, 410 } 411 412 for _, tc := range []struct { 413 msg string 414 domainFilter endpoint.DomainFilter 415 namespaceTypeFilter string 416 expectedNamespaces []*sd.NamespaceSummary 417 }{ 418 {"public filter", endpoint.NewDomainFilter([]string{}), "public", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["public"])}}, 419 {"private filter", endpoint.NewDomainFilter([]string{}), "private", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["private"])}}, 420 {"domain filter", endpoint.NewDomainFilter([]string{"public.com"}), "", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["public"])}}, 421 {"non-existing domain", endpoint.NewDomainFilter([]string{"xxx.com"}), "", []*sd.NamespaceSummary{}}, 422 } { 423 provider := newTestAWSSDProvider(api, tc.domainFilter, tc.namespaceTypeFilter, "") 424 425 result, err := provider.ListNamespaces() 426 require.NoError(t, err) 427 428 expectedMap := make(map[string]*sd.NamespaceSummary) 429 resultMap := make(map[string]*sd.NamespaceSummary) 430 for _, ns := range tc.expectedNamespaces { 431 expectedMap[*ns.Id] = ns 432 } 433 for _, ns := range result { 434 resultMap[*ns.Id] = ns 435 } 436 437 if !reflect.DeepEqual(resultMap, expectedMap) { 438 t.Errorf("AWSSDProvider.ListNamespaces() error = %v, wantErr %v", result, tc.expectedNamespaces) 439 } 440 } 441 } 442 443 func TestAWSSDProvider_ListServicesByNamespace(t *testing.T) { 444 namespaces := map[string]*sd.Namespace{ 445 "private": { 446 Id: aws.String("private"), 447 Name: aws.String("private.com"), 448 Type: aws.String(sd.NamespaceTypeDnsPrivate), 449 }, 450 "public": { 451 Id: aws.String("public"), 452 Name: aws.String("public.com"), 453 Type: aws.String(sd.NamespaceTypeDnsPublic), 454 }, 455 } 456 457 services := map[string]map[string]*sd.Service{ 458 "private": { 459 "srv1": { 460 Id: aws.String("srv1"), 461 Name: aws.String("service1"), 462 NamespaceId: aws.String("private"), 463 }, 464 "srv2": { 465 Id: aws.String("srv2"), 466 Name: aws.String("service2"), 467 NamespaceId: aws.String("private"), 468 }, 469 }, 470 "public": { 471 "srv3": { 472 Id: aws.String("srv3"), 473 Name: aws.String("service3"), 474 NamespaceId: aws.String("public"), 475 }, 476 }, 477 } 478 479 api := &AWSSDClientStub{ 480 namespaces: namespaces, 481 services: services, 482 } 483 484 for _, tc := range []struct { 485 expectedServices map[string]*sd.Service 486 }{ 487 {map[string]*sd.Service{"service1": services["private"]["srv1"], "service2": services["private"]["srv2"]}}, 488 } { 489 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 490 491 result, err := provider.ListServicesByNamespaceID(namespaces["private"].Id) 492 require.NoError(t, err) 493 assert.Equal(t, tc.expectedServices, result) 494 } 495 } 496 497 func TestAWSSDProvider_CreateService(t *testing.T) { 498 namespaces := map[string]*sd.Namespace{ 499 "private": { 500 Id: aws.String("private"), 501 Name: aws.String("private.com"), 502 Type: aws.String(sd.NamespaceTypeDnsPrivate), 503 }, 504 } 505 506 api := &AWSSDClientStub{ 507 namespaces: namespaces, 508 services: make(map[string]map[string]*sd.Service), 509 } 510 511 expectedServices := make(map[string]*sd.Service) 512 513 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 514 515 // A type 516 provider.CreateService(aws.String("private"), aws.String("A-srv"), &endpoint.Endpoint{ 517 RecordType: endpoint.RecordTypeA, 518 RecordTTL: 60, 519 Targets: endpoint.Targets{"1.2.3.4"}, 520 }) 521 expectedServices["A-srv"] = &sd.Service{ 522 Name: aws.String("A-srv"), 523 DnsConfig: &sd.DnsConfig{ 524 RoutingPolicy: aws.String(sd.RoutingPolicyMultivalue), 525 DnsRecords: []*sd.DnsRecord{{ 526 Type: aws.String(sd.RecordTypeA), 527 TTL: aws.Int64(60), 528 }}, 529 }, 530 NamespaceId: aws.String("private"), 531 } 532 533 // CNAME type 534 provider.CreateService(aws.String("private"), aws.String("CNAME-srv"), &endpoint.Endpoint{ 535 RecordType: endpoint.RecordTypeCNAME, 536 RecordTTL: 80, 537 Targets: endpoint.Targets{"cname.target.com"}, 538 }) 539 expectedServices["CNAME-srv"] = &sd.Service{ 540 Name: aws.String("CNAME-srv"), 541 DnsConfig: &sd.DnsConfig{ 542 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 543 DnsRecords: []*sd.DnsRecord{{ 544 Type: aws.String(sd.RecordTypeCname), 545 TTL: aws.Int64(80), 546 }}, 547 }, 548 NamespaceId: aws.String("private"), 549 } 550 551 // ALIAS type 552 provider.CreateService(aws.String("private"), aws.String("ALIAS-srv"), &endpoint.Endpoint{ 553 RecordType: endpoint.RecordTypeCNAME, 554 RecordTTL: 100, 555 Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com"}, 556 }) 557 expectedServices["ALIAS-srv"] = &sd.Service{ 558 Name: aws.String("ALIAS-srv"), 559 DnsConfig: &sd.DnsConfig{ 560 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 561 DnsRecords: []*sd.DnsRecord{{ 562 Type: aws.String(sd.RecordTypeA), 563 TTL: aws.Int64(100), 564 }}, 565 }, 566 NamespaceId: aws.String("private"), 567 } 568 569 validateAWSSDServicesMapsEqual(t, expectedServices, api.services["private"]) 570 } 571 572 func validateAWSSDServicesMapsEqual(t *testing.T, expected map[string]*sd.Service, services map[string]*sd.Service) { 573 require.Len(t, services, len(expected)) 574 575 for _, srv := range services { 576 validateAWSSDServicesEqual(t, expected[*srv.Name], srv) 577 } 578 } 579 580 func validateAWSSDServicesEqual(t *testing.T, expected *sd.Service, srv *sd.Service) { 581 assert.Equal(t, aws.StringValue(expected.Description), aws.StringValue(srv.Description)) 582 assert.Equal(t, aws.StringValue(expected.Name), aws.StringValue(srv.Name)) 583 assert.True(t, reflect.DeepEqual(*expected.DnsConfig, *srv.DnsConfig)) 584 } 585 586 func TestAWSSDProvider_UpdateService(t *testing.T) { 587 namespaces := map[string]*sd.Namespace{ 588 "private": { 589 Id: aws.String("private"), 590 Name: aws.String("private.com"), 591 Type: aws.String(sd.NamespaceTypeDnsPrivate), 592 }, 593 } 594 595 services := map[string]map[string]*sd.Service{ 596 "private": { 597 "srv1": { 598 Id: aws.String("srv1"), 599 Name: aws.String("service1"), 600 DnsConfig: &sd.DnsConfig{ 601 NamespaceId: aws.String("private"), 602 RoutingPolicy: aws.String(sd.RoutingPolicyMultivalue), 603 DnsRecords: []*sd.DnsRecord{{ 604 Type: aws.String(sd.RecordTypeA), 605 TTL: aws.Int64(60), 606 }}, 607 }, 608 }, 609 }, 610 } 611 612 api := &AWSSDClientStub{ 613 namespaces: namespaces, 614 services: services, 615 } 616 617 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 618 619 // update service with different TTL 620 provider.UpdateService(services["private"]["srv1"], &endpoint.Endpoint{ 621 RecordType: endpoint.RecordTypeA, 622 RecordTTL: 100, 623 }) 624 625 assert.Equal(t, int64(100), *api.services["private"]["srv1"].DnsConfig.DnsRecords[0].TTL) 626 } 627 628 func TestAWSSDProvider_DeleteService(t *testing.T) { 629 namespaces := map[string]*sd.Namespace{ 630 "private": { 631 Id: aws.String("private"), 632 Name: aws.String("private.com"), 633 Type: aws.String(sd.NamespaceTypeDnsPrivate), 634 }, 635 } 636 637 services := map[string]map[string]*sd.Service{ 638 "private": { 639 "srv1": { 640 Id: aws.String("srv1"), 641 Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"), 642 Name: aws.String("service1"), 643 NamespaceId: aws.String("private"), 644 }, 645 "srv2": { 646 Id: aws.String("srv2"), 647 Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"), 648 Name: aws.String("service2"), 649 NamespaceId: aws.String("private"), 650 }, 651 "srv3": { 652 Id: aws.String("srv3"), 653 Description: aws.String("heritage=external-dns,external-dns/owner=owner-id,external-dns/resource=virtualservice/grpc-server/validate-grpc-server"), 654 Name: aws.String("service3"), 655 NamespaceId: aws.String("private"), 656 }, 657 }, 658 } 659 660 api := &AWSSDClientStub{ 661 namespaces: namespaces, 662 services: services, 663 } 664 665 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "owner-id") 666 667 // delete first service 668 err := provider.DeleteService(services["private"]["srv1"]) 669 assert.NoError(t, err) 670 assert.Len(t, api.services["private"], 2) 671 672 // delete third service 673 err1 := provider.DeleteService(services["private"]["srv3"]) 674 assert.NoError(t, err1) 675 assert.Len(t, api.services["private"], 1) 676 677 expectedServices := map[string]*sd.Service{ 678 "srv2": { 679 Id: aws.String("srv2"), 680 Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"), 681 Name: aws.String("service2"), 682 NamespaceId: aws.String("private"), 683 }, 684 } 685 686 assert.Equal(t, expectedServices, api.services["private"]) 687 } 688 689 func TestAWSSDProvider_RegisterInstance(t *testing.T) { 690 namespaces := map[string]*sd.Namespace{ 691 "private": { 692 Id: aws.String("private"), 693 Name: aws.String("private.com"), 694 Type: aws.String(sd.NamespaceTypeDnsPrivate), 695 }, 696 } 697 698 services := map[string]map[string]*sd.Service{ 699 "private": { 700 "a-srv": { 701 Id: aws.String("a-srv"), 702 Name: aws.String("service1"), 703 DnsConfig: &sd.DnsConfig{ 704 NamespaceId: aws.String("private"), 705 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 706 DnsRecords: []*sd.DnsRecord{{ 707 Type: aws.String(sd.RecordTypeA), 708 TTL: aws.Int64(60), 709 }}, 710 }, 711 }, 712 "cname-srv": { 713 Id: aws.String("cname-srv"), 714 Name: aws.String("service2"), 715 DnsConfig: &sd.DnsConfig{ 716 NamespaceId: aws.String("private"), 717 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 718 DnsRecords: []*sd.DnsRecord{{ 719 Type: aws.String(sd.RecordTypeCname), 720 TTL: aws.Int64(60), 721 }}, 722 }, 723 }, 724 "alias-srv": { 725 Id: aws.String("alias-srv"), 726 Name: aws.String("service3"), 727 DnsConfig: &sd.DnsConfig{ 728 NamespaceId: aws.String("private"), 729 RoutingPolicy: aws.String(sd.RoutingPolicyWeighted), 730 DnsRecords: []*sd.DnsRecord{{ 731 Type: aws.String(sd.RecordTypeA), 732 TTL: aws.Int64(60), 733 }}, 734 }, 735 }, 736 }, 737 } 738 739 api := &AWSSDClientStub{ 740 namespaces: namespaces, 741 services: services, 742 instances: make(map[string]map[string]*sd.Instance), 743 } 744 745 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 746 747 expectedInstances := make(map[string]*sd.Instance) 748 749 // IP-based instance 750 provider.RegisterInstance(services["private"]["a-srv"], &endpoint.Endpoint{ 751 RecordType: endpoint.RecordTypeA, 752 DNSName: "service1.private.com.", 753 RecordTTL: 300, 754 Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5"}, 755 }) 756 expectedInstances["1.2.3.4"] = &sd.Instance{ 757 Id: aws.String("1.2.3.4"), 758 Attributes: map[string]*string{ 759 sdInstanceAttrIPV4: aws.String("1.2.3.4"), 760 }, 761 } 762 expectedInstances["1.2.3.5"] = &sd.Instance{ 763 Id: aws.String("1.2.3.5"), 764 Attributes: map[string]*string{ 765 sdInstanceAttrIPV4: aws.String("1.2.3.5"), 766 }, 767 } 768 769 // AWS ELB instance (ALIAS) 770 provider.RegisterInstance(services["private"]["alias-srv"], &endpoint.Endpoint{ 771 RecordType: endpoint.RecordTypeCNAME, 772 DNSName: "service1.private.com.", 773 RecordTTL: 300, 774 Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com", "load-balancer.us-west-2.elb.amazonaws.com"}, 775 }) 776 expectedInstances["load-balancer.us-east-1.elb.amazonaws.com"] = &sd.Instance{ 777 Id: aws.String("load-balancer.us-east-1.elb.amazonaws.com"), 778 Attributes: map[string]*string{ 779 sdInstanceAttrAlias: aws.String("load-balancer.us-east-1.elb.amazonaws.com"), 780 }, 781 } 782 expectedInstances["load-balancer.us-west-2.elb.amazonaws.com"] = &sd.Instance{ 783 Id: aws.String("load-balancer.us-west-2.elb.amazonaws.com"), 784 Attributes: map[string]*string{ 785 sdInstanceAttrAlias: aws.String("load-balancer.us-west-2.elb.amazonaws.com"), 786 }, 787 } 788 789 // AWS NLB instance (ALIAS) 790 provider.RegisterInstance(services["private"]["alias-srv"], &endpoint.Endpoint{ 791 RecordType: endpoint.RecordTypeCNAME, 792 DNSName: "service1.private.com.", 793 RecordTTL: 300, 794 Targets: endpoint.Targets{"load-balancer.elb.us-west-2.amazonaws.com"}, 795 }) 796 expectedInstances["load-balancer.elb.us-west-2.amazonaws.com"] = &sd.Instance{ 797 Id: aws.String("load-balancer.elb.us-west-2.amazonaws.com"), 798 Attributes: map[string]*string{ 799 sdInstanceAttrAlias: aws.String("load-balancer.elb.us-west-2.amazonaws.com"), 800 }, 801 } 802 803 // CNAME instance 804 provider.RegisterInstance(services["private"]["cname-srv"], &endpoint.Endpoint{ 805 RecordType: endpoint.RecordTypeCNAME, 806 DNSName: "service2.private.com.", 807 RecordTTL: 300, 808 Targets: endpoint.Targets{"cname.target.com"}, 809 }) 810 expectedInstances["cname.target.com"] = &sd.Instance{ 811 Id: aws.String("cname.target.com"), 812 Attributes: map[string]*string{ 813 sdInstanceAttrCname: aws.String("cname.target.com"), 814 }, 815 } 816 817 // validate instances 818 for _, srvInst := range api.instances { 819 for id, inst := range srvInst { 820 if !reflect.DeepEqual(*expectedInstances[id], *inst) { 821 t.Errorf("Instances don't match, expected = %v, actual %v", *expectedInstances[id], *inst) 822 } 823 } 824 } 825 } 826 827 func TestAWSSDProvider_DeregisterInstance(t *testing.T) { 828 namespaces := map[string]*sd.Namespace{ 829 "private": { 830 Id: aws.String("private"), 831 Name: aws.String("private.com"), 832 Type: aws.String(sd.NamespaceTypeDnsPrivate), 833 }, 834 } 835 836 services := map[string]map[string]*sd.Service{ 837 "private": { 838 "srv1": { 839 Id: aws.String("srv1"), 840 Name: aws.String("service1"), 841 }, 842 }, 843 } 844 845 instances := map[string]map[string]*sd.Instance{ 846 "srv1": { 847 "1.2.3.4": { 848 Id: aws.String("1.2.3.4"), 849 Attributes: map[string]*string{ 850 sdInstanceAttrIPV4: aws.String("1.2.3.4"), 851 }, 852 }, 853 }, 854 } 855 856 api := &AWSSDClientStub{ 857 namespaces: namespaces, 858 services: services, 859 instances: instances, 860 } 861 862 provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "") 863 864 provider.DeregisterInstance(services["private"]["srv1"], endpoint.NewEndpoint("srv1.private.com.", endpoint.RecordTypeA, "1.2.3.4")) 865 866 assert.Len(t, instances["srv1"], 0) 867 }