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 }