sigs.k8s.io/external-dns@v0.14.1/provider/bluecat/bluecat_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package bluecat 15 16 import ( 17 "context" 18 "fmt" 19 "strings" 20 "testing" 21 22 "github.com/stretchr/testify/assert" 23 "sigs.k8s.io/external-dns/endpoint" 24 "sigs.k8s.io/external-dns/internal/testutils" 25 "sigs.k8s.io/external-dns/plan" 26 "sigs.k8s.io/external-dns/provider" 27 api "sigs.k8s.io/external-dns/provider/bluecat/gateway" 28 ) 29 30 type mockGatewayClient struct { 31 mockBluecatZones *[]api.BluecatZone 32 mockBluecatHosts *[]api.BluecatHostRecord 33 mockBluecatCNAMEs *[]api.BluecatCNAMERecord 34 mockBluecatTXTs *[]api.BluecatTXTRecord 35 } 36 37 type Changes struct { 38 // Records that need to be created 39 Create []*endpoint.Endpoint 40 // Records that need to be updated (current data) 41 UpdateOld []*endpoint.Endpoint 42 // Records that need to be updated (desired data) 43 UpdateNew []*endpoint.Endpoint 44 // Records that need to be deleted 45 Delete []*endpoint.Endpoint 46 } 47 48 func (g mockGatewayClient) GetBluecatZones(zoneName string) ([]api.BluecatZone, error) { 49 return *g.mockBluecatZones, nil 50 } 51 52 func (g mockGatewayClient) GetHostRecords(zone string, records *[]api.BluecatHostRecord) error { 53 *records = *g.mockBluecatHosts 54 return nil 55 } 56 57 func (g mockGatewayClient) GetCNAMERecords(zone string, records *[]api.BluecatCNAMERecord) error { 58 *records = *g.mockBluecatCNAMEs 59 return nil 60 } 61 62 func (g mockGatewayClient) GetHostRecord(name string, record *api.BluecatHostRecord) error { 63 for _, currentRecord := range *g.mockBluecatHosts { 64 if currentRecord.Name == strings.Split(name, ".")[0] { 65 *record = currentRecord 66 return nil 67 } 68 } 69 return nil 70 } 71 72 func (g mockGatewayClient) GetCNAMERecord(name string, record *api.BluecatCNAMERecord) error { 73 for _, currentRecord := range *g.mockBluecatCNAMEs { 74 if currentRecord.Name == strings.Split(name, ".")[0] { 75 *record = currentRecord 76 return nil 77 } 78 } 79 return nil 80 } 81 82 func (g mockGatewayClient) CreateHostRecord(zone string, req *api.BluecatCreateHostRecordRequest) (err error) { 83 return nil 84 } 85 86 func (g mockGatewayClient) CreateCNAMERecord(zone string, req *api.BluecatCreateCNAMERecordRequest) (err error) { 87 return nil 88 } 89 90 func (g mockGatewayClient) DeleteHostRecord(name string, zone string) (err error) { 91 *g.mockBluecatHosts = nil 92 return nil 93 } 94 95 func (g mockGatewayClient) DeleteCNAMERecord(name string, zone string) (err error) { 96 *g.mockBluecatCNAMEs = nil 97 return nil 98 } 99 100 func (g mockGatewayClient) GetTXTRecords(zone string, records *[]api.BluecatTXTRecord) error { 101 *records = *g.mockBluecatTXTs 102 return nil 103 } 104 105 func (g mockGatewayClient) GetTXTRecord(name string, record *api.BluecatTXTRecord) error { 106 for _, currentRecord := range *g.mockBluecatTXTs { 107 if currentRecord.Name == name { 108 *record = currentRecord 109 return nil 110 } 111 } 112 return nil 113 } 114 115 func (g mockGatewayClient) CreateTXTRecord(zone string, req *api.BluecatCreateTXTRecordRequest) error { 116 return nil 117 } 118 119 func (g mockGatewayClient) DeleteTXTRecord(name string, zone string) error { 120 *g.mockBluecatTXTs = nil 121 return nil 122 } 123 124 func (g mockGatewayClient) ServerFullDeploy() error { 125 return nil 126 } 127 128 func createMockBluecatZone(fqdn string) api.BluecatZone { 129 props := "absoluteName=" + fqdn 130 return api.BluecatZone{ 131 Properties: props, 132 Name: fqdn, 133 ID: 3, 134 } 135 } 136 137 func createMockBluecatHostRecord(fqdn, target string, ttl int) api.BluecatHostRecord { 138 props := "absoluteName=" + fqdn + "|addresses=" + target + "|ttl=" + fmt.Sprint(ttl) + "|" 139 nameParts := strings.Split(fqdn, ".") 140 return api.BluecatHostRecord{ 141 Name: nameParts[0], 142 Properties: props, 143 ID: 3, 144 } 145 } 146 147 func createMockBluecatCNAME(alias, target string, ttl int) api.BluecatCNAMERecord { 148 props := "absoluteName=" + alias + "|linkedRecordName=" + target + "|ttl=" + fmt.Sprint(ttl) + "|" 149 nameParts := strings.Split(alias, ".") 150 return api.BluecatCNAMERecord{ 151 Name: nameParts[0], 152 Properties: props, 153 } 154 } 155 156 func createMockBluecatTXT(fqdn, txt string) api.BluecatTXTRecord { 157 return api.BluecatTXTRecord{ 158 Name: fqdn, 159 Properties: txt, 160 } 161 } 162 163 func newBluecatProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, client mockGatewayClient) *BluecatProvider { 164 return &BluecatProvider{ 165 domainFilter: domainFilter, 166 zoneIDFilter: zoneIDFilter, 167 dryRun: dryRun, 168 gatewayClient: client, 169 } 170 } 171 172 type bluecatTestData []struct { 173 TestDescription string 174 Endpoints []*endpoint.Endpoint 175 } 176 177 var tests = bluecatTestData{ 178 { 179 "first test case", // TODO: better test description 180 []*endpoint.Endpoint{ 181 { 182 DNSName: "example.com", 183 RecordType: endpoint.RecordTypeA, 184 Targets: endpoint.Targets{"123.123.123.122"}, 185 RecordTTL: endpoint.TTL(30), 186 }, 187 { 188 DNSName: "nginx.example.com", 189 RecordType: endpoint.RecordTypeA, 190 Targets: endpoint.Targets{"123.123.123.123"}, 191 RecordTTL: endpoint.TTL(30), 192 }, 193 { 194 DNSName: "whitespace.example.com", 195 RecordType: endpoint.RecordTypeA, 196 Targets: endpoint.Targets{"123.123.123.124"}, 197 RecordTTL: endpoint.TTL(30), 198 }, 199 { 200 DNSName: "hack.example.com", 201 RecordType: endpoint.RecordTypeCNAME, 202 Targets: endpoint.Targets{"bluecatnetworks.com"}, 203 RecordTTL: endpoint.TTL(30), 204 }, 205 { 206 DNSName: "wack.example.com", 207 RecordType: endpoint.RecordTypeTXT, 208 Targets: endpoint.Targets{"hello"}, 209 Labels: endpoint.Labels{"owner": ""}, 210 }, 211 { 212 DNSName: "sack.example.com", 213 RecordType: endpoint.RecordTypeTXT, 214 Targets: endpoint.Targets{""}, 215 Labels: endpoint.Labels{"owner": ""}, 216 }, 217 { 218 DNSName: "kdb.example.com", 219 RecordType: endpoint.RecordTypeTXT, 220 Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, 221 Labels: endpoint.Labels{"owner": "default"}, 222 }, 223 }, 224 }, 225 } 226 227 func TestBluecatRecords(t *testing.T) { 228 client := mockGatewayClient{ 229 mockBluecatZones: &[]api.BluecatZone{ 230 createMockBluecatZone("example.com"), 231 }, 232 mockBluecatTXTs: &[]api.BluecatTXTRecord{ 233 createMockBluecatTXT("kdb.example.com", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"), 234 createMockBluecatTXT("wack.example.com", "hello"), 235 createMockBluecatTXT("sack.example.com", ""), 236 }, 237 mockBluecatHosts: &[]api.BluecatHostRecord{ 238 createMockBluecatHostRecord("example.com", "123.123.123.122", 30), 239 createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30), 240 createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30), 241 }, 242 mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{ 243 createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30), 244 }, 245 } 246 247 provider := newBluecatProvider( 248 endpoint.NewDomainFilter([]string{"example.com"}), 249 provider.NewZoneIDFilter([]string{""}), false, client) 250 251 for _, ti := range tests { 252 actual, err := provider.Records(context.Background()) 253 if err != nil { 254 t.Fatal(err) 255 } 256 validateEndpoints(t, actual, ti.Endpoints) 257 } 258 } 259 260 func TestBluecatApplyChangesCreate(t *testing.T) { 261 client := mockGatewayClient{ 262 mockBluecatZones: &[]api.BluecatZone{ 263 createMockBluecatZone("example.com"), 264 }, 265 mockBluecatHosts: &[]api.BluecatHostRecord{}, 266 mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{}, 267 mockBluecatTXTs: &[]api.BluecatTXTRecord{}, 268 } 269 270 provider := newBluecatProvider( 271 endpoint.NewDomainFilter([]string{"example.com"}), 272 provider.NewZoneIDFilter([]string{""}), false, client) 273 274 for _, ti := range tests { 275 err := provider.ApplyChanges(context.Background(), &plan.Changes{Create: ti.Endpoints}) 276 if err != nil { 277 t.Fatal(err) 278 } 279 280 actual, err := provider.Records(context.Background()) 281 if err != nil { 282 t.Fatal(err) 283 } 284 validateEndpoints(t, actual, []*endpoint.Endpoint{}) 285 } 286 } 287 288 func TestBluecatApplyChangesDelete(t *testing.T) { 289 client := mockGatewayClient{ 290 mockBluecatZones: &[]api.BluecatZone{ 291 createMockBluecatZone("example.com"), 292 }, 293 mockBluecatHosts: &[]api.BluecatHostRecord{ 294 createMockBluecatHostRecord("example.com", "123.123.123.122", 30), 295 createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30), 296 createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30), 297 }, 298 mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{ 299 createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30), 300 }, 301 mockBluecatTXTs: &[]api.BluecatTXTRecord{ 302 createMockBluecatTXT("kdb.example.com", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"), 303 createMockBluecatTXT("wack.example.com", "hello"), 304 createMockBluecatTXT("sack.example.com", ""), 305 }, 306 } 307 308 provider := newBluecatProvider( 309 endpoint.NewDomainFilter([]string{"example.com"}), 310 provider.NewZoneIDFilter([]string{""}), false, client) 311 312 for _, ti := range tests { 313 err := provider.ApplyChanges(context.Background(), &plan.Changes{Delete: ti.Endpoints}) 314 if err != nil { 315 t.Fatal(err) 316 } 317 318 actual, err := provider.Records(context.Background()) 319 if err != nil { 320 t.Fatal(err) 321 } 322 validateEndpoints(t, actual, []*endpoint.Endpoint{}) 323 } 324 } 325 326 func TestBluecatApplyChangesDeleteWithOwner(t *testing.T) { 327 client := mockGatewayClient{ 328 mockBluecatZones: &[]api.BluecatZone{ 329 createMockBluecatZone("example.com"), 330 }, 331 mockBluecatHosts: &[]api.BluecatHostRecord{ 332 createMockBluecatHostRecord("example.com", "123.123.123.122", 30), 333 createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30), 334 createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30), 335 }, 336 mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{ 337 createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30), 338 }, 339 mockBluecatTXTs: &[]api.BluecatTXTRecord{ 340 createMockBluecatTXT("kdb.example.com", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"), 341 createMockBluecatTXT("wack.example.com", "hello"), 342 createMockBluecatTXT("sack.example.com", ""), 343 }, 344 } 345 346 provider := newBluecatProvider( 347 endpoint.NewDomainFilter([]string{"example.com"}), 348 provider.NewZoneIDFilter([]string{""}), false, client) 349 350 for _, ti := range tests { 351 for _, ep := range ti.Endpoints { 352 if strings.Contains(ep.Targets.String(), "external-dns") { 353 owner, err := extractOwnerfromTXTRecord(ep.Targets.String()) 354 if err != nil { 355 t.Logf("%v", err) 356 } else { 357 t.Logf("Owner %s", owner) 358 } 359 } 360 } 361 err := provider.ApplyChanges(context.Background(), &plan.Changes{Delete: ti.Endpoints}) 362 if err != nil { 363 t.Fatal(err) 364 } 365 actual, err := provider.Records(context.Background()) 366 if err != nil { 367 t.Fatal(err) 368 } 369 validateEndpoints(t, actual, []*endpoint.Endpoint{}) 370 } 371 } 372 373 // TODO: ensure findZone method is tested 374 // TODO: ensure zones method is tested 375 // TODO: ensure createRecords method is tested 376 // TODO: ensure deleteRecords method is tested 377 // TODO: ensure recordSet method is tested 378 379 // TODO: Figure out why recordSet.res is not being set properly 380 func TestBluecatRecordset(t *testing.T) { 381 client := mockGatewayClient{ 382 mockBluecatZones: &[]api.BluecatZone{ 383 createMockBluecatZone("example.com"), 384 }, 385 mockBluecatHosts: &[]api.BluecatHostRecord{ 386 createMockBluecatHostRecord("example.com", "123.123.123.122", 30), 387 createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30), 388 createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30), 389 }, 390 mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{ 391 createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30), 392 }, 393 mockBluecatTXTs: &[]api.BluecatTXTRecord{ 394 createMockBluecatTXT("abc.example.com", "hello"), 395 }, 396 } 397 398 provider := newBluecatProvider( 399 endpoint.NewDomainFilter([]string{"example.com"}), 400 provider.NewZoneIDFilter([]string{""}), false, client) 401 402 // Test txt records for recordSet function 403 testTxtEndpoint := endpoint.NewEndpoint("abc.example.com", endpoint.RecordTypeTXT, "hello") 404 txtObj := api.BluecatCreateTXTRecordRequest{ 405 AbsoluteName: testTxtEndpoint.DNSName, 406 Text: testTxtEndpoint.Targets[0], 407 } 408 txtRecords := []api.BluecatTXTRecord{ 409 createMockBluecatTXT("abc.example.com", "hello"), 410 } 411 expected := bluecatRecordSet{ 412 obj: &txtObj, 413 res: &txtRecords, 414 } 415 actual, err := provider.recordSet(testTxtEndpoint, true) 416 if err != nil { 417 t.Fatal(err) 418 } 419 assert.Equal(t, actual.obj, expected.obj) 420 assert.Equal(t, actual.res, expected.res) 421 422 // Test a records for recordSet function 423 testHostEndpoint := endpoint.NewEndpoint("whitespace.example.com", endpoint.RecordTypeA, "123.123.123.124") 424 hostObj := api.BluecatCreateHostRecordRequest{ 425 AbsoluteName: testHostEndpoint.DNSName, 426 IP4Address: testHostEndpoint.Targets[0], 427 } 428 hostRecords := []api.BluecatHostRecord{ 429 createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30), 430 } 431 hostExpected := bluecatRecordSet{ 432 obj: &hostObj, 433 res: &hostRecords, 434 } 435 hostActual, err := provider.recordSet(testHostEndpoint, true) 436 if err != nil { 437 t.Fatal(err) 438 } 439 assert.Equal(t, hostActual.obj, hostExpected.obj) 440 assert.Equal(t, hostActual.res, hostExpected.res) 441 442 // Test CName records for recordSet function 443 testCnameEndpoint := endpoint.NewEndpoint("hack.example.com", endpoint.RecordTypeCNAME, "bluecatnetworks.com") 444 cnameObj := api.BluecatCreateCNAMERecordRequest{ 445 AbsoluteName: testCnameEndpoint.DNSName, 446 LinkedRecord: testCnameEndpoint.Targets[0], 447 } 448 cnameRecords := []api.BluecatCNAMERecord{ 449 createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30), 450 } 451 cnameExpected := bluecatRecordSet{ 452 obj: &cnameObj, 453 res: &cnameRecords, 454 } 455 cnameActual, err := provider.recordSet(testCnameEndpoint, true) 456 if err != nil { 457 t.Fatal(err) 458 } 459 assert.Equal(t, cnameActual.obj, cnameExpected.obj) 460 assert.Equal(t, cnameActual.res, cnameExpected.res) 461 } 462 463 func validateEndpoints(t *testing.T, actual, expected []*endpoint.Endpoint) { 464 assert.True(t, testutils.SameEndpoints(actual, expected), "actual and expected endpoints don't match. %s:%s", actual, expected) 465 }