sigs.k8s.io/external-dns@v0.14.1/provider/rdns/rdns_test.go (about)

     1  /*
     2  Copyright 2019 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 rdns
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"go.etcd.io/etcd/api/v3/mvccpb"
    28  	clientv3 "go.etcd.io/etcd/client/v3"
    29  
    30  	"sigs.k8s.io/external-dns/endpoint"
    31  	"sigs.k8s.io/external-dns/plan"
    32  )
    33  
    34  type fakeEtcdv3Client struct {
    35  	rs map[string]RDNSRecord
    36  }
    37  
    38  func (c fakeEtcdv3Client) Get(key string) ([]RDNSRecord, error) {
    39  	rs := make([]RDNSRecord, 0)
    40  	for k, v := range c.rs {
    41  		if strings.Contains(k, key) {
    42  			rs = append(rs, v)
    43  		}
    44  	}
    45  	return rs, nil
    46  }
    47  
    48  func (c fakeEtcdv3Client) List(rootDomain string) ([]RDNSRecord, error) {
    49  	var result []RDNSRecord
    50  	for key, value := range c.rs {
    51  		rootPath := rdnsPrefix + dnsNameToKey(rootDomain)
    52  		if strings.HasPrefix(key, rootPath) {
    53  			value.Key = key
    54  			result = append(result, value)
    55  		}
    56  	}
    57  
    58  	r := &clientv3.GetResponse{}
    59  
    60  	for _, v := range result {
    61  		b, err := json.Marshal(v)
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  
    66  		k := &mvccpb.KeyValue{
    67  			Key:   []byte(v.Key),
    68  			Value: b,
    69  		}
    70  
    71  		r.Kvs = append(r.Kvs, k)
    72  	}
    73  
    74  	return c.aggregationRecords(r)
    75  }
    76  
    77  func (c fakeEtcdv3Client) Set(r RDNSRecord) error {
    78  	c.rs[r.Key] = r
    79  	return nil
    80  }
    81  
    82  func (c fakeEtcdv3Client) Delete(key string) error {
    83  	ks := make([]string, 0)
    84  	for k := range c.rs {
    85  		if strings.Contains(k, key) {
    86  			ks = append(ks, k)
    87  		}
    88  	}
    89  
    90  	for _, v := range ks {
    91  		delete(c.rs, v)
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  func TestARecordTranslation(t *testing.T) {
    98  	expectedTarget1 := "1.2.3.4"
    99  	expectedTarget2 := "2.3.4.5"
   100  	expectedTargets := []string{expectedTarget1, expectedTarget2}
   101  	expectedDNSName := "p1xaf1.lb.rancher.cloud"
   102  	expectedRecordType := endpoint.RecordTypeA
   103  
   104  	client := fakeEtcdv3Client{
   105  		map[string]RDNSRecord{
   106  			"/rdnsv3/cloud/rancher/lb/p1xaf1/1_2_3_4": {Host: expectedTarget1},
   107  			"/rdnsv3/cloud/rancher/lb/p1xaf1/2_3_4_5": {Host: expectedTarget2},
   108  		},
   109  	}
   110  
   111  	provider := RDNSProvider{
   112  		client:     client,
   113  		rootDomain: "lb.rancher.cloud",
   114  	}
   115  
   116  	endpoints, err := provider.Records(context.Background())
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  	if len(endpoints) != 1 {
   121  		t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
   122  	}
   123  
   124  	ep := endpoints[0]
   125  	if ep.DNSName != expectedDNSName {
   126  		t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName)
   127  	}
   128  	assert.Contains(t, expectedTargets, ep.Targets[0])
   129  	assert.Contains(t, expectedTargets, ep.Targets[1])
   130  	if ep.RecordType != expectedRecordType {
   131  		t.Errorf("got unexpected DNS record type: %s != %s", ep.RecordType, expectedRecordType)
   132  	}
   133  }
   134  
   135  func TestTXTRecordTranslation(t *testing.T) {
   136  	expectedTarget := "string"
   137  	expectedDNSName := "p1xaf1.lb.rancher.cloud"
   138  	expectedRecordType := endpoint.RecordTypeTXT
   139  
   140  	client := fakeEtcdv3Client{
   141  		map[string]RDNSRecord{
   142  			"/rdnsv3/cloud/rancher/lb/p1xaf1": {Text: expectedTarget},
   143  		},
   144  	}
   145  
   146  	provider := RDNSProvider{
   147  		client:     client,
   148  		rootDomain: "lb.rancher.cloud",
   149  	}
   150  
   151  	endpoints, err := provider.Records(context.Background())
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	if len(endpoints) != 1 {
   156  		t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
   157  	}
   158  	if endpoints[0].DNSName != expectedDNSName {
   159  		t.Errorf("got unexpected DNS name: %s != %s", endpoints[0].DNSName, expectedDNSName)
   160  	}
   161  	if endpoints[0].Targets[0] != expectedTarget {
   162  		t.Errorf("got unexpected DNS target: %s != %s", endpoints[0].Targets[0], expectedTarget)
   163  	}
   164  	if endpoints[0].RecordType != expectedRecordType {
   165  		t.Errorf("got unexpected DNS record type: %s != %s", endpoints[0].RecordType, expectedRecordType)
   166  	}
   167  }
   168  
   169  func TestAWithTXTRecordTranslation(t *testing.T) {
   170  	expectedTargets := map[string]string{
   171  		endpoint.RecordTypeA:   "1.2.3.4",
   172  		endpoint.RecordTypeTXT: "string",
   173  	}
   174  	expectedDNSName := "p1xaf1.lb.rancher.cloud"
   175  
   176  	client := fakeEtcdv3Client{
   177  		map[string]RDNSRecord{
   178  			"/rdnsv3/cloud/rancher/lb/p1xaf1": {Host: "1.2.3.4", Text: "string"},
   179  		},
   180  	}
   181  
   182  	provider := RDNSProvider{
   183  		client:     client,
   184  		rootDomain: "lb.rancher.cloud",
   185  	}
   186  
   187  	endpoints, err := provider.Records(context.Background())
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  	if len(endpoints) != len(expectedTargets) {
   193  		t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
   194  	}
   195  
   196  	for _, ep := range endpoints {
   197  		expectedTarget := expectedTargets[ep.RecordType]
   198  		if expectedTarget == "" {
   199  			t.Errorf("got unexpected DNS record type: %s", ep.RecordType)
   200  			continue
   201  		}
   202  		delete(expectedTargets, ep.RecordType)
   203  
   204  		if ep.DNSName != expectedDNSName {
   205  			t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName)
   206  		}
   207  
   208  		if ep.Targets[0] != expectedTarget {
   209  			t.Errorf("got unexpected DNS target: %s != %s", ep.Targets[0], expectedTarget)
   210  		}
   211  	}
   212  }
   213  
   214  func TestRDNSApplyChanges(t *testing.T) {
   215  	client := fakeEtcdv3Client{
   216  		map[string]RDNSRecord{},
   217  	}
   218  
   219  	provider := RDNSProvider{
   220  		client:     client,
   221  		rootDomain: "lb.rancher.cloud",
   222  	}
   223  
   224  	changes1 := &plan.Changes{
   225  		Create: []*endpoint.Endpoint{
   226  			endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "5.5.5.5", "6.6.6.6"),
   227  			endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeTXT, "string1"),
   228  		},
   229  	}
   230  
   231  	if err := provider.ApplyChanges(context.Background(), changes1); err != nil {
   232  		t.Error(err)
   233  	}
   234  
   235  	expectedRecords1 := map[string]RDNSRecord{
   236  		"/rdnsv3/cloud/rancher/lb/p1xaf1/5_5_5_5": {Host: "5.5.5.5", Text: "string1"},
   237  		"/rdnsv3/cloud/rancher/lb/p1xaf1/6_6_6_6": {Host: "6.6.6.6", Text: "string1"},
   238  	}
   239  
   240  	client.validateRecords(client.rs, expectedRecords1, t)
   241  
   242  	changes2 := &plan.Changes{
   243  		Create: []*endpoint.Endpoint{
   244  			endpoint.NewEndpoint("abx1v1.lb.rancher.cloud", endpoint.RecordTypeA, "7.7.7.7"),
   245  		},
   246  		UpdateNew: []*endpoint.Endpoint{
   247  			endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "8.8.8.8", "9.9.9.9"),
   248  		},
   249  	}
   250  
   251  	records, _ := provider.Records(context.Background())
   252  	for _, ep := range records {
   253  		if ep.DNSName == "p1xaf1.lb.rancher.cloud" {
   254  			changes2.UpdateOld = append(changes2.UpdateOld, ep)
   255  		}
   256  	}
   257  
   258  	if err := provider.ApplyChanges(context.Background(), changes2); err != nil {
   259  		t.Error(err)
   260  	}
   261  
   262  	expectedRecords2 := map[string]RDNSRecord{
   263  		"/rdnsv3/cloud/rancher/lb/p1xaf1/8_8_8_8": {Host: "8.8.8.8"},
   264  		"/rdnsv3/cloud/rancher/lb/p1xaf1/9_9_9_9": {Host: "9.9.9.9"},
   265  		"/rdnsv3/cloud/rancher/lb/abx1v1/7_7_7_7": {Host: "7.7.7.7"},
   266  	}
   267  
   268  	client.validateRecords(client.rs, expectedRecords2, t)
   269  
   270  	changes3 := &plan.Changes{
   271  		Delete: []*endpoint.Endpoint{
   272  			endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "8.8.8.8", "9.9.9.9"),
   273  		},
   274  	}
   275  
   276  	if err := provider.ApplyChanges(context.Background(), changes3); err != nil {
   277  		t.Error(err)
   278  	}
   279  
   280  	expectedRecords3 := map[string]RDNSRecord{
   281  		"/rdnsv3/cloud/rancher/lb/abx1v1/7_7_7_7": {Host: "7.7.7.7"},
   282  	}
   283  
   284  	client.validateRecords(client.rs, expectedRecords3, t)
   285  }
   286  
   287  func (c fakeEtcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRecord, error) {
   288  	var rs []RDNSRecord
   289  	bx := make(map[RDNSRecordType]RDNSRecord)
   290  
   291  	for _, n := range result.Kvs {
   292  		r := new(RDNSRecord)
   293  		if err := json.Unmarshal(n.Value, r); err != nil {
   294  			return nil, fmt.Errorf("%s: %s", n.Key, err.Error())
   295  		}
   296  
   297  		r.Key = string(n.Key)
   298  
   299  		if r.Host == "" && r.Text == "" {
   300  			continue
   301  		}
   302  
   303  		if r.Host != "" {
   304  			c := RDNSRecord{
   305  				AggregationHosts: r.AggregationHosts,
   306  				Host:             r.Host,
   307  				Text:             r.Text,
   308  				TTL:              r.TTL,
   309  				Key:              r.Key,
   310  			}
   311  			n, isContinue := appendRecords(c, endpoint.RecordTypeA, bx, rs)
   312  			if isContinue {
   313  				continue
   314  			}
   315  			rs = n
   316  		}
   317  
   318  		if r.Text != "" && r.Host == "" {
   319  			c := RDNSRecord{
   320  				AggregationHosts: []string{},
   321  				Host:             r.Host,
   322  				Text:             r.Text,
   323  				TTL:              r.TTL,
   324  				Key:              r.Key,
   325  			}
   326  			n, isContinue := appendRecords(c, endpoint.RecordTypeTXT, bx, rs)
   327  			if isContinue {
   328  				continue
   329  			}
   330  			rs = n
   331  		}
   332  	}
   333  
   334  	return rs, nil
   335  }
   336  
   337  func (c fakeEtcdv3Client) validateRecords(rs, expectedRs map[string]RDNSRecord, t *testing.T) {
   338  	if len(rs) != len(expectedRs) {
   339  		t.Errorf("wrong number of records: %d != %d", len(rs), len(expectedRs))
   340  	}
   341  	for key, value := range rs {
   342  		if _, ok := expectedRs[key]; !ok {
   343  			t.Errorf("unexpected record %s", key)
   344  			continue
   345  		}
   346  		expected := expectedRs[key]
   347  		delete(expectedRs, key)
   348  		if value.Host != expected.Host {
   349  			t.Errorf("wrong host for record %s: %s != %s", key, value.Host, expected.Host)
   350  		}
   351  		if value.Text != expected.Text {
   352  			t.Errorf("wrong text for record %s: %s != %s", key, value.Text, expected.Text)
   353  		}
   354  	}
   355  }