external-dns/provider/awssd/aws_sd_test.go
Artem Voronin 4eb7e7513a
AWSSD: Utilize DiscoverInstances instead of ListInstances (#2506)
* AWSSD: Utilize DiscoverInstances instead of ListInstances

* Fixed stylecheck

Renamed instanceToHttpInstanceSummary to instanceToHTTPInstanceSummary

* awssd use DiscoverInstancesWithContext from client directly

* updated awssd tests

fix DiscoverInstancesWithContext to implement AWSSDClient interface
drop old test, no need to cover direct calls to aws clent methods
moved instanceToHTTPInstanceSummary to _test file

* awssd log error on failed DeleteService

* updated awssd tests

* fix missing import

* awssd tests handle not found NS with DiscoverInstancesWithContext

* Update provider/awssd/aws_sd_test.go

Co-authored-by: John Gardiner Myers <jgmyers@proofpoint.com>

* Update provider/awssd/aws_sd_test.go

Co-authored-by: John Gardiner Myers <jgmyers@proofpoint.com>

---------

Co-authored-by: John Gardiner Myers <jgmyers@proofpoint.com>
2023-10-04 02:33:43 +02:00

868 lines
25 KiB
Go

/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package awssd
import (
"context"
"errors"
"math/rand"
"reflect"
"strconv"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
sd "github.com/aws/aws-sdk-go/service/servicediscovery"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/internal/testutils"
"sigs.k8s.io/external-dns/plan"
)
// Compile time check for interface conformance
var _ AWSSDClient = &AWSSDClientStub{}
var (
ErrNamespaceNotFound = errors.New("Namespace not found")
)
type AWSSDClientStub struct {
// map[namespace_id]namespace
namespaces map[string]*sd.Namespace
// map[namespace_id] => map[service_id]instance
services map[string]map[string]*sd.Service
// map[service_id] => map[inst_id]instance
instances map[string]map[string]*sd.Instance
}
func (s *AWSSDClientStub) CreateService(input *sd.CreateServiceInput) (*sd.CreateServiceOutput, error) {
srv := &sd.Service{
Id: aws.String(strconv.Itoa(rand.Intn(10000))),
DnsConfig: input.DnsConfig,
Name: input.Name,
Description: input.Description,
CreateDate: aws.Time(time.Now()),
CreatorRequestId: input.CreatorRequestId,
}
nsServices, ok := s.services[*input.NamespaceId]
if !ok {
nsServices = make(map[string]*sd.Service)
s.services[*input.NamespaceId] = nsServices
}
nsServices[*srv.Id] = srv
return &sd.CreateServiceOutput{
Service: srv,
}, nil
}
func (s *AWSSDClientStub) DeregisterInstance(input *sd.DeregisterInstanceInput) (*sd.DeregisterInstanceOutput, error) {
serviceInstances := s.instances[*input.ServiceId]
delete(serviceInstances, *input.InstanceId)
return &sd.DeregisterInstanceOutput{}, nil
}
func (s *AWSSDClientStub) GetService(input *sd.GetServiceInput) (*sd.GetServiceOutput, error) {
for _, entry := range s.services {
srv, ok := entry[*input.Id]
if ok {
return &sd.GetServiceOutput{
Service: srv,
}, nil
}
}
return nil, errors.New("service not found")
}
func (s *AWSSDClientStub) DiscoverInstancesWithContext(ctx context.Context, input *sd.DiscoverInstancesInput, opts ...request.Option) (*sd.DiscoverInstancesOutput, error) {
instances := make([]*sd.HttpInstanceSummary, 0)
var foundNs bool
for _, ns := range s.namespaces {
if aws.StringValue(ns.Name) == aws.StringValue(input.NamespaceName) {
foundNs = true
for _, srv := range s.services[*ns.Id] {
if aws.StringValue(srv.Name) == aws.StringValue(input.ServiceName) {
for _, inst := range s.instances[*srv.Id] {
instances = append(instances, instanceToHTTPInstanceSummary(inst))
}
}
}
}
}
if !foundNs {
return nil, ErrNamespaceNotFound
}
return &sd.DiscoverInstancesOutput{
Instances: instances,
}, nil
}
func (s *AWSSDClientStub) ListNamespacesPages(input *sd.ListNamespacesInput, fn func(*sd.ListNamespacesOutput, bool) bool) error {
namespaces := make([]*sd.NamespaceSummary, 0)
filter := input.Filters[0]
for _, ns := range s.namespaces {
if filter != nil && *filter.Name == sd.NamespaceFilterNameType {
if *ns.Type != *filter.Values[0] {
// skip namespaces not matching filter
continue
}
}
namespaces = append(namespaces, namespaceToNamespaceSummary(ns))
}
fn(&sd.ListNamespacesOutput{
Namespaces: namespaces,
}, true)
return nil
}
func (s *AWSSDClientStub) ListServicesPages(input *sd.ListServicesInput, fn func(*sd.ListServicesOutput, bool) bool) error {
services := make([]*sd.ServiceSummary, 0)
// get namespace filter
filter := input.Filters[0]
if filter == nil || *filter.Name != sd.ServiceFilterNameNamespaceId {
return errors.New("missing namespace filter")
}
nsID := filter.Values[0]
for _, srv := range s.services[*nsID] {
services = append(services, serviceToServiceSummary(srv))
}
fn(&sd.ListServicesOutput{
Services: services,
}, true)
return nil
}
func (s *AWSSDClientStub) RegisterInstance(input *sd.RegisterInstanceInput) (*sd.RegisterInstanceOutput, error) {
srvInstances, ok := s.instances[*input.ServiceId]
if !ok {
srvInstances = make(map[string]*sd.Instance)
s.instances[*input.ServiceId] = srvInstances
}
srvInstances[*input.InstanceId] = &sd.Instance{
Id: input.InstanceId,
Attributes: input.Attributes,
CreatorRequestId: input.CreatorRequestId,
}
return &sd.RegisterInstanceOutput{}, nil
}
func (s *AWSSDClientStub) UpdateService(input *sd.UpdateServiceInput) (*sd.UpdateServiceOutput, error) {
out, err := s.GetService(&sd.GetServiceInput{Id: input.Id})
if err != nil {
return nil, err
}
origSrv := out.Service
updateSrv := input.Service
origSrv.Description = updateSrv.Description
origSrv.DnsConfig.DnsRecords = updateSrv.DnsConfig.DnsRecords
return &sd.UpdateServiceOutput{}, nil
}
func (s *AWSSDClientStub) DeleteService(input *sd.DeleteServiceInput) (*sd.DeleteServiceOutput, error) {
out, err := s.GetService(&sd.GetServiceInput{Id: input.Id})
if err != nil {
return nil, err
}
service := out.Service
namespace := s.services[*service.NamespaceId]
delete(namespace, *input.Id)
return &sd.DeleteServiceOutput{}, nil
}
func newTestAWSSDProvider(api AWSSDClient, domainFilter endpoint.DomainFilter, namespaceTypeFilter, ownerID string) *AWSSDProvider {
return &AWSSDProvider{
client: api,
dryRun: false,
namespaceFilter: domainFilter,
namespaceTypeFilter: newSdNamespaceFilter(namespaceTypeFilter),
cleanEmptyService: true,
ownerID: ownerID,
}
}
// nolint: deadcode
// used for unit test
func instanceToHTTPInstanceSummary(instance *sd.Instance) *sd.HttpInstanceSummary {
if instance == nil {
return nil
}
return &sd.HttpInstanceSummary{
InstanceId: instance.Id,
Attributes: instance.Attributes,
}
}
func TestAWSSDProvider_Records(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
services := map[string]map[string]*sd.Service{
"private": {
"a-srv": {
Id: aws.String("a-srv"),
Name: aws.String("service1"),
Description: aws.String("owner-id"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(100),
}},
},
},
"alias-srv": {
Id: aws.String("alias-srv"),
Name: aws.String("service2"),
Description: aws.String("owner-id"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(100),
}},
},
},
"cname-srv": {
Id: aws.String("cname-srv"),
Name: aws.String("service3"),
Description: aws.String("owner-id"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeCname),
TTL: aws.Int64(80),
}},
},
},
},
}
instances := map[string]map[string]*sd.Instance{
"a-srv": {
"1.2.3.4": {
Id: aws.String("1.2.3.4"),
Attributes: map[string]*string{
sdInstanceAttrIPV4: aws.String("1.2.3.4"),
},
},
"1.2.3.5": {
Id: aws.String("1.2.3.5"),
Attributes: map[string]*string{
sdInstanceAttrIPV4: aws.String("1.2.3.5"),
},
},
},
"alias-srv": {
"load-balancer.us-east-1.elb.amazonaws.com": {
Id: aws.String("load-balancer.us-east-1.elb.amazonaws.com"),
Attributes: map[string]*string{
sdInstanceAttrAlias: aws.String("load-balancer.us-east-1.elb.amazonaws.com"),
},
},
},
"cname-srv": {
"cname.target.com": {
Id: aws.String("cname.target.com"),
Attributes: map[string]*string{
sdInstanceAttrCname: aws.String("cname.target.com"),
},
},
},
}
expectedEndpoints := []*endpoint.Endpoint{
{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"}},
{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"}},
{DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 80, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: services,
instances: instances,
}
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
endpoints, _ := provider.Records(context.Background())
assert.True(t, testutils.SameEndpoints(expectedEndpoints, endpoints), "expected and actual endpoints don't match, expected=%v, actual=%v", expectedEndpoints, endpoints)
}
func TestAWSSDProvider_ApplyChanges(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: make(map[string]map[string]*sd.Service),
instances: make(map[string]map[string]*sd.Instance),
}
expectedEndpoints := []*endpoint.Endpoint{
{DNSName: "service1.private.com", Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5"}, RecordType: endpoint.RecordTypeA, RecordTTL: 60},
{DNSName: "service2.private.com", Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 80},
{DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100},
}
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
ctx := context.Background()
// apply creates
provider.ApplyChanges(ctx, &plan.Changes{
Create: expectedEndpoints,
})
// make sure services were created
assert.Len(t, api.services["private"], 3)
existingServices, _ := provider.ListServicesByNamespaceID(namespaces["private"].Id)
assert.NotNil(t, existingServices["service1"])
assert.NotNil(t, existingServices["service2"])
assert.NotNil(t, existingServices["service3"])
// make sure instances were registered
endpoints, _ := provider.Records(ctx)
assert.True(t, testutils.SameEndpoints(expectedEndpoints, endpoints), "expected and actual endpoints don't match, expected=%v, actual=%v", expectedEndpoints, endpoints)
ctx = context.Background()
// apply deletes
provider.ApplyChanges(ctx, &plan.Changes{
Delete: expectedEndpoints,
})
// make sure all instances are gone
endpoints, _ = provider.Records(ctx)
assert.Empty(t, endpoints)
}
func TestAWSSDProvider_ListNamespaces(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
"public": {
Id: aws.String("public"),
Name: aws.String("public.com"),
Type: aws.String(sd.NamespaceTypeDnsPublic),
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
}
for _, tc := range []struct {
msg string
domainFilter endpoint.DomainFilter
namespaceTypeFilter string
expectedNamespaces []*sd.NamespaceSummary
}{
{"public filter", endpoint.NewDomainFilter([]string{}), "public", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["public"])}},
{"private filter", endpoint.NewDomainFilter([]string{}), "private", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["private"])}},
{"domain filter", endpoint.NewDomainFilter([]string{"public.com"}), "", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["public"])}},
{"non-existing domain", endpoint.NewDomainFilter([]string{"xxx.com"}), "", []*sd.NamespaceSummary{}},
} {
provider := newTestAWSSDProvider(api, tc.domainFilter, tc.namespaceTypeFilter, "")
result, err := provider.ListNamespaces()
require.NoError(t, err)
expectedMap := make(map[string]*sd.NamespaceSummary)
resultMap := make(map[string]*sd.NamespaceSummary)
for _, ns := range tc.expectedNamespaces {
expectedMap[*ns.Id] = ns
}
for _, ns := range result {
resultMap[*ns.Id] = ns
}
if !reflect.DeepEqual(resultMap, expectedMap) {
t.Errorf("AWSSDProvider.ListNamespaces() error = %v, wantErr %v", result, tc.expectedNamespaces)
}
}
}
func TestAWSSDProvider_ListServicesByNamespace(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
"public": {
Id: aws.String("public"),
Name: aws.String("public.com"),
Type: aws.String(sd.NamespaceTypeDnsPublic),
},
}
services := map[string]map[string]*sd.Service{
"private": {
"srv1": {
Id: aws.String("srv1"),
Name: aws.String("service1"),
NamespaceId: aws.String("private"),
},
"srv2": {
Id: aws.String("srv2"),
Name: aws.String("service2"),
NamespaceId: aws.String("private"),
},
},
"public": {
"srv3": {
Id: aws.String("srv3"),
Name: aws.String("service3"),
NamespaceId: aws.String("public"),
},
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: services,
}
for _, tc := range []struct {
expectedServices map[string]*sd.Service
}{
{map[string]*sd.Service{"service1": services["private"]["srv1"], "service2": services["private"]["srv2"]}},
} {
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
result, err := provider.ListServicesByNamespaceID(namespaces["private"].Id)
require.NoError(t, err)
assert.Equal(t, tc.expectedServices, result)
}
}
func TestAWSSDProvider_CreateService(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: make(map[string]map[string]*sd.Service),
}
expectedServices := make(map[string]*sd.Service)
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
// A type
provider.CreateService(aws.String("private"), aws.String("A-srv"), &endpoint.Endpoint{
RecordType: endpoint.RecordTypeA,
RecordTTL: 60,
Targets: endpoint.Targets{"1.2.3.4"},
})
expectedServices["A-srv"] = &sd.Service{
Name: aws.String("A-srv"),
DnsConfig: &sd.DnsConfig{
RoutingPolicy: aws.String(sd.RoutingPolicyMultivalue),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(60),
}},
},
NamespaceId: aws.String("private"),
}
// CNAME type
provider.CreateService(aws.String("private"), aws.String("CNAME-srv"), &endpoint.Endpoint{
RecordType: endpoint.RecordTypeCNAME,
RecordTTL: 80,
Targets: endpoint.Targets{"cname.target.com"},
})
expectedServices["CNAME-srv"] = &sd.Service{
Name: aws.String("CNAME-srv"),
DnsConfig: &sd.DnsConfig{
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeCname),
TTL: aws.Int64(80),
}},
},
NamespaceId: aws.String("private"),
}
// ALIAS type
provider.CreateService(aws.String("private"), aws.String("ALIAS-srv"), &endpoint.Endpoint{
RecordType: endpoint.RecordTypeCNAME,
RecordTTL: 100,
Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com"},
})
expectedServices["ALIAS-srv"] = &sd.Service{
Name: aws.String("ALIAS-srv"),
DnsConfig: &sd.DnsConfig{
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(100),
}},
},
NamespaceId: aws.String("private"),
}
validateAWSSDServicesMapsEqual(t, expectedServices, api.services["private"])
}
func validateAWSSDServicesMapsEqual(t *testing.T, expected map[string]*sd.Service, services map[string]*sd.Service) {
require.Len(t, services, len(expected))
for _, srv := range services {
validateAWSSDServicesEqual(t, expected[*srv.Name], srv)
}
}
func validateAWSSDServicesEqual(t *testing.T, expected *sd.Service, srv *sd.Service) {
assert.Equal(t, aws.StringValue(expected.Description), aws.StringValue(srv.Description))
assert.Equal(t, aws.StringValue(expected.Name), aws.StringValue(srv.Name))
assert.True(t, reflect.DeepEqual(*expected.DnsConfig, *srv.DnsConfig))
}
func TestAWSSDProvider_UpdateService(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
services := map[string]map[string]*sd.Service{
"private": {
"srv1": {
Id: aws.String("srv1"),
Name: aws.String("service1"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyMultivalue),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(60),
}},
},
},
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: services,
}
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
// update service with different TTL
provider.UpdateService(services["private"]["srv1"], &endpoint.Endpoint{
RecordType: endpoint.RecordTypeA,
RecordTTL: 100,
})
assert.Equal(t, int64(100), *api.services["private"]["srv1"].DnsConfig.DnsRecords[0].TTL)
}
func TestAWSSDProvider_DeleteService(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
services := map[string]map[string]*sd.Service{
"private": {
"srv1": {
Id: aws.String("srv1"),
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"),
Name: aws.String("service1"),
NamespaceId: aws.String("private"),
},
"srv2": {
Id: aws.String("srv2"),
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"),
Name: aws.String("service2"),
NamespaceId: aws.String("private"),
},
"srv3": {
Id: aws.String("srv3"),
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id,external-dns/resource=virtualservice/grpc-server/validate-grpc-server"),
Name: aws.String("service3"),
NamespaceId: aws.String("private"),
},
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: services,
}
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "owner-id")
// delete first service
err := provider.DeleteService(services["private"]["srv1"])
assert.NoError(t, err)
assert.Len(t, api.services["private"], 2)
// delete third service
err1 := provider.DeleteService(services["private"]["srv3"])
assert.NoError(t, err1)
assert.Len(t, api.services["private"], 1)
expectedServices := map[string]*sd.Service{
"srv2": {
Id: aws.String("srv2"),
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"),
Name: aws.String("service2"),
NamespaceId: aws.String("private"),
},
}
assert.Equal(t, expectedServices, api.services["private"])
}
func TestAWSSDProvider_RegisterInstance(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
services := map[string]map[string]*sd.Service{
"private": {
"a-srv": {
Id: aws.String("a-srv"),
Name: aws.String("service1"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(60),
}},
},
},
"cname-srv": {
Id: aws.String("cname-srv"),
Name: aws.String("service2"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeCname),
TTL: aws.Int64(60),
}},
},
},
"alias-srv": {
Id: aws.String("alias-srv"),
Name: aws.String("service3"),
DnsConfig: &sd.DnsConfig{
NamespaceId: aws.String("private"),
RoutingPolicy: aws.String(sd.RoutingPolicyWeighted),
DnsRecords: []*sd.DnsRecord{{
Type: aws.String(sd.RecordTypeA),
TTL: aws.Int64(60),
}},
},
},
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: services,
instances: make(map[string]map[string]*sd.Instance),
}
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
expectedInstances := make(map[string]*sd.Instance)
// IP-based instance
provider.RegisterInstance(services["private"]["a-srv"], &endpoint.Endpoint{
RecordType: endpoint.RecordTypeA,
DNSName: "service1.private.com.",
RecordTTL: 300,
Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5"},
})
expectedInstances["1.2.3.4"] = &sd.Instance{
Id: aws.String("1.2.3.4"),
Attributes: map[string]*string{
sdInstanceAttrIPV4: aws.String("1.2.3.4"),
},
}
expectedInstances["1.2.3.5"] = &sd.Instance{
Id: aws.String("1.2.3.5"),
Attributes: map[string]*string{
sdInstanceAttrIPV4: aws.String("1.2.3.5"),
},
}
// AWS ELB instance (ALIAS)
provider.RegisterInstance(services["private"]["alias-srv"], &endpoint.Endpoint{
RecordType: endpoint.RecordTypeCNAME,
DNSName: "service1.private.com.",
RecordTTL: 300,
Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com", "load-balancer.us-west-2.elb.amazonaws.com"},
})
expectedInstances["load-balancer.us-east-1.elb.amazonaws.com"] = &sd.Instance{
Id: aws.String("load-balancer.us-east-1.elb.amazonaws.com"),
Attributes: map[string]*string{
sdInstanceAttrAlias: aws.String("load-balancer.us-east-1.elb.amazonaws.com"),
},
}
expectedInstances["load-balancer.us-west-2.elb.amazonaws.com"] = &sd.Instance{
Id: aws.String("load-balancer.us-west-2.elb.amazonaws.com"),
Attributes: map[string]*string{
sdInstanceAttrAlias: aws.String("load-balancer.us-west-2.elb.amazonaws.com"),
},
}
// AWS NLB instance (ALIAS)
provider.RegisterInstance(services["private"]["alias-srv"], &endpoint.Endpoint{
RecordType: endpoint.RecordTypeCNAME,
DNSName: "service1.private.com.",
RecordTTL: 300,
Targets: endpoint.Targets{"load-balancer.elb.us-west-2.amazonaws.com"},
})
expectedInstances["load-balancer.elb.us-west-2.amazonaws.com"] = &sd.Instance{
Id: aws.String("load-balancer.elb.us-west-2.amazonaws.com"),
Attributes: map[string]*string{
sdInstanceAttrAlias: aws.String("load-balancer.elb.us-west-2.amazonaws.com"),
},
}
// CNAME instance
provider.RegisterInstance(services["private"]["cname-srv"], &endpoint.Endpoint{
RecordType: endpoint.RecordTypeCNAME,
DNSName: "service2.private.com.",
RecordTTL: 300,
Targets: endpoint.Targets{"cname.target.com"},
})
expectedInstances["cname.target.com"] = &sd.Instance{
Id: aws.String("cname.target.com"),
Attributes: map[string]*string{
sdInstanceAttrCname: aws.String("cname.target.com"),
},
}
// validate instances
for _, srvInst := range api.instances {
for id, inst := range srvInst {
if !reflect.DeepEqual(*expectedInstances[id], *inst) {
t.Errorf("Instances don't match, expected = %v, actual %v", *expectedInstances[id], *inst)
}
}
}
}
func TestAWSSDProvider_DeregisterInstance(t *testing.T) {
namespaces := map[string]*sd.Namespace{
"private": {
Id: aws.String("private"),
Name: aws.String("private.com"),
Type: aws.String(sd.NamespaceTypeDnsPrivate),
},
}
services := map[string]map[string]*sd.Service{
"private": {
"srv1": {
Id: aws.String("srv1"),
Name: aws.String("service1"),
},
},
}
instances := map[string]map[string]*sd.Instance{
"srv1": {
"1.2.3.4": {
Id: aws.String("1.2.3.4"),
Attributes: map[string]*string{
sdInstanceAttrIPV4: aws.String("1.2.3.4"),
},
},
},
}
api := &AWSSDClientStub{
namespaces: namespaces,
services: services,
instances: instances,
}
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
provider.DeregisterInstance(services["private"]["srv1"], endpoint.NewEndpoint("srv1.private.com.", endpoint.RecordTypeA, "1.2.3.4"))
assert.Len(t, instances["srv1"], 0)
}