sigs.k8s.io/external-dns@v0.14.1/provider/ns1/ns1_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 ns1
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/mock"
    28  	"github.com/stretchr/testify/require"
    29  	api "gopkg.in/ns1/ns1-go.v2/rest"
    30  	"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
    31  
    32  	"sigs.k8s.io/external-dns/endpoint"
    33  	"sigs.k8s.io/external-dns/plan"
    34  	"sigs.k8s.io/external-dns/provider"
    35  )
    36  
    37  type MockNS1DomainClient struct {
    38  	mock.Mock
    39  }
    40  
    41  func (m *MockNS1DomainClient) CreateRecord(r *dns.Record) (*http.Response, error) {
    42  	return nil, nil
    43  }
    44  
    45  func (m *MockNS1DomainClient) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
    46  	return nil, nil
    47  }
    48  
    49  func (m *MockNS1DomainClient) UpdateRecord(r *dns.Record) (*http.Response, error) {
    50  	return nil, nil
    51  }
    52  
    53  func (m *MockNS1DomainClient) GetZone(zone string) (*dns.Zone, *http.Response, error) {
    54  	r := &dns.ZoneRecord{
    55  		Domain:   "test.foo.com",
    56  		ShortAns: []string{"2.2.2.2"},
    57  		TTL:      3600,
    58  		Type:     "A",
    59  		ID:       "123456789abcdefghijklmno",
    60  	}
    61  	z := &dns.Zone{
    62  		Zone:    "foo.com",
    63  		Records: []*dns.ZoneRecord{r},
    64  		TTL:     3600,
    65  		ID:      "12345678910111213141516a",
    66  	}
    67  
    68  	if zone == "foo.com" {
    69  		return z, nil, nil
    70  	}
    71  	return nil, nil, nil
    72  }
    73  
    74  func (m *MockNS1DomainClient) ListZones() ([]*dns.Zone, *http.Response, error) {
    75  	zones := []*dns.Zone{
    76  		{Zone: "foo.com", ID: "12345678910111213141516a"},
    77  		{Zone: "bar.com", ID: "12345678910111213141516b"},
    78  	}
    79  	return zones, nil, nil
    80  }
    81  
    82  type MockNS1GetZoneFail struct{}
    83  
    84  func (m *MockNS1GetZoneFail) CreateRecord(r *dns.Record) (*http.Response, error) {
    85  	return nil, nil
    86  }
    87  
    88  func (m *MockNS1GetZoneFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
    89  	return nil, nil
    90  }
    91  
    92  func (m *MockNS1GetZoneFail) UpdateRecord(r *dns.Record) (*http.Response, error) {
    93  	return nil, nil
    94  }
    95  
    96  func (m *MockNS1GetZoneFail) GetZone(zone string) (*dns.Zone, *http.Response, error) {
    97  	return nil, nil, api.ErrZoneMissing
    98  }
    99  
   100  func (m *MockNS1GetZoneFail) ListZones() ([]*dns.Zone, *http.Response, error) {
   101  	zones := []*dns.Zone{
   102  		{Zone: "foo.com", ID: "12345678910111213141516a"},
   103  		{Zone: "bar.com", ID: "12345678910111213141516b"},
   104  	}
   105  	return zones, nil, nil
   106  }
   107  
   108  type MockNS1ListZonesFail struct{}
   109  
   110  func (m *MockNS1ListZonesFail) CreateRecord(r *dns.Record) (*http.Response, error) {
   111  	return nil, nil
   112  }
   113  
   114  func (m *MockNS1ListZonesFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
   115  	return nil, nil
   116  }
   117  
   118  func (m *MockNS1ListZonesFail) UpdateRecord(r *dns.Record) (*http.Response, error) {
   119  	return nil, nil
   120  }
   121  
   122  func (m *MockNS1ListZonesFail) GetZone(zone string) (*dns.Zone, *http.Response, error) {
   123  	return &dns.Zone{}, nil, nil
   124  }
   125  
   126  func (m *MockNS1ListZonesFail) ListZones() ([]*dns.Zone, *http.Response, error) {
   127  	return nil, nil, fmt.Errorf("no zones available")
   128  }
   129  
   130  func TestNS1Records(t *testing.T) {
   131  	provider := &NS1Provider{
   132  		client:        &MockNS1DomainClient{},
   133  		domainFilter:  endpoint.NewDomainFilter([]string{"foo.com."}),
   134  		zoneIDFilter:  provider.NewZoneIDFilter([]string{""}),
   135  		minTTLSeconds: 3600,
   136  	}
   137  	ctx := context.Background()
   138  
   139  	records, err := provider.Records(ctx)
   140  	require.NoError(t, err)
   141  	assert.Equal(t, 1, len(records))
   142  
   143  	provider.client = &MockNS1GetZoneFail{}
   144  	_, err = provider.Records(ctx)
   145  	require.Error(t, err)
   146  
   147  	provider.client = &MockNS1ListZonesFail{}
   148  	_, err = provider.Records(ctx)
   149  	require.Error(t, err)
   150  }
   151  
   152  func TestNewNS1Provider(t *testing.T) {
   153  	_ = os.Setenv("NS1_APIKEY", "xxxxxxxxxxxxxxxxx")
   154  	testNS1Config := NS1Config{
   155  		DomainFilter: endpoint.NewDomainFilter([]string{"foo.com."}),
   156  		ZoneIDFilter: provider.NewZoneIDFilter([]string{""}),
   157  		DryRun:       false,
   158  	}
   159  	_, err := NewNS1Provider(testNS1Config)
   160  	require.NoError(t, err)
   161  
   162  	_ = os.Unsetenv("NS1_APIKEY")
   163  	_, err = NewNS1Provider(testNS1Config)
   164  	require.Error(t, err)
   165  }
   166  
   167  func TestNS1Zones(t *testing.T) {
   168  	provider := &NS1Provider{
   169  		client:       &MockNS1DomainClient{},
   170  		domainFilter: endpoint.NewDomainFilter([]string{"foo.com."}),
   171  		zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
   172  	}
   173  
   174  	zones, err := provider.zonesFiltered()
   175  	require.NoError(t, err)
   176  
   177  	validateNS1Zones(t, zones, []*dns.Zone{
   178  		{Zone: "foo.com"},
   179  	})
   180  }
   181  
   182  func validateNS1Zones(t *testing.T, zones []*dns.Zone, expected []*dns.Zone) {
   183  	require.Len(t, zones, len(expected))
   184  
   185  	for i, zone := range zones {
   186  		assert.Equal(t, expected[i].Zone, zone.Zone)
   187  	}
   188  }
   189  
   190  func TestNS1BuildRecord(t *testing.T) {
   191  	change := &ns1Change{
   192  		Action: ns1Create,
   193  		Endpoint: &endpoint.Endpoint{
   194  			DNSName:    "new",
   195  			Targets:    endpoint.Targets{"target"},
   196  			RecordType: "A",
   197  		},
   198  	}
   199  
   200  	provider := &NS1Provider{
   201  		client:        &MockNS1DomainClient{},
   202  		domainFilter:  endpoint.NewDomainFilter([]string{"foo.com."}),
   203  		zoneIDFilter:  provider.NewZoneIDFilter([]string{""}),
   204  		minTTLSeconds: 300,
   205  	}
   206  
   207  	record := provider.ns1BuildRecord("foo.com", change)
   208  	assert.Equal(t, "foo.com", record.Zone)
   209  	assert.Equal(t, "new.foo.com", record.Domain)
   210  	assert.Equal(t, 300, record.TTL)
   211  
   212  	changeWithTTL := &ns1Change{
   213  		Action: ns1Create,
   214  		Endpoint: &endpoint.Endpoint{
   215  			DNSName:    "new-b",
   216  			Targets:    endpoint.Targets{"target"},
   217  			RecordType: "A",
   218  			RecordTTL:  3600,
   219  		},
   220  	}
   221  	record = provider.ns1BuildRecord("foo.com", changeWithTTL)
   222  	assert.Equal(t, "foo.com", record.Zone)
   223  	assert.Equal(t, "new-b.foo.com", record.Domain)
   224  	assert.Equal(t, 3600, record.TTL)
   225  }
   226  
   227  func TestNS1ApplyChanges(t *testing.T) {
   228  	changes := &plan.Changes{}
   229  	provider := &NS1Provider{
   230  		client: &MockNS1DomainClient{},
   231  	}
   232  	changes.Create = []*endpoint.Endpoint{
   233  		{DNSName: "new.foo.com", Targets: endpoint.Targets{"target"}},
   234  		{DNSName: "new.subdomain.bar.com", Targets: endpoint.Targets{"target"}},
   235  	}
   236  	changes.Delete = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target"}}}
   237  	changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target-new"}}}
   238  	err := provider.ApplyChanges(context.Background(), changes)
   239  	require.NoError(t, err)
   240  
   241  	// empty changes
   242  	changes.Create = []*endpoint.Endpoint{}
   243  	changes.Delete = []*endpoint.Endpoint{}
   244  	changes.UpdateNew = []*endpoint.Endpoint{}
   245  	err = provider.ApplyChanges(context.Background(), changes)
   246  	require.NoError(t, err)
   247  }
   248  
   249  func TestNewNS1Changes(t *testing.T) {
   250  	endpoints := []*endpoint.Endpoint{
   251  		{
   252  			DNSName:    "testa.foo.com",
   253  			Targets:    endpoint.Targets{"target-old"},
   254  			RecordType: "A",
   255  		},
   256  		{
   257  			DNSName:    "testba.bar.com",
   258  			Targets:    endpoint.Targets{"target-new"},
   259  			RecordType: "A",
   260  		},
   261  	}
   262  	expected := []*ns1Change{
   263  		{
   264  			Action:   "ns1Create",
   265  			Endpoint: endpoints[0],
   266  		},
   267  		{
   268  			Action:   "ns1Create",
   269  			Endpoint: endpoints[1],
   270  		},
   271  	}
   272  	changes := newNS1Changes("ns1Create", endpoints)
   273  	require.Len(t, changes, len(expected))
   274  	assert.Equal(t, expected, changes)
   275  }
   276  
   277  func TestNewNS1ChangesByZone(t *testing.T) {
   278  	provider := &NS1Provider{
   279  		client: &MockNS1DomainClient{},
   280  	}
   281  	zones, _ := provider.zonesFiltered()
   282  	changeSets := []*ns1Change{
   283  		{
   284  			Action: "ns1Create",
   285  			Endpoint: &endpoint.Endpoint{
   286  				DNSName:    "new.foo.com",
   287  				Targets:    endpoint.Targets{"target"},
   288  				RecordType: "A",
   289  			},
   290  		},
   291  		{
   292  			Action: "ns1Create",
   293  			Endpoint: &endpoint.Endpoint{
   294  				DNSName:    "unrelated.bar.com",
   295  				Targets:    endpoint.Targets{"target"},
   296  				RecordType: "A",
   297  			},
   298  		},
   299  		{
   300  			Action: "ns1Delete",
   301  			Endpoint: &endpoint.Endpoint{
   302  				DNSName:    "test.foo.com",
   303  				Targets:    endpoint.Targets{"target"},
   304  				RecordType: "A",
   305  			},
   306  		},
   307  		{
   308  			Action: "ns1Update",
   309  			Endpoint: &endpoint.Endpoint{
   310  				DNSName:    "test.foo.com",
   311  				Targets:    endpoint.Targets{"target-new"},
   312  				RecordType: "A",
   313  			},
   314  		},
   315  	}
   316  
   317  	changes := ns1ChangesByZone(zones, changeSets)
   318  	assert.Len(t, changes["bar.com"], 1)
   319  	assert.Len(t, changes["foo.com"], 3)
   320  }