sigs.k8s.io/external-dns@v0.14.1/provider/transip/transip_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 transip
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  	"github.com/transip/gotransip/v6/domain"
    29  	"github.com/transip/gotransip/v6/rest"
    30  
    31  	"sigs.k8s.io/external-dns/endpoint"
    32  	"sigs.k8s.io/external-dns/provider"
    33  )
    34  
    35  func newProvider() *TransIPProvider {
    36  	return &TransIPProvider{
    37  		zoneMap: provider.ZoneIDName{},
    38  	}
    39  }
    40  
    41  func TestTransIPDnsEntriesAreEqual(t *testing.T) {
    42  	// test with equal set
    43  	a := []domain.DNSEntry{
    44  		{
    45  			Name:    "www.example.org",
    46  			Type:    "CNAME",
    47  			Expire:  3600,
    48  			Content: "www.example.com",
    49  		},
    50  		{
    51  			Name:    "www.example.com",
    52  			Type:    "A",
    53  			Expire:  3600,
    54  			Content: "192.168.0.1",
    55  		},
    56  	}
    57  
    58  	b := []domain.DNSEntry{
    59  		{
    60  			Name:    "www.example.com",
    61  			Type:    "A",
    62  			Expire:  3600,
    63  			Content: "192.168.0.1",
    64  		},
    65  		{
    66  			Name:    "www.example.org",
    67  			Type:    "CNAME",
    68  			Expire:  3600,
    69  			Content: "www.example.com",
    70  		},
    71  	}
    72  
    73  	assert.Equal(t, true, dnsEntriesAreEqual(a, b))
    74  
    75  	// change type on one of b's records
    76  	b[1].Type = "NS"
    77  	assert.Equal(t, false, dnsEntriesAreEqual(a, b))
    78  	b[1].Type = "CNAME"
    79  
    80  	// change ttl on one of b's records
    81  	b[1].Expire = 1800
    82  	assert.Equal(t, false, dnsEntriesAreEqual(a, b))
    83  	b[1].Expire = 3600
    84  
    85  	// change name on one of b's records
    86  	b[1].Name = "example.org"
    87  	assert.Equal(t, false, dnsEntriesAreEqual(a, b))
    88  
    89  	// remove last entry of b
    90  	b = b[:1]
    91  	assert.Equal(t, false, dnsEntriesAreEqual(a, b))
    92  }
    93  
    94  func TestTransIPGetMinimalValidTTL(t *testing.T) {
    95  	// test with 'unconfigured' TTL
    96  	ep := &endpoint.Endpoint{}
    97  	assert.EqualValues(t, transipMinimalValidTTL, getMinimalValidTTL(ep))
    98  
    99  	// test with lower than minimal ttl
   100  	ep.RecordTTL = (transipMinimalValidTTL - 1)
   101  	assert.EqualValues(t, transipMinimalValidTTL, getMinimalValidTTL(ep))
   102  
   103  	// test with higher than minimal ttl
   104  	ep.RecordTTL = (transipMinimalValidTTL + 1)
   105  	assert.EqualValues(t, transipMinimalValidTTL+1, getMinimalValidTTL(ep))
   106  }
   107  
   108  func TestTransIPRecordNameForEndpoint(t *testing.T) {
   109  	ep := &endpoint.Endpoint{
   110  		DNSName: "example.org",
   111  	}
   112  	d := domain.Domain{
   113  		Name: "example.org",
   114  	}
   115  
   116  	assert.Equal(t, "@", recordNameForEndpoint(ep, d.Name))
   117  
   118  	ep.DNSName = "www.example.org"
   119  	assert.Equal(t, "www", recordNameForEndpoint(ep, d.Name))
   120  }
   121  
   122  func TestTransIPEndpointNameForRecord(t *testing.T) {
   123  	r := domain.DNSEntry{
   124  		Name: "@",
   125  	}
   126  	d := domain.Domain{
   127  		Name: "example.org",
   128  	}
   129  
   130  	assert.Equal(t, d.Name, endpointNameForRecord(r, d.Name))
   131  
   132  	r.Name = "www"
   133  	assert.Equal(t, "www.example.org", endpointNameForRecord(r, d.Name))
   134  }
   135  
   136  func TestTransIPAddEndpointToEntries(t *testing.T) {
   137  	// prepare endpoint
   138  	ep := &endpoint.Endpoint{
   139  		DNSName:    "www.example.org",
   140  		RecordType: "A",
   141  		RecordTTL:  1800,
   142  		Targets: []string{
   143  			"192.168.0.1",
   144  			"192.168.0.2",
   145  		},
   146  	}
   147  
   148  	// prepare zone with DNS entry set
   149  	zone := domain.Domain{
   150  		Name: "example.org",
   151  	}
   152  
   153  	// add endpoint to zone's entries
   154  	result := dnsEntriesForEndpoint(ep, zone.Name)
   155  
   156  	if assert.Equal(t, 2, len(result)) {
   157  		assert.Equal(t, "www", result[0].Name)
   158  		assert.Equal(t, "A", result[0].Type)
   159  		assert.Equal(t, "192.168.0.1", result[0].Content)
   160  		assert.EqualValues(t, 1800, result[0].Expire)
   161  		assert.Equal(t, "www", result[1].Name)
   162  		assert.Equal(t, "A", result[1].Type)
   163  		assert.Equal(t, "192.168.0.2", result[1].Content)
   164  		assert.EqualValues(t, 1800, result[1].Expire)
   165  	}
   166  
   167  	// try again with CNAME
   168  	ep.RecordType = "CNAME"
   169  	ep.Targets = []string{"foo.bar"}
   170  	result = dnsEntriesForEndpoint(ep, zone.Name)
   171  	if assert.Equal(t, 1, len(result)) {
   172  		assert.Equal(t, "CNAME", result[0].Type)
   173  		assert.Equal(t, "foo.bar.", result[0].Content)
   174  	}
   175  }
   176  
   177  func TestZoneNameForDNSName(t *testing.T) {
   178  	p := newProvider()
   179  	p.zoneMap.Add("example.com", "example.com")
   180  
   181  	zoneName, err := p.zoneNameForDNSName("www.example.com")
   182  	if assert.NoError(t, err) {
   183  		assert.Equal(t, "example.com", zoneName)
   184  	}
   185  
   186  	_, err = p.zoneNameForDNSName("www.example.org")
   187  	if assert.Error(t, err) {
   188  		assert.Equal(t, "could not find zoneName for www.example.org", err.Error())
   189  	}
   190  }
   191  
   192  // fakeClient mocks the REST API client
   193  type fakeClient struct {
   194  	getFunc func(rest.Request, interface{}) error
   195  }
   196  
   197  func (f *fakeClient) Get(request rest.Request, dest interface{}) error {
   198  	if f.getFunc == nil {
   199  		return errors.New("GET not defined")
   200  	}
   201  
   202  	return f.getFunc(request, dest)
   203  }
   204  
   205  func (f fakeClient) Put(request rest.Request) error {
   206  	return errors.New("PUT not implemented")
   207  }
   208  
   209  func (f fakeClient) Post(request rest.Request) error {
   210  	return errors.New("POST not implemented")
   211  }
   212  
   213  func (f fakeClient) Delete(request rest.Request) error {
   214  	return errors.New("DELETE not implemented")
   215  }
   216  
   217  func (f fakeClient) Patch(request rest.Request) error {
   218  	return errors.New("PATCH not implemented")
   219  }
   220  
   221  func (f fakeClient) PatchWithResponse(request rest.Request) (rest.Response, error) {
   222  	return rest.Response{}, errors.New("PATCH with response not implemented")
   223  }
   224  
   225  func (f fakeClient) PostWithResponse(request rest.Request) (rest.Response, error) {
   226  	return rest.Response{}, errors.New("POST with response not implemented")
   227  }
   228  
   229  func (f fakeClient) PutWithResponse(request rest.Request) (rest.Response, error) {
   230  	return rest.Response{}, errors.New("PUT with response not implemented")
   231  }
   232  
   233  func TestProviderRecords(t *testing.T) {
   234  	// set up the fake REST client
   235  	client := &fakeClient{}
   236  	client.getFunc = func(req rest.Request, dest interface{}) error {
   237  		var data []byte
   238  		switch {
   239  		case req.Endpoint == "/domains":
   240  			// return list of some domain names
   241  			// names only, other fields are not used
   242  			data = []byte(`{"domains":[{"name":"example.org"}, {"name":"example.com"}]}`)
   243  		case strings.HasSuffix(req.Endpoint, "/dns"):
   244  			// return list of DNS entries
   245  			// also some unsupported types
   246  			data = []byte(`{"dnsEntries":[{"name":"www", "expire":1234, "type":"CNAME", "content":"@"},{"type":"MX"},{"type":"AAAA"}]}`)
   247  		}
   248  
   249  		// unmarshal the prepared return data into the given destination type
   250  		return json.Unmarshal(data, &dest)
   251  	}
   252  
   253  	// set up provider
   254  	p := newProvider()
   255  	p.domainRepo = domain.Repository{Client: client}
   256  
   257  	endpoints, err := p.Records(context.TODO())
   258  	if assert.NoError(t, err) {
   259  		if assert.Equal(t, 4, len(endpoints)) {
   260  			assert.Equal(t, "www.example.org", endpoints[0].DNSName)
   261  			assert.EqualValues(t, "@", endpoints[0].Targets[0])
   262  			assert.Equal(t, "CNAME", endpoints[0].RecordType)
   263  			assert.Equal(t, 0, len(endpoints[0].Labels))
   264  			assert.EqualValues(t, 1234, endpoints[0].RecordTTL)
   265  		}
   266  	}
   267  }
   268  
   269  func TestProviderEntriesForEndpoint(t *testing.T) {
   270  	// set up fake REST client
   271  	client := &fakeClient{}
   272  
   273  	// set up provider
   274  	p := newProvider()
   275  	p.domainRepo = domain.Repository{Client: client}
   276  	p.zoneMap.Add("example.com", "example.com")
   277  
   278  	// get entries for endpoint with unknown zone
   279  	_, _, err := p.entriesForEndpoint(&endpoint.Endpoint{
   280  		DNSName: "www.example.org",
   281  	})
   282  	if assert.Error(t, err) {
   283  		assert.Equal(t, "could not find zoneName for www.example.org", err.Error())
   284  	}
   285  
   286  	// get entries for endpoint with known zone but client returns error
   287  	// we leave GET functions undefined so we know which error to expect
   288  	zoneName, _, err := p.entriesForEndpoint(&endpoint.Endpoint{
   289  		DNSName: "www.example.com",
   290  	})
   291  	if assert.Error(t, err) {
   292  		assert.Equal(t, "GET not defined", err.Error())
   293  	}
   294  	assert.Equal(t, "example.com", zoneName)
   295  
   296  	// to be able to return a valid set of DNS entries through the API, we define
   297  	// some first, then JSON encode them and have the fake API client's Get function
   298  	// return that
   299  	// in this set are some entries that do and others that don't match the given
   300  	// endpoint
   301  	dnsEntries := []domain.DNSEntry{
   302  		{
   303  			Name:    "www",
   304  			Type:    "A",
   305  			Expire:  3600,
   306  			Content: "1.2.3.4",
   307  		},
   308  		{
   309  			Name:    "ftp",
   310  			Type:    "A",
   311  			Expire:  86400,
   312  			Content: "3.4.5.6",
   313  		},
   314  		{
   315  			Name:    "www",
   316  			Type:    "A",
   317  			Expire:  3600,
   318  			Content: "2.3.4.5",
   319  		},
   320  		{
   321  			Name:    "www",
   322  			Type:    "CNAME",
   323  			Expire:  3600,
   324  			Content: "@",
   325  		},
   326  	}
   327  	var v struct {
   328  		DNSEntries []domain.DNSEntry `json:"dnsEntries"`
   329  	}
   330  	v.DNSEntries = dnsEntries
   331  	returnData, err := json.Marshal(&v)
   332  	require.NoError(t, err)
   333  
   334  	// define GET function
   335  	client.getFunc = func(unused rest.Request, dest interface{}) error {
   336  		// unmarshal the prepared return data into the given dnsEntriesWrapper
   337  		return json.Unmarshal(returnData, &dest)
   338  	}
   339  	_, entries, err := p.entriesForEndpoint(&endpoint.Endpoint{
   340  		DNSName:    "www.example.com",
   341  		RecordType: "A",
   342  	})
   343  	if assert.NoError(t, err) {
   344  		if assert.Equal(t, 2, len(entries)) {
   345  			// only first and third entry should be returned
   346  			assert.Equal(t, dnsEntries[0], entries[0])
   347  			assert.Equal(t, dnsEntries[2], entries[1])
   348  		}
   349  	}
   350  }