sigs.k8s.io/external-dns@v0.14.1/provider/rcode0/rcode0_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 rcode0
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  	"testing"
    24  
    25  	rc0 "github.com/nic-at/rc0go"
    26  	"github.com/stretchr/testify/require"
    27  
    28  	"sigs.k8s.io/external-dns/endpoint"
    29  	"sigs.k8s.io/external-dns/plan"
    30  )
    31  
    32  const (
    33  	testZoneOne = "testzone1.at"
    34  	testZoneTwo = "testzone2.at"
    35  
    36  	rrsetChangesUnsupportedChangeType = 0
    37  )
    38  
    39  type mockRcodeZeroClient rc0.Client
    40  
    41  type mockZoneManagementService struct {
    42  	TestNilZonesReturned bool
    43  	TestErrorReturned    bool
    44  }
    45  
    46  type mockRRSetService struct {
    47  	TestErrorReturned bool
    48  }
    49  
    50  func (m *mockZoneManagementService) resetTestConditions() {
    51  	m.TestNilZonesReturned = false
    52  	m.TestErrorReturned = false
    53  }
    54  
    55  func TestRcodeZeroProvider_Records(t *testing.T) {
    56  	mockRRSetService := &mockRRSetService{}
    57  	mockZoneManagementService := &mockZoneManagementService{}
    58  
    59  	provider := &RcodeZeroProvider{
    60  		Client: (*rc0.Client)(&mockRcodeZeroClient{
    61  			Zones: mockZoneManagementService,
    62  			RRSet: mockRRSetService,
    63  		}),
    64  	}
    65  
    66  	ctx := context.Background()
    67  
    68  	endpoints, err := provider.Records(ctx) // should return 6 rrs
    69  	if err != nil {
    70  		t.Errorf("should not fail, %s", err)
    71  	}
    72  	require.Equal(t, 10, len(endpoints))
    73  
    74  	mockRRSetService.TestErrorReturned = true
    75  
    76  	_, err = provider.Records(ctx)
    77  	if err == nil {
    78  		t.Errorf("expected to fail, %s", err)
    79  	}
    80  }
    81  
    82  func TestRcodeZeroProvider_ApplyChanges(t *testing.T) {
    83  	mockRRSetService := &mockRRSetService{}
    84  	mockZoneManagementService := &mockZoneManagementService{}
    85  
    86  	provider := &RcodeZeroProvider{
    87  		Client: (*rc0.Client)(&mockRcodeZeroClient{
    88  			Zones: mockZoneManagementService,
    89  			RRSet: mockRRSetService,
    90  		}),
    91  		DomainFilter: endpoint.NewDomainFilter([]string{testZoneOne}),
    92  	}
    93  
    94  	changes := mockChanges()
    95  
    96  	err := provider.ApplyChanges(context.Background(), changes)
    97  	if err != nil {
    98  		t.Errorf("should not fail, %s", err)
    99  	}
   100  }
   101  
   102  func TestRcodeZeroProvider_NewRcodezeroChanges(t *testing.T) {
   103  	provider := &RcodeZeroProvider{}
   104  
   105  	changes := mockChanges()
   106  
   107  	createChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Create)
   108  	require.Equal(t, 4, len(createChanges))
   109  
   110  	deleteChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Delete)
   111  	require.Equal(t, 1, len(deleteChanges))
   112  
   113  	updateOldChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateOld)
   114  	require.Equal(t, 1, len(updateOldChanges))
   115  
   116  	updateNewChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateNew)
   117  	require.Equal(t, 1, len(updateNewChanges))
   118  }
   119  
   120  func TestRcodeZeroProvider_NewRcodezeroChange(t *testing.T) {
   121  	_endpoint := &endpoint.Endpoint{
   122  		RecordType: "A",
   123  		DNSName:    "app." + testZoneOne,
   124  		RecordTTL:  300,
   125  		Targets:    endpoint.Targets{"target"},
   126  	}
   127  
   128  	provider := &RcodeZeroProvider{}
   129  
   130  	rrsetChange := provider.NewRcodezeroChange(testZoneOne, _endpoint)
   131  
   132  	require.Equal(t, _endpoint.RecordType, rrsetChange.Type)
   133  	require.Equal(t, _endpoint.DNSName, rrsetChange.Name)
   134  	require.Equal(t, _endpoint.Targets[0], rrsetChange.Records[0].Content)
   135  	// require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL)
   136  }
   137  
   138  func Test_submitChanges(t *testing.T) {
   139  	mockRRSetService := &mockRRSetService{}
   140  	mockZoneManagementService := &mockZoneManagementService{}
   141  
   142  	provider := &RcodeZeroProvider{
   143  		Client: (*rc0.Client)(&mockRcodeZeroClient{
   144  			Zones: mockZoneManagementService,
   145  			RRSet: mockRRSetService,
   146  		}),
   147  		DomainFilter: endpoint.NewDomainFilter([]string{testZoneOne}),
   148  	}
   149  
   150  	changes := mockRRSetChanges(rrsetChangesUnsupportedChangeType)
   151  
   152  	err := provider.submitChanges(changes)
   153  
   154  	if err == nil {
   155  		t.Errorf("expected to fail, %s", err)
   156  	}
   157  }
   158  
   159  func mockRRSetChanges(condition int) []*rc0.RRSetChange {
   160  	switch condition {
   161  	case rrsetChangesUnsupportedChangeType:
   162  		return []*rc0.RRSetChange{
   163  			{
   164  				Name:       testZoneOne,
   165  				Type:       "A",
   166  				ChangeType: "UNSUPPORTED",
   167  				Records:    []*rc0.Record{{Content: "fail"}},
   168  			},
   169  		}
   170  	default:
   171  		return nil
   172  	}
   173  }
   174  
   175  func mockChanges() *plan.Changes {
   176  	changes := &plan.Changes{}
   177  
   178  	changes.Create = []*endpoint.Endpoint{
   179  		{DNSName: "new.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A"},
   180  		{DNSName: "new.ext-dns-test-with-ttl." + testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A", RecordTTL: 100},
   181  		{DNSName: "new.ext-dns-test.unexpected.com", Targets: endpoint.Targets{"target"}, RecordType: "AAAA"},
   182  		{DNSName: testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "CNAME"},
   183  	}
   184  	changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target"}}}
   185  	changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target-old"}}}
   186  	changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 100}}
   187  
   188  	return changes
   189  }
   190  
   191  func TestRcodeZeroProvider_Zones(t *testing.T) {
   192  	mockRRSetService := &mockRRSetService{}
   193  	mockZoneManagementService := &mockZoneManagementService{}
   194  
   195  	provider := &RcodeZeroProvider{
   196  		Client: (*rc0.Client)(&mockRcodeZeroClient{
   197  			Zones: mockZoneManagementService,
   198  			RRSet: mockRRSetService,
   199  		}),
   200  	}
   201  
   202  	mockZoneManagementService.TestNilZonesReturned = true
   203  
   204  	zones, err := provider.Zones()
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	require.Equal(t, 0, len(zones))
   209  	mockZoneManagementService.resetTestConditions()
   210  
   211  	mockZoneManagementService.TestErrorReturned = true
   212  
   213  	_, err = provider.Zones()
   214  	if err == nil {
   215  		t.Errorf("expected to fail, %s", err)
   216  	}
   217  }
   218  
   219  func TestNewRcodeZeroProvider(t *testing.T) {
   220  	_ = os.Setenv("RC0_API_KEY", "123")
   221  	p, err := NewRcodeZeroProvider(endpoint.NewDomainFilter([]string{"ext-dns-test." + testZoneOne + "."}), true, true)
   222  	if err != nil {
   223  		t.Errorf("should not fail, %s", err)
   224  	}
   225  
   226  	require.Equal(t, true, p.DryRun)
   227  	require.Equal(t, true, p.TXTEncrypt)
   228  	require.Equal(t, true, p.DomainFilter.IsConfigured())
   229  	require.Equal(t, false, p.DomainFilter.Match("ext-dns-test."+testZoneTwo+".")) // filter is set, so it should match only provided domains
   230  
   231  	p, err = NewRcodeZeroProvider(endpoint.DomainFilter{}, false, false)
   232  
   233  	if err != nil {
   234  		t.Errorf("should not fail, %s", err)
   235  	}
   236  
   237  	require.Equal(t, false, p.DryRun)
   238  	require.Equal(t, false, p.DomainFilter.IsConfigured())
   239  	require.Equal(t, true, p.DomainFilter.Match("ext-dns-test."+testZoneOne+".")) // filter is not set, so it should match any
   240  
   241  	_ = os.Unsetenv("RC0_API_KEY")
   242  	_, err = NewRcodeZeroProvider(endpoint.DomainFilter{}, false, false)
   243  
   244  	if err == nil {
   245  		t.Errorf("expected to fail")
   246  	}
   247  }
   248  
   249  /* mocking mockRRSetServiceInterface */
   250  
   251  func (m *mockRRSetService) List(zone string, options *rc0.ListOptions) ([]*rc0.RRType, *rc0.Page, error) {
   252  	if m.TestErrorReturned {
   253  		return nil, nil, fmt.Errorf("operation RRSet.List failed")
   254  	}
   255  
   256  	return mockRRSet(zone), nil, nil
   257  }
   258  
   259  func mockRRSet(zone string) []*rc0.RRType {
   260  	return []*rc0.RRType{
   261  		{
   262  			Name: "app." + zone + ".",
   263  			Type: "TXT",
   264  			TTL:  300,
   265  			Records: []*rc0.Record{
   266  				{
   267  					Content:  "\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/default/app\"",
   268  					Disabled: false,
   269  				},
   270  			},
   271  		},
   272  		{
   273  			Name: "app." + zone + ".",
   274  			Type: "A",
   275  			TTL:  300,
   276  			Records: []*rc0.Record{
   277  				{
   278  					Content:  "127.0.0.1",
   279  					Disabled: false,
   280  				},
   281  			},
   282  		},
   283  		{
   284  			Name: "www." + zone + ".",
   285  			Type: "A",
   286  			TTL:  300,
   287  			Records: []*rc0.Record{
   288  				{
   289  					Content:  "127.0.0.1",
   290  					Disabled: false,
   291  				},
   292  			},
   293  		},
   294  		{
   295  			Name: zone + ".",
   296  			Type: "SOA",
   297  			TTL:  3600,
   298  			Records: []*rc0.Record{
   299  				{
   300  					Content:  "sec1.rcode0.net. rcodezero-soa.ipcom.at. 2019011616 10800 3600 604800 3600",
   301  					Disabled: false,
   302  				},
   303  			},
   304  		},
   305  		{
   306  			Name: zone + ".",
   307  			Type: "NS",
   308  			TTL:  3600,
   309  			Records: []*rc0.Record{
   310  				{
   311  					Content:  "sec2.rcode0.net.",
   312  					Disabled: false,
   313  				},
   314  				{
   315  					Content:  "sec1.rcode0.net.",
   316  					Disabled: false,
   317  				},
   318  			},
   319  		},
   320  	}
   321  }
   322  
   323  func (m *mockRRSetService) Create(zone string, rrsetCreate []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
   324  	return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
   325  }
   326  
   327  func (m *mockRRSetService) Edit(zone string, rrsetEdit []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
   328  	return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
   329  }
   330  
   331  func (m *mockRRSetService) Delete(zone string, rrsetDelete []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
   332  	return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
   333  }
   334  
   335  func (m *mockRRSetService) SubmitChangeSet(zone string, changeSet []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
   336  	return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
   337  }
   338  
   339  func (m *mockRRSetService) EncryptTXT(key []byte, rrType *rc0.RRSetChange) {}
   340  
   341  func (m *mockRRSetService) DecryptTXT(key []byte, rrType *rc0.RRType) {}
   342  
   343  /* mocking ZoneManagementServiceInterface */
   344  
   345  func (m *mockZoneManagementService) List(options *rc0.ListOptions) ([]*rc0.Zone, *rc0.Page, error) {
   346  	if m.TestNilZonesReturned {
   347  		return nil, nil, nil
   348  	}
   349  
   350  	if m.TestErrorReturned {
   351  		return nil, nil, fmt.Errorf("operation Zone.List failed")
   352  	}
   353  
   354  	zones := []*rc0.Zone{
   355  		{
   356  			Domain: testZoneOne,
   357  			Type:   "SLAVE",
   358  			// "dnssec": "yes",                   @todo: add this
   359  			// "created": "2018-04-09T09:27:31Z", @todo: add this
   360  			LastCheck: "",
   361  			Serial:    20180411,
   362  			Masters: []string{
   363  				"193.0.2.2",
   364  				"2001:db8::2",
   365  			},
   366  		},
   367  		{
   368  			Domain: testZoneTwo,
   369  			Type:   "MASTER",
   370  			// "dnssec": "no",                    @todo: add this
   371  			// "created": "2019-01-15T13:20:10Z", @todo: add this
   372  			LastCheck: "",
   373  			Serial:    2019011616,
   374  			Masters: []string{
   375  				"",
   376  			},
   377  		},
   378  	}
   379  
   380  	return zones, nil, nil
   381  }
   382  
   383  func (m *mockZoneManagementService) Get(zone string) (*rc0.Zone, error) { return nil, nil }
   384  func (m *mockZoneManagementService) Create(zoneCreate *rc0.ZoneCreate) (*rc0.StatusResponse, error) {
   385  	return nil, nil
   386  }
   387  
   388  func (m *mockZoneManagementService) Edit(zone string, zoneEdit *rc0.ZoneEdit) (*rc0.StatusResponse, error) {
   389  	return nil, nil
   390  }
   391  func (m *mockZoneManagementService) Delete(zone string) (*rc0.StatusResponse, error) { return nil, nil }
   392  func (m *mockZoneManagementService) Transfer(zone string) (*rc0.StatusResponse, error) {
   393  	return nil, nil
   394  }