external-dns/provider/bluecat/bluecat_test.go
Andy Bursavich 74ffff6c26 gofumpt
2022-09-20 20:48:57 -07:00

466 lines
14 KiB
Go

/*
Copyright 2020 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 bluecat
import (
"context"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/internal/testutils"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
api "sigs.k8s.io/external-dns/provider/bluecat/gateway"
)
type mockGatewayClient struct {
mockBluecatZones *[]api.BluecatZone
mockBluecatHosts *[]api.BluecatHostRecord
mockBluecatCNAMEs *[]api.BluecatCNAMERecord
mockBluecatTXTs *[]api.BluecatTXTRecord
}
type Changes struct {
// Records that need to be created
Create []*endpoint.Endpoint
// Records that need to be updated (current data)
UpdateOld []*endpoint.Endpoint
// Records that need to be updated (desired data)
UpdateNew []*endpoint.Endpoint
// Records that need to be deleted
Delete []*endpoint.Endpoint
}
func (g mockGatewayClient) GetBluecatZones(zoneName string) ([]api.BluecatZone, error) {
return *g.mockBluecatZones, nil
}
func (g mockGatewayClient) GetHostRecords(zone string, records *[]api.BluecatHostRecord) error {
*records = *g.mockBluecatHosts
return nil
}
func (g mockGatewayClient) GetCNAMERecords(zone string, records *[]api.BluecatCNAMERecord) error {
*records = *g.mockBluecatCNAMEs
return nil
}
func (g mockGatewayClient) GetHostRecord(name string, record *api.BluecatHostRecord) error {
for _, currentRecord := range *g.mockBluecatHosts {
if currentRecord.Name == strings.Split(name, ".")[0] {
*record = currentRecord
return nil
}
}
return nil
}
func (g mockGatewayClient) GetCNAMERecord(name string, record *api.BluecatCNAMERecord) error {
for _, currentRecord := range *g.mockBluecatCNAMEs {
if currentRecord.Name == strings.Split(name, ".")[0] {
*record = currentRecord
return nil
}
}
return nil
}
func (g mockGatewayClient) CreateHostRecord(zone string, req *api.BluecatCreateHostRecordRequest) (err error) {
return nil
}
func (g mockGatewayClient) CreateCNAMERecord(zone string, req *api.BluecatCreateCNAMERecordRequest) (err error) {
return nil
}
func (g mockGatewayClient) DeleteHostRecord(name string, zone string) (err error) {
*g.mockBluecatHosts = nil
return nil
}
func (g mockGatewayClient) DeleteCNAMERecord(name string, zone string) (err error) {
*g.mockBluecatCNAMEs = nil
return nil
}
func (g mockGatewayClient) GetTXTRecords(zone string, records *[]api.BluecatTXTRecord) error {
*records = *g.mockBluecatTXTs
return nil
}
func (g mockGatewayClient) GetTXTRecord(name string, record *api.BluecatTXTRecord) error {
for _, currentRecord := range *g.mockBluecatTXTs {
if currentRecord.Name == name {
*record = currentRecord
return nil
}
}
return nil
}
func (g mockGatewayClient) CreateTXTRecord(zone string, req *api.BluecatCreateTXTRecordRequest) error {
return nil
}
func (g mockGatewayClient) DeleteTXTRecord(name string, zone string) error {
*g.mockBluecatTXTs = nil
return nil
}
func (g mockGatewayClient) ServerFullDeploy() error {
return nil
}
func createMockBluecatZone(fqdn string) api.BluecatZone {
props := "absoluteName=" + fqdn
return api.BluecatZone{
Properties: props,
Name: fqdn,
ID: 3,
}
}
func createMockBluecatHostRecord(fqdn, target string, ttl int) api.BluecatHostRecord {
props := "absoluteName=" + fqdn + "|addresses=" + target + "|ttl=" + fmt.Sprint(ttl) + "|"
nameParts := strings.Split(fqdn, ".")
return api.BluecatHostRecord{
Name: nameParts[0],
Properties: props,
ID: 3,
}
}
func createMockBluecatCNAME(alias, target string, ttl int) api.BluecatCNAMERecord {
props := "absoluteName=" + alias + "|linkedRecordName=" + target + "|ttl=" + fmt.Sprint(ttl) + "|"
nameParts := strings.Split(alias, ".")
return api.BluecatCNAMERecord{
Name: nameParts[0],
Properties: props,
}
}
func createMockBluecatTXT(fqdn, txt string) api.BluecatTXTRecord {
return api.BluecatTXTRecord{
Name: fqdn,
Properties: txt,
}
}
func newBluecatProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, client mockGatewayClient) *BluecatProvider {
return &BluecatProvider{
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun,
gatewayClient: client,
}
}
type bluecatTestData []struct {
TestDescription string
Endpoints []*endpoint.Endpoint
}
var tests = bluecatTestData{
{
"first test case", // TODO: better test description
[]*endpoint.Endpoint{
{
DNSName: "example.com",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"123.123.123.122"},
RecordTTL: endpoint.TTL(30),
},
{
DNSName: "nginx.example.com",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"123.123.123.123"},
RecordTTL: endpoint.TTL(30),
},
{
DNSName: "whitespace.example.com",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"123.123.123.124"},
RecordTTL: endpoint.TTL(30),
},
{
DNSName: "hack.example.com",
RecordType: endpoint.RecordTypeCNAME,
Targets: endpoint.Targets{"bluecatnetworks.com"},
RecordTTL: endpoint.TTL(30),
},
{
DNSName: "wack.example.com",
RecordType: endpoint.RecordTypeTXT,
Targets: endpoint.Targets{"hello"},
Labels: endpoint.Labels{"owner": ""},
},
{
DNSName: "sack.example.com",
RecordType: endpoint.RecordTypeTXT,
Targets: endpoint.Targets{""},
Labels: endpoint.Labels{"owner": ""},
},
{
DNSName: "kdb.example.com",
RecordType: endpoint.RecordTypeTXT,
Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"},
Labels: endpoint.Labels{"owner": "default"},
},
},
},
}
func TestBluecatRecords(t *testing.T) {
client := mockGatewayClient{
mockBluecatZones: &[]api.BluecatZone{
createMockBluecatZone("example.com"),
},
mockBluecatTXTs: &[]api.BluecatTXTRecord{
createMockBluecatTXT("kdb.example.com", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"),
createMockBluecatTXT("wack.example.com", "hello"),
createMockBluecatTXT("sack.example.com", ""),
},
mockBluecatHosts: &[]api.BluecatHostRecord{
createMockBluecatHostRecord("example.com", "123.123.123.122", 30),
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30),
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30),
},
mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30),
},
}
provider := newBluecatProvider(
endpoint.NewDomainFilter([]string{"example.com"}),
provider.NewZoneIDFilter([]string{""}), false, client)
for _, ti := range tests {
actual, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
validateEndpoints(t, actual, ti.Endpoints)
}
}
func TestBluecatApplyChangesCreate(t *testing.T) {
client := mockGatewayClient{
mockBluecatZones: &[]api.BluecatZone{
createMockBluecatZone("example.com"),
},
mockBluecatHosts: &[]api.BluecatHostRecord{},
mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{},
mockBluecatTXTs: &[]api.BluecatTXTRecord{},
}
provider := newBluecatProvider(
endpoint.NewDomainFilter([]string{"example.com"}),
provider.NewZoneIDFilter([]string{""}), false, client)
for _, ti := range tests {
err := provider.ApplyChanges(context.Background(), &plan.Changes{Create: ti.Endpoints})
if err != nil {
t.Fatal(err)
}
actual, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
validateEndpoints(t, actual, []*endpoint.Endpoint{})
}
}
func TestBluecatApplyChangesDelete(t *testing.T) {
client := mockGatewayClient{
mockBluecatZones: &[]api.BluecatZone{
createMockBluecatZone("example.com"),
},
mockBluecatHosts: &[]api.BluecatHostRecord{
createMockBluecatHostRecord("example.com", "123.123.123.122", 30),
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30),
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30),
},
mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30),
},
mockBluecatTXTs: &[]api.BluecatTXTRecord{
createMockBluecatTXT("kdb.example.com", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"),
createMockBluecatTXT("wack.example.com", "hello"),
createMockBluecatTXT("sack.example.com", ""),
},
}
provider := newBluecatProvider(
endpoint.NewDomainFilter([]string{"example.com"}),
provider.NewZoneIDFilter([]string{""}), false, client)
for _, ti := range tests {
err := provider.ApplyChanges(context.Background(), &plan.Changes{Delete: ti.Endpoints})
if err != nil {
t.Fatal(err)
}
actual, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
validateEndpoints(t, actual, []*endpoint.Endpoint{})
}
}
func TestBluecatApplyChangesDeleteWithOwner(t *testing.T) {
client := mockGatewayClient{
mockBluecatZones: &[]api.BluecatZone{
createMockBluecatZone("example.com"),
},
mockBluecatHosts: &[]api.BluecatHostRecord{
createMockBluecatHostRecord("example.com", "123.123.123.122", 30),
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30),
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30),
},
mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30),
},
mockBluecatTXTs: &[]api.BluecatTXTRecord{
createMockBluecatTXT("kdb.example.com", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"),
createMockBluecatTXT("wack.example.com", "hello"),
createMockBluecatTXT("sack.example.com", ""),
},
}
provider := newBluecatProvider(
endpoint.NewDomainFilter([]string{"example.com"}),
provider.NewZoneIDFilter([]string{""}), false, client)
for _, ti := range tests {
for _, ep := range ti.Endpoints {
if strings.Contains(ep.Targets.String(), "external-dns") {
owner, err := extractOwnerfromTXTRecord(ep.Targets.String())
if err != nil {
t.Logf("%v", err)
} else {
t.Logf("Owner %s", owner)
}
}
}
err := provider.ApplyChanges(context.Background(), &plan.Changes{Delete: ti.Endpoints})
if err != nil {
t.Fatal(err)
}
actual, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
validateEndpoints(t, actual, []*endpoint.Endpoint{})
}
}
// TODO: ensure findZone method is tested
// TODO: ensure zones method is tested
// TODO: ensure createRecords method is tested
// TODO: ensure deleteRecords method is tested
// TODO: ensure recordSet method is tested
// TODO: Figure out why recordSet.res is not being set properly
func TestBluecatRecordset(t *testing.T) {
client := mockGatewayClient{
mockBluecatZones: &[]api.BluecatZone{
createMockBluecatZone("example.com"),
},
mockBluecatHosts: &[]api.BluecatHostRecord{
createMockBluecatHostRecord("example.com", "123.123.123.122", 30),
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123", 30),
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30),
},
mockBluecatCNAMEs: &[]api.BluecatCNAMERecord{
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30),
},
mockBluecatTXTs: &[]api.BluecatTXTRecord{
createMockBluecatTXT("abc.example.com", "hello"),
},
}
provider := newBluecatProvider(
endpoint.NewDomainFilter([]string{"example.com"}),
provider.NewZoneIDFilter([]string{""}), false, client)
// Test txt records for recordSet function
testTxtEndpoint := endpoint.NewEndpoint("abc.example.com", endpoint.RecordTypeTXT, "hello")
txtObj := api.BluecatCreateTXTRecordRequest{
AbsoluteName: testTxtEndpoint.DNSName,
Text: testTxtEndpoint.Targets[0],
}
txtRecords := []api.BluecatTXTRecord{
createMockBluecatTXT("abc.example.com", "hello"),
}
expected := bluecatRecordSet{
obj: &txtObj,
res: &txtRecords,
}
actual, err := provider.recordSet(testTxtEndpoint, true)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, actual.obj, expected.obj)
assert.Equal(t, actual.res, expected.res)
// Test a records for recordSet function
testHostEndpoint := endpoint.NewEndpoint("whitespace.example.com", endpoint.RecordTypeA, "123.123.123.124")
hostObj := api.BluecatCreateHostRecordRequest{
AbsoluteName: testHostEndpoint.DNSName,
IP4Address: testHostEndpoint.Targets[0],
}
hostRecords := []api.BluecatHostRecord{
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124", 30),
}
hostExpected := bluecatRecordSet{
obj: &hostObj,
res: &hostRecords,
}
hostActual, err := provider.recordSet(testHostEndpoint, true)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, hostActual.obj, hostExpected.obj)
assert.Equal(t, hostActual.res, hostExpected.res)
// Test CName records for recordSet function
testCnameEndpoint := endpoint.NewEndpoint("hack.example.com", endpoint.RecordTypeCNAME, "bluecatnetworks.com")
cnameObj := api.BluecatCreateCNAMERecordRequest{
AbsoluteName: testCnameEndpoint.DNSName,
LinkedRecord: testCnameEndpoint.Targets[0],
}
cnameRecords := []api.BluecatCNAMERecord{
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com", 30),
}
cnameExpected := bluecatRecordSet{
obj: &cnameObj,
res: &cnameRecords,
}
cnameActual, err := provider.recordSet(testCnameEndpoint, true)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, cnameActual.obj, cnameExpected.obj)
assert.Equal(t, cnameActual.res, cnameExpected.res)
}
func validateEndpoints(t *testing.T, actual, expected []*endpoint.Endpoint) {
assert.True(t, testutils.SameEndpoints(actual, expected), "actual and expected endpoints don't match. %s:%s", actual, expected)
}