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

     1  /*
     2  Copyright 2017 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 pihole
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"sigs.k8s.io/external-dns/endpoint"
    25  	"sigs.k8s.io/external-dns/plan"
    26  )
    27  
    28  type testPiholeClient struct {
    29  	endpoints []*endpoint.Endpoint
    30  	requests  *requestTracker
    31  }
    32  
    33  func (t *testPiholeClient) listRecords(ctx context.Context, rtype string) ([]*endpoint.Endpoint, error) {
    34  	out := make([]*endpoint.Endpoint, 0)
    35  	for _, ep := range t.endpoints {
    36  		if ep.RecordType == rtype {
    37  			out = append(out, ep)
    38  		}
    39  	}
    40  	return out, nil
    41  }
    42  
    43  func (t *testPiholeClient) createRecord(ctx context.Context, ep *endpoint.Endpoint) error {
    44  	t.endpoints = append(t.endpoints, ep)
    45  	t.requests.createRequests = append(t.requests.createRequests, ep)
    46  	return nil
    47  }
    48  
    49  func (t *testPiholeClient) deleteRecord(ctx context.Context, ep *endpoint.Endpoint) error {
    50  	newEPs := make([]*endpoint.Endpoint, 0)
    51  	for _, existing := range t.endpoints {
    52  		if existing.DNSName != ep.DNSName && existing.Targets[0] != ep.Targets[0] {
    53  			newEPs = append(newEPs, existing)
    54  		}
    55  	}
    56  	t.endpoints = newEPs
    57  	t.requests.deleteRequests = append(t.requests.deleteRequests, ep)
    58  	return nil
    59  }
    60  
    61  type requestTracker struct {
    62  	createRequests []*endpoint.Endpoint
    63  	deleteRequests []*endpoint.Endpoint
    64  }
    65  
    66  func (r *requestTracker) clear() {
    67  	r.createRequests = nil
    68  	r.deleteRequests = nil
    69  }
    70  
    71  func TestNewPiholeProvider(t *testing.T) {
    72  	// Test invalid configuration
    73  	_, err := NewPiholeProvider(PiholeConfig{})
    74  	if err == nil {
    75  		t.Error("Expected error from invalid configuration")
    76  	}
    77  	// Test valid configuration
    78  	_, err = NewPiholeProvider(PiholeConfig{Server: "test.example.com"})
    79  	if err != nil {
    80  		t.Error("Expected no error from valid configuration, got:", err)
    81  	}
    82  }
    83  
    84  func TestProvider(t *testing.T) {
    85  	requests := requestTracker{}
    86  	p := &PiholeProvider{
    87  		api: &testPiholeClient{endpoints: make([]*endpoint.Endpoint, 0), requests: &requests},
    88  	}
    89  
    90  	records, err := p.Records(context.Background())
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  	if len(records) != 0 {
    95  		t.Fatal("Expected empty list of records, got:", records)
    96  	}
    97  
    98  	// Populate the provider with records
    99  	records = []*endpoint.Endpoint{
   100  		{
   101  			DNSName:    "test1.example.com",
   102  			Targets:    []string{"192.168.1.1"},
   103  			RecordType: endpoint.RecordTypeA,
   104  		},
   105  		{
   106  			DNSName:    "test2.example.com",
   107  			Targets:    []string{"192.168.1.2"},
   108  			RecordType: endpoint.RecordTypeA,
   109  		},
   110  		{
   111  			DNSName:    "test3.example.com",
   112  			Targets:    []string{"192.168.1.3"},
   113  			RecordType: endpoint.RecordTypeA,
   114  		},
   115  	}
   116  	if err := p.ApplyChanges(context.Background(), &plan.Changes{
   117  		Create: records,
   118  	}); err != nil {
   119  		t.Fatal(err)
   120  	}
   121  
   122  	// Test records are correct on retrieval
   123  
   124  	newRecords, err := p.Records(context.Background())
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	if len(newRecords) != 3 {
   129  		t.Fatal("Expected list of 3 records, got:", records)
   130  	}
   131  	if len(requests.createRequests) != 3 {
   132  		t.Fatal("Expected 3 create requests, got:", requests.createRequests)
   133  	}
   134  	if len(requests.deleteRequests) != 0 {
   135  		t.Fatal("Expected no delete requests, got:", requests.deleteRequests)
   136  	}
   137  
   138  	for idx, record := range records {
   139  		if newRecords[idx].DNSName != record.DNSName {
   140  			t.Error("DNS Name malformed on retrieval, got:", newRecords[idx].DNSName, "expected:", record.DNSName)
   141  		}
   142  		if newRecords[idx].Targets[0] != record.Targets[0] {
   143  			t.Error("Targets malformed on retrieval, got:", newRecords[idx].Targets, "expected:", record.Targets)
   144  		}
   145  
   146  		if !reflect.DeepEqual(requests.createRequests[idx], record) {
   147  			t.Error("Unexpected create request, got:", newRecords[idx].DNSName, "expected:", record.DNSName)
   148  		}
   149  	}
   150  
   151  	requests.clear()
   152  
   153  	// Test delete a record
   154  
   155  	records = []*endpoint.Endpoint{
   156  		{
   157  			DNSName:    "test1.example.com",
   158  			Targets:    []string{"192.168.1.1"},
   159  			RecordType: endpoint.RecordTypeA,
   160  		},
   161  		{
   162  			DNSName:    "test2.example.com",
   163  			Targets:    []string{"192.168.1.2"},
   164  			RecordType: endpoint.RecordTypeA,
   165  		},
   166  	}
   167  	recordToDelete := endpoint.Endpoint{
   168  		DNSName:    "test3.example.com",
   169  		Targets:    []string{"192.168.1.3"},
   170  		RecordType: endpoint.RecordTypeA,
   171  	}
   172  	if err := p.ApplyChanges(context.Background(), &plan.Changes{
   173  		Delete: []*endpoint.Endpoint{
   174  			&recordToDelete,
   175  		},
   176  	}); err != nil {
   177  		t.Fatal(err)
   178  	}
   179  
   180  	// Test records are updated
   181  	newRecords, err = p.Records(context.Background())
   182  	if err != nil {
   183  		t.Fatal(err)
   184  	}
   185  	if len(newRecords) != 2 {
   186  		t.Fatal("Expected list of 2 records, got:", records)
   187  	}
   188  	if len(requests.createRequests) != 0 {
   189  		t.Fatal("Expected no create requests, got:", requests.createRequests)
   190  	}
   191  	if len(requests.deleteRequests) != 1 {
   192  		t.Fatal("Expected 1 delete request, got:", requests.deleteRequests)
   193  	}
   194  
   195  	for idx, record := range records {
   196  		if newRecords[idx].DNSName != record.DNSName {
   197  			t.Error("DNS Name malformed on retrieval, got:", newRecords[idx].DNSName, "expected:", record.DNSName)
   198  		}
   199  		if newRecords[idx].Targets[0] != record.Targets[0] {
   200  			t.Error("Targets malformed on retrieval, got:", newRecords[idx].Targets, "expected:", record.Targets)
   201  		}
   202  	}
   203  
   204  	if !reflect.DeepEqual(requests.deleteRequests[0], &recordToDelete) {
   205  		t.Error("Unexpected delete request, got:", requests.deleteRequests[0], "expected:", recordToDelete)
   206  	}
   207  
   208  	requests.clear()
   209  
   210  	// Test update a record
   211  
   212  	records = []*endpoint.Endpoint{
   213  		{
   214  			DNSName:    "test1.example.com",
   215  			Targets:    []string{"192.168.1.1"},
   216  			RecordType: endpoint.RecordTypeA,
   217  		},
   218  		{
   219  			DNSName:    "test2.example.com",
   220  			Targets:    []string{"10.0.0.1"},
   221  			RecordType: endpoint.RecordTypeA,
   222  		},
   223  	}
   224  	if err := p.ApplyChanges(context.Background(), &plan.Changes{
   225  		UpdateOld: []*endpoint.Endpoint{
   226  			{
   227  				DNSName:    "test1.example.com",
   228  				Targets:    []string{"192.168.1.1"},
   229  				RecordType: endpoint.RecordTypeA,
   230  			},
   231  			{
   232  				DNSName:    "test2.example.com",
   233  				Targets:    []string{"192.168.1.2"},
   234  				RecordType: endpoint.RecordTypeA,
   235  			},
   236  		},
   237  		UpdateNew: []*endpoint.Endpoint{
   238  			{
   239  				DNSName:    "test1.example.com",
   240  				Targets:    []string{"192.168.1.1"},
   241  				RecordType: endpoint.RecordTypeA,
   242  			},
   243  			{
   244  				DNSName:    "test2.example.com",
   245  				Targets:    []string{"10.0.0.1"},
   246  				RecordType: endpoint.RecordTypeA,
   247  			},
   248  		},
   249  	}); err != nil {
   250  		t.Fatal(err)
   251  	}
   252  
   253  	// Test records are updated
   254  	newRecords, err = p.Records(context.Background())
   255  	if err != nil {
   256  		t.Fatal(err)
   257  	}
   258  	if len(newRecords) != 2 {
   259  		t.Fatal("Expected list of 2 records, got:", records)
   260  	}
   261  	if len(requests.createRequests) != 1 {
   262  		t.Fatal("Expected 1 create request, got:", requests.createRequests)
   263  	}
   264  	if len(requests.deleteRequests) != 1 {
   265  		t.Fatal("Expected 1 delete request, got:", requests.deleteRequests)
   266  	}
   267  
   268  	for idx, record := range records {
   269  		if newRecords[idx].DNSName != record.DNSName {
   270  			t.Error("DNS Name malformed on retrieval, got:", newRecords[idx].DNSName, "expected:", record.DNSName)
   271  		}
   272  		if newRecords[idx].Targets[0] != record.Targets[0] {
   273  			t.Error("Targets malformed on retrieval, got:", newRecords[idx].Targets, "expected:", record.Targets)
   274  		}
   275  	}
   276  
   277  	expectedCreate := endpoint.Endpoint{
   278  		DNSName:    "test2.example.com",
   279  		Targets:    []string{"10.0.0.1"},
   280  		RecordType: endpoint.RecordTypeA,
   281  	}
   282  	expectedDelete := endpoint.Endpoint{
   283  		DNSName:    "test2.example.com",
   284  		Targets:    []string{"192.168.1.2"},
   285  		RecordType: endpoint.RecordTypeA,
   286  	}
   287  
   288  	if !reflect.DeepEqual(requests.createRequests[0], &expectedCreate) {
   289  		t.Error("Unexpected create request, got:", requests.createRequests[0], "expected:", &expectedCreate)
   290  	}
   291  	if !reflect.DeepEqual(requests.deleteRequests[0], &expectedDelete) {
   292  		t.Error("Unexpected delete request, got:", requests.deleteRequests[0], "expected:", &expectedDelete)
   293  	}
   294  
   295  	requests.clear()
   296  }