Unverified Commit a0240289 authored by Nick Jüttner's avatar Nick Jüttner Committed by GitHub
Browse files

Revert "Add NS1 as a provider"

parent cd89eb82
......@@ -41,7 +41,6 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected
* [Oracle Cloud Infrastructure DNS](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm)
* [Linode DNS](https://www.linode.com/docs/networking/dns/)
* [RFC2136](https://tools.ietf.org/html/rfc2136)
* [NS1](https://ns1.com/)
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
......@@ -79,7 +78,6 @@ The following table clarifies the current status of the providers according to t
| Oracle Cloud Infrastructure DNS | Alpha |
| Linode DNS | Alpha |
| RFC2136 | Alpha |
| NS1 | Alpha |
## Running ExternalDNS:
......@@ -110,7 +108,6 @@ The following tutorials are provided:
* [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md)
* [Linode](docs/tutorials/linode.md)
* [RFC2136](docs/tutorials/rfc2136.md)
* [NS1](docs/tutorials/ns1.md)
### Running Locally
......@@ -233,7 +230,6 @@ Here's a rough outline on what is to come (subject to change):
- [x] Support for PowerDNS
- [x] Support for Linode
- [x] Support for RcodeZero
- [x] Support for NS1
### v0.6
......
# Setting up ExternalDNS for Services on NS1
This tutorial describes how to setup ExternalDNS for use within a
Kubernetes cluster using NS1 DNS.
Make sure to use **>=0.5** version of ExternalDNS for this tutorial.
## Creating a zone with NS1 DNS
If you are new to NS1, we recommend you first read the following
instructions for creating a zone.
[Creating a zone using the NS1
portal](https://ns1.com/knowledgebase/creating-a-zone)
[Creating a zone using the NS1
API](https://ns1.com/api#put-create-a-new-dns-zone)
## Creating NS1 Credentials
All NS1 products are API-first, meaning everything that can be done on
the portal---including managing zones and records, data sources and
feeds, and account settings and users---can be done via API.
The NS1 API is a standard REST API with JSON responses. The environment
var `NS1_APIKEY` will be needed to run ExternalDNS with NS1.
### To add or delete an API key
1. Log into the NS1 portal at [my.nsone.net](http://my.nsone.net).
2. Click your username in the upper-right corner, and navigate to **Account Settings** \> **Users & Teams**.
3. Navigate to the _API Keys_ tab, and click **Add Key**.
4. Enter the name of the application and modify permissions and settings as desired. Once complete, click **Create Key**. The new API key appears in the list.
Note: Set the permissions for your API keys just as you would for a user or team associated with your organization's NS1 account. For more information, refer to the article [Creating and Managing API Keys](https://ns1.com/knowledgebase/creating-and-managing-users) in the NS1 Knowledge Base.
## Deploy ExternalDNS
Connect your `kubectl` client to the cluster with which you want to test ExternalDNS, and then apply one of the following manifest files for deployment:
### Manifest (for clusters without RBAC enabled)
```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
- --provider=ns1
env:
- name: NS1_APIKEY
value: "YOUR_NS1_API_KEY"
```
### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
- --provider=ns1
env:
- name: NS1_APIKEY
value: "YOUR_NS1_API_KEY"
```
## Deploying an Nginx Service
Create a service file called 'nginx.yaml' with the following contents:
```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: example.com
external-dns.alpha.kubernetes.io/ttl: "120" #optional
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
```
**A note about annotations**
Verify that the annotation on the service uses the same hostname as the NS1 DNS zone created above. The annotation may also be a subdomain of the DNS zone (e.g. 'www.example.com').
The TTL annotation can be used to configure the TTL on DNS records managed by ExternalDNS and is optional. If this annotation is not set, the TTL on records managed by ExternalDNS will default to 10.
ExternalDNS uses the hostname annotation to determine which services should be registered with DNS. Removing the hostname annotation will cause ExternalDNS to remove the corresponding DNS records.
### Create the deployment and service
```
$ kubectl create -f nginx.yaml
```
Depending on where you run your service, it may take some time for your cloud provider to create an external IP for the service. Once an external IP is assigned, ExternalDNS detects the new service IP address and synchronizes the NS1 DNS records.
## Verifying NS1 DNS records
Use the NS1 portal or API to verify that the A record for your domain shows the external IP address of the services.
## Cleanup
Once you successfully configure and verify record management via ExternalDNS, you can delete the tutorial's example:
```
$ kubectl delete -f nginx.yaml
$ kubectl delete -f externaldns.yaml
```
......@@ -91,7 +91,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.42.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
gopkg.in/yaml.v2 v2.2.2
istio.io/api v0.0.0-20190321180614-db16d82d3672
istio.io/istio v0.0.0-20190322063008-2b1331886076
......
......@@ -201,14 +201,6 @@ func main() {
}
case "rfc2136":
p, err = provider.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, nil)
case "ns1":
p, err = provider.NewNS1Provider(
provider.NS1Config{
DomainFilter: domainFilter,
ZoneIDFilter: zoneIDFilter,
DryRun: cfg.DryRun,
},
)
default:
log.Fatalf("unknown dns provider: %s", cfg.Provider)
}
......
......@@ -253,7 +253,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
// Flags related to providers
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1")
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136")
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject)
......
/*
Copyright 2017 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 provider
import (
"fmt"
"net/http"
"os"
"strings"
log "github.com/sirupsen/logrus"
api "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
"github.com/kubernetes-incubator/external-dns/endpoint"
"github.com/kubernetes-incubator/external-dns/plan"
)
const (
// ns1Create is a ChangeAction enum value
ns1Create = "CREATE"
// ns1Delete is a ChangeAction enum value
ns1Delete = "DELETE"
// ns1Update is a ChangeAction enum value
ns1Update = "UPDATE"
// ns1DefaultTTL is the default ttl for ttls that are not set
ns1DefaultTTL = 10
)
// NS1DomainClient is a subset of the NS1 API the the provider uses, to ease testing
type NS1DomainClient interface {
CreateRecord(r *dns.Record) (*http.Response, error)
DeleteRecord(zone string, domain string, t string) (*http.Response, error)
UpdateRecord(r *dns.Record) (*http.Response, error)
GetZone(zone string) (*dns.Zone, *http.Response, error)
ListZones() ([]*dns.Zone, *http.Response, error)
}
// NS1DomainService wraps the API and fulfills the NS1DomainClient interface
type NS1DomainService struct {
service *api.Client
}
// CreateRecord wraps the Create method of the API's Record service
func (n NS1DomainService) CreateRecord(r *dns.Record) (*http.Response, error) {
return n.service.Records.Create(r)
}
// DeleteRecord wraps the Delete method of the API's Record service
func (n NS1DomainService) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
return n.service.Records.Delete(zone, domain, t)
}
// UpdateRecord wraps the Update method of the API's Record service
func (n NS1DomainService) UpdateRecord(r *dns.Record) (*http.Response, error) {
return n.service.Records.Update(r)
}
// GetZone wraps the Get method of the API's Zones service
func (n NS1DomainService) GetZone(zone string) (*dns.Zone, *http.Response, error) {
return n.service.Zones.Get(zone)
}
// ListZones wraps the List method of the API's Zones service
func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) {
return n.service.Zones.List()
}
// NS1Config passes cli args to the NS1Provider
type NS1Config struct {
DomainFilter DomainFilter
ZoneIDFilter ZoneIDFilter
DryRun bool
}
// NS1Provider is the NS1 provider
type NS1Provider struct {
client NS1DomainClient
domainFilter DomainFilter
zoneIDFilter ZoneIDFilter
dryRun bool
}
// NewNS1Provider creates a new NS1 Provider
func NewNS1Provider(config NS1Config) (*NS1Provider, error) {
return newNS1ProviderWithHTTPClient(config, http.DefaultClient)
}
func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Provider, error) {
token, ok := os.LookupEnv("NS1_APIKEY")
if !ok {
return nil, fmt.Errorf("NS1_APIKEY environment variable is not set")
}
apiClient := api.NewClient(client, api.SetAPIKey(token))
provider := &NS1Provider{
client: NS1DomainService{apiClient},
domainFilter: config.DomainFilter,
zoneIDFilter: config.ZoneIDFilter,
}
return provider, nil
}
// Records returns the endpoints this provider knows about
func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) {
zones, err := p.zonesFiltered()
if err != nil {
return nil, err
}
var endpoints []*endpoint.Endpoint
for _, zone := range zones {
// TODO handle Header Codes
zoneData, _, err := p.client.GetZone(zone.String())
if err != nil {
return nil, err
}
for _, record := range zoneData.Records {
if supportedRecordType(record.Type) {
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(
record.Domain,
record.Type,
endpoint.TTL(record.TTL),
record.ShortAns...,
),
)
}
}
}
return endpoints, nil
}
// ns1BuildRecord returns a dns.Record for a change set
func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record {
record := dns.NewRecord(zoneName, change.Endpoint.DNSName, change.Endpoint.RecordType)
for _, v := range change.Endpoint.Targets {
record.AddAnswer(dns.NewAnswer(strings.Split(v, " ")))
}
// set detault ttl
var ttl = ns1DefaultTTL
if change.Endpoint.RecordTTL.IsConfigured() {
ttl = int(change.Endpoint.RecordTTL)
}
record.TTL = ttl
return record
}
// ns1SubmitChanges takes an array of changes and sends them to NS1
func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error {
// return early if there is nothing to change
if len(changes) == 0 {
return nil
}
zones, err := p.zonesFiltered()
if err != nil {
return err
}
// separate into per-zone change sets to be passed to the API.
changesByZone := ns1ChangesByZone(zones, changes)
for zoneName, changes := range changesByZone {
for _, change := range changes {
record := ns1BuildRecord(zoneName, change)
logFields := log.Fields{
"record": record.Domain,
"type": record.Type,
"ttl": record.TTL,
"action": change.Action,
"zone": zoneName,
}
log.WithFields(logFields).Info("Changing record.")
if p.dryRun {
continue
}
switch change.Action {
case ns1Create:
_, err := p.client.CreateRecord(record)
if err != nil {
return err
}
case ns1Delete:
_, err := p.client.DeleteRecord(zoneName, record.Domain, record.Type)
if err != nil {
return err
}
case ns1Update:
_, err := p.client.UpdateRecord(record)
if err != nil {
return err
}
}
}
}
return nil
}
// Zones returns the list of hosted zones.
func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) {
// TODO handle Header Codes
zones, _, err := p.client.ListZones()
if err != nil {
return nil, err
}
toReturn := []*dns.Zone{}
for _, z := range zones {
if p.domainFilter.Match(z.Zone) && p.zoneIDFilter.Match(z.ID) {
toReturn = append(toReturn, z)
log.Debugf("Matched %s", z.Zone)
} else {
log.Debugf("Filtered %s", z.Zone)
}
}
return toReturn, nil
}
// ns1Change differentiates between ChangeActions
type ns1Change struct {
Action string
Endpoint *endpoint.Endpoint
}
// ApplyChanges applies a given set of changes in a given zone.
func (p *NS1Provider) ApplyChanges(changes *plan.Changes) error {
combinedChanges := make([]*ns1Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete))
combinedChanges = append(combinedChanges, newNS1Changes(ns1Create, changes.Create)...)
combinedChanges = append(combinedChanges, newNS1Changes(ns1Update, changes.UpdateNew)...)
combinedChanges = append(combinedChanges, newNS1Changes(ns1Delete, changes.Delete)...)
return p.ns1SubmitChanges(combinedChanges)
}
// newNS1Changes returns a collection of Changes based on the given records and action.
func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change {
changes := make([]*ns1Change, 0, len(endpoints))
for _, endpoint := range endpoints {
changes = append(changes, &ns1Change{
Action: action,
Endpoint: endpoint,
},
)
}
return changes
}
// ns1ChangesByZone separates a multi-zone change into a single change per zone.
func ns1ChangesByZone(zones []*dns.Zone, changeSets []*ns1Change) map[string][]*ns1Change {
changes := make(map[string][]*ns1Change)
zoneNameIDMapper := zoneIDName{}
for _, z := range zones {
zoneNameIDMapper.Add(z.Zone, z.Zone)
changes[z.Zone] = []*ns1Change{}
}
for _, c := range changeSets {
zone, _ := zoneNameIDMapper.FindZone(c.Endpoint.DNSName)
if zone == "" {
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Endpoint.DNSName)
continue
}
changes[zone] = append(changes[zone], c)
}
return changes
}
/*
Copyright 2017 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 provider
import (
"fmt"
"net/http"
"os"
"testing"
"github.com/kubernetes-incubator/external-dns/endpoint"
"github.com/kubernetes-incubator/external-dns/plan"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
api "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
)
type MockNS1DomainClient struct {
mock.Mock
}
func (m *MockNS1DomainClient) CreateRecord(r *dns.Record) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1DomainClient) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1DomainClient) UpdateRecord(r *dns.Record) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1DomainClient) GetZone(zone string) (*dns.Zone, *http.Response, error) {
r := &dns.ZoneRecord{
Domain: "test.foo.com",
ShortAns: []string{"2.2.2.2"},
TTL: 3600,
Type: "A",
ID: "123456789abcdefghijklmno",
}
z := &dns.Zone{
Zone: "foo.com",
Records: []*dns.ZoneRecord{r},
TTL: 3600,
ID: "12345678910111213141516a",
}
if zone == "foo.com" {
return z, nil, nil
}
return nil, nil, nil
}
func (m *MockNS1DomainClient) ListZones() ([]*dns.Zone, *http.Response, error) {
zones := []*dns.Zone{
{Zone: "foo.com", ID: "12345678910111213141516a"},
{Zone: "bar.com", ID: "12345678910111213141516b"},
}
return zones, nil, nil
}
type MockNS1GetZoneFail struct{}
func (m *MockNS1GetZoneFail) CreateRecord(r *dns.Record) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1GetZoneFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1GetZoneFail) UpdateRecord(r *dns.Record) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1GetZoneFail) GetZone(zone string) (*dns.Zone, *http.Response, error) {
return nil, nil, api.ErrZoneMissing
}
func (m *MockNS1GetZoneFail) ListZones() ([]*dns.Zone, *http.Response, error) {
zones := []*dns.Zone{
{Zone: "foo.com", ID: "12345678910111213141516a"},
{Zone: "bar.com", ID: "12345678910111213141516b"},
}
return zones, nil, nil
}
type MockNS1ListZonesFail struct{}
func (m *MockNS1ListZonesFail) CreateRecord(r *dns.Record) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1ListZonesFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1ListZonesFail) UpdateRecord(r *dns.Record) (*http.Response, error) {
return nil, nil
}
func (m *MockNS1ListZonesFail) GetZone(zone string) (*dns.Zone, *http.Response, error) {
return &dns.Zone{}, nil, nil
}
func (m *MockNS1ListZonesFail) ListZones() ([]*dns.Zone, *http.Response, error) {
return nil, nil, fmt.Errorf("no zones available")
}
func TestNS1Records(t *testing.T) {
provider := &NS1Provider{
client: &MockNS1DomainClient{},
domainFilter: NewDomainFilter([]string{"foo.com."}),
zoneIDFilter: NewZoneIDFilter([]string{""}),
}
records, err := provider.Records()
require.NoError(t, err)
assert.Equal(t, 1, len(records))
provider.client = &MockNS1GetZoneFail{}
_, err = provider.Records()
require.Error(t, err)
provider.client = &MockNS1ListZonesFail{}
_, err = provider.Records()
require.Error(t, err)
}
func TestNewNS1Provider(t *testing.T) {
_ = os.Setenv("NS1_APIKEY", "xxxxxxxxxxxxxxxxx")
testNS1Config := NS1Config{
DomainFilter: NewDomainFilter([]string{"foo.com."}),
ZoneIDFilter: NewZoneIDFilter([]string{""}),
DryRun: false,
}
_, err := NewNS1Provider(testNS1Config)
require.NoError(t, err)
_ = os.Unsetenv("NS1_APIKEY")
_, err = NewNS1Provider(testNS1Config)
require.Error(t, err)
}
func TestNS1Zones(t *testing.T) {
provider := &NS1Provider{
client: &MockNS1DomainClient{},
domainFilter: NewDomainFilter([]string{"foo.com."}),
zoneIDFilter: NewZoneIDFilter([]string{""}),
}
zones, err := provider.zonesFiltered()
require.NoError(t, err)
validateNS1Zones(t, zones, []*dns.Zone{
{Zone: "foo.com"},
})
}
func validateNS1Zones(t *testing.T, zones []*dns.Zone, expected []*dns.Zone) {
require.Len(t, zones, len(expected))
for i, zone := range zones {
assert.Equal(t, expected[i].Zone, zone.Zone)
}
}
func TestNS1BuildRecord(t *testing.T) {
change := &ns1Change{
Action: ns1Create,
Endpoint: &endpoint.Endpoint{
DNSName: "new",
Targets: endpoint.Targets{"target"},
RecordType: "A",
},
}
record := ns1BuildRecord("foo.com", change)
assert.Equal(t, "foo.com", record.Zone)
assert.Equal(t, "new.foo.com", record.Domain)
assert.Equal(t, ns1DefaultTTL, record.TTL)
changeWithTTL := &ns1Change{
Action: ns1Create,
Endpoint: &endpoint.Endpoint{
DNSName: "new-b",
Targets: endpoint.Targets{"target"},
RecordType: "A",
RecordTTL: 100,
},
}
record = ns1BuildRecord("foo.com", changeWithTTL)
assert.Equal(t, "foo.com", record.Zone)
assert.Equal(t, "new-b.foo.com", record.Domain)
assert.Equal(t, 100, record.TTL)
}
func TestNS1ApplyChanges(t *testing.T) {
changes := &plan.Changes{}
provider := &NS1Provider{
client: &MockNS1DomainClient{},
}
changes.Create = []*endpoint.Endpoint{
{DNSName: "new.foo.com", Targets: endpoint.Targets{"target"}},
{DNSName: "new.subdomain.bar.com", Targets: endpoint.Targets{"target"}},
}
changes.Delete = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target"}}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target-new"}}}
err := provider.ApplyChanges(changes)
require.NoError(t, err)
// empty changes
changes.Create = []*endpoint.Endpoint{}
changes.Delete = []*endpoint.Endpoint{}
changes.UpdateNew = []*endpoint.Endpoint{}
err = provider.ApplyChanges(changes)
require.NoError(t, err)
}
func TestNewNS1Changes(t *testing.T) {
endpoints := []*endpoint.Endpoint{
{
DNSName: "testa.foo.com",
Targets: endpoint.Targets{"target-old"},
RecordType: "A",
},
{
DNSName: "testba.bar.com",
Targets: endpoint.Targets{"target-new"},
RecordType: "A",
},
}
expected := []*ns1Change{
{
Action: "ns1Create",
Endpoint: endpoints[0],
},
{
Action: "ns1Create",
Endpoint: endpoints[1],
},
}
changes := newNS1Changes("ns1Create", endpoints)
require.Len(t, changes, len(expected))
assert.Equal(t, expected, changes)
}
func TestNewNS1ChangesByZone(t *testing.T) {
provider := &NS1Provider{
client: &MockNS1DomainClient{},
}
zones, _ := provider.zonesFiltered()
changeSets := []*ns1Change{
{
Action: "ns1Create",
Endpoint: &endpoint.Endpoint{
DNSName: "new.foo.com",
Targets: endpoint.Targets{"target"},
RecordType: "A",
},
},
{
Action: "ns1Create",
Endpoint: &endpoint.Endpoint{
DNSName: "unrelated.bar.com",
Targets: endpoint.Targets{"target"},
RecordType: "A",
},
},
{
Action: "ns1Delete",
Endpoint: &endpoint.Endpoint{
DNSName: "test.foo.com",
Targets: endpoint.Targets{"target"},
RecordType: "A",
},
},
{
Action: "ns1Update",
Endpoint: &endpoint.Endpoint{
DNSName: "test.foo.com",
Targets: endpoint.Targets{"target-new"},
RecordType: "A",
},
},
}
changes := ns1ChangesByZone(zones, changeSets)
assert.Len(t, changes["bar.com"], 1)
assert.Len(t, changes["foo.com"], 3)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment