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

Merge pull request #625 from FaKod/external-dns-exoscale

Added Exoscale as Provider
parents 8382e2a0 0fd3a7af
......@@ -190,6 +190,12 @@
]
revision = "09691a3b6378b740595c1002f40c34dd5f218a22"
[[projects]]
name = "github.com/exoscale/egoscale"
packages = ["."]
revision = "631ee6ea16ccb48a0c98054fdbf0f6e94d8f4a8c"
version = "v0.9.31"
[[projects]]
branch = "master"
name = "github.com/ffledgling/pdns-go"
......@@ -288,6 +294,12 @@
packages = ["."]
revision = "61dc5f9b0a655ebf43026f0d8a837ad1e28e4b96"
[[projects]]
branch = "master"
name = "github.com/jinzhu/copier"
packages = ["."]
revision = "7e38e58719c33e0d44d585c4ab477a30f8cb82dd"
[[projects]]
name = "github.com/jmespath/go-jmespath"
packages = ["."]
......@@ -679,6 +691,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "66dc2d3612a3cea92d6533aef837db593aa9b49b2eeffe724d7211ceba87294b"
inputs-digest = "d704eb6432ef9b41338900e647421a195366f87134918f9feb023fc377064f57"
solver-name = "gps-cdcl"
solver-version = 1
......@@ -68,6 +68,10 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"]
name = "github.com/nesv/go-dynect"
version = "0.6.0"
[[constraint]]
name = "github.com/exoscale/egoscale"
version = "~0.9.31"
[[constraint]]
name = "github.com/oracle/oci-go-sdk"
version = "1.8.0"
......@@ -36,6 +36,7 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected
* [OpenStack Designate](https://docs.openstack.org/designate/latest/)
* [PowerDNS](https://www.powerdns.com/)
* [CoreDNS](https://coredns.io/)
* [Exoscale](https://www.exoscale.com/dns/)
* [Oracle Cloud Infrastructure DNS](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm)
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.
......@@ -58,6 +59,7 @@ The following tutorials are provided:
* Google Container Engine
* [Using Google's Default Ingress Controller](docs/tutorials/gke.md)
* [Using the Nginx Ingress Controller](docs/tutorials/nginx-ingress.md)
* [Exoscale](docs/tutorials/exoscale.md)
* [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md)
## Running Locally
......
# Setting up ExternalDNS for Exoscale
## Prerequisites
Exoscale provider support was added via [this PR](https://github.com/kubernetes-incubator/external-dns/pull/625), thus you need to use external-dns v0.5.5.
The Exoscale provider expects that your Exoscale zones, you wish to add records to, already exists
and are configured correctly. It does not add, remove or configure new zones in anyway.
To do this pease refer to the [Exoscale DNS documentation](https://community.exoscale.com/documentation/dns/).
Additionally you will have to provide the Exoscale...:
* API Key
* API Secret
* API Endpoint
* Elastic IP address, to access the workers
## Deployment
Deploying external DNS for Exoscale is actually nearly identical to deploying
it for other providers. This is what a sample `deployment.yaml` looks like:
```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
# Only use if you're also using RBAC
# serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:v0.5.5
args:
- --source=ingress # or service or both
- --provider=exoscale
- --domain-filter={{ my-domain }}
- --policy=sync # if you want DNS entries to get deleted as well
- --txt-owner-id={{ owner-id-for-this-external-dns }}
- --exoscale-endpoint={{ endpoint }} # usually https://api.exoscale.ch/dns
- --exoscale-apikey={{ api-key}}
- --exoscale-apisecret={{ api-secret }}
```
## RBAC
If your cluster is RBAC enabled, you also need to setup the following, before you can run external-dns:
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: default
---
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
```
## Testing and Verification
**Important!**: Remember to change `example.com` with your own domain throughout the following text.
Spin up a simple nginx HTTP server with the following spec (`kubectl apply -f`):
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: nginx
external-dns.alpha.kubernetes.io/target: {{ Elastic-IP-address }}
spec:
rules:
- host: via-ingress.example.com
http:
paths:
- backend:
serviceName: nginx
servicePort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 80
targetPort: 80
selector:
app: nginx
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
```
**Important!**: Don't run dig, nslookup or similar immediately (until you've
confirmed the record exists). You'll get hit by [negative DNS caching](https://tools.ietf.org/html/rfc2308), which is hard to flush.
Wait about 30s-1m (interval for external-dns to kick in), then check Exoscales [portal](https://portal.exoscale.com/dns/example.com)... via-ingress.example.com should appear as a A and TXT record with your Elastic-IP-address.
\ No newline at end of file
......@@ -151,6 +151,8 @@ func main() {
)
case "coredns", "skydns":
p, err = provider.NewCoreDNSProvider(domainFilter, cfg.DryRun)
case "exoscale":
p, err = provider.NewExoscaleProvider(cfg.ExoscaleEndpoint, cfg.ExoscaleAPIKey, cfg.ExoscaleAPISecret, cfg.DryRun, provider.ExoscaleWithDomain(domainFilter), provider.ExoscaleWithLogging()), nil
case "inmemory":
p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
case "designate":
......
......@@ -86,6 +86,9 @@ type Config struct {
MetricsAddress string
LogLevel string
TXTCacheInterval time.Duration
ExoscaleEndpoint string
ExoscaleAPIKey string
ExoscaleAPISecret string
}
var defaultConfig = &Config{
......@@ -134,6 +137,9 @@ var defaultConfig = &Config{
LogFormat: "text",
MetricsAddress: ":7979",
LogLevel: logrus.InfoLevel.String(),
ExoscaleEndpoint: "https://api.exoscale.ch/dns",
ExoscaleAPIKey: "",
ExoscaleAPISecret: "",
}
// NewConfig returns new Config object
......@@ -187,7 +193,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("connector-source-server", "The server to connect for connector source, valid only when using connector source").Default(defaultConfig.ConnectorSourceServer).StringVar(&cfg.ConnectorSourceServer)
// 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, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci")
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale")
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)
......@@ -220,6 +226,10 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("tls-client-cert", "When using TLS communication, the path to the certificate to present as a client (not required for TLS)").Default(defaultConfig.TLSClientCert).StringVar(&cfg.TLSClientCert)
app.Flag("tls-client-cert-key", "When using TLS communication, the path to the certificate key to use with the client certificate (not required for TLS)").Default(defaultConfig.TLSClientCertKey).StringVar(&cfg.TLSClientCertKey)
app.Flag("exoscale-endpoint", "Provide the endpoint for the Exoscale provider").Default(defaultConfig.ExoscaleEndpoint).StringVar(&cfg.ExoscaleEndpoint)
app.Flag("exoscale-apikey", "Provide your API Key for the Exoscale provider").Default(defaultConfig.ExoscaleAPIKey).StringVar(&cfg.ExoscaleAPIKey)
app.Flag("exoscale-apisecret", "Provide your API Secret for the Exoscale provider").Default(defaultConfig.ExoscaleAPISecret).StringVar(&cfg.ExoscaleAPISecret)
// Flags related to policies
app.Flag("policy", "Modify how DNS records are sychronized between sources and providers (default: sync, options: sync, upsert-only)").Default(defaultConfig.Policy).EnumVar(&cfg.Policy, "sync", "upsert-only")
......
......@@ -68,6 +68,9 @@ var (
MetricsAddress: ":7979",
LogLevel: logrus.InfoLevel.String(),
ConnectorSourceServer: "localhost:8080",
ExoscaleEndpoint: "https://api.exoscale.ch/dns",
ExoscaleAPIKey: "",
ExoscaleAPISecret: "",
}
overriddenConfig = &Config{
......@@ -114,6 +117,9 @@ var (
MetricsAddress: "127.0.0.1:9099",
LogLevel: logrus.DebugLevel.String(),
ConnectorSourceServer: "localhost:8081",
ExoscaleEndpoint: "https://api.foo.ch/dns",
ExoscaleAPIKey: "1",
ExoscaleAPISecret: "2",
}
)
......@@ -184,6 +190,9 @@ func TestParseFlags(t *testing.T) {
"--metrics-address=127.0.0.1:9099",
"--log-level=debug",
"--connector-source-server=localhost:8081",
"--exoscale-endpoint=https://api.foo.ch/dns",
"--exoscale-apikey=1",
"--exoscale-apisecret=2",
},
envVars: map[string]string{},
expected: overriddenConfig,
......@@ -235,6 +244,9 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_METRICS_ADDRESS": "127.0.0.1:9099",
"EXTERNAL_DNS_LOG_LEVEL": "debug",
"EXTERNAL_DNS_CONNECTOR_SOURCE_SERVER": "localhost:8081",
"EXTERNAL_DNS_EXOSCALE_ENDPOINT": "https://api.foo.ch/dns",
"EXTERNAL_DNS_EXOSCALE_APIKEY": "1",
"EXTERNAL_DNS_EXOSCALE_APISECRET": "2",
},
expected: overriddenConfig,
},
......
/*
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 (
"strings"
"github.com/exoscale/egoscale"
"github.com/kubernetes-incubator/external-dns/endpoint"
"github.com/kubernetes-incubator/external-dns/plan"
log "github.com/sirupsen/logrus"
)
// EgoscaleClientI for replaceable implementation
type EgoscaleClientI interface {
GetRecords(string) ([]egoscale.DNSRecord, error)
GetDomains() ([]egoscale.DNSDomain, error)
CreateRecord(string, egoscale.DNSRecord) (*egoscale.DNSRecord, error)
DeleteRecord(string, int64) error
UpdateRecord(string, egoscale.UpdateDNSRecord) (*egoscale.DNSRecord, error)
}
// ExoscaleProvider initialized as dns provider with no records
type ExoscaleProvider struct {
domain DomainFilter
client EgoscaleClientI
filter *zoneFilter
OnApplyChanges func(changes *plan.Changes)
dryRun bool
}
// ExoscaleOption for Provider options
type ExoscaleOption func(*ExoscaleProvider)
// NewExoscaleProvider returns ExoscaleProvider DNS provider interface implementation
func NewExoscaleProvider(endpoint, apiKey, apiSecret string, dryRun bool, opts ...ExoscaleOption) *ExoscaleProvider {
client := egoscale.NewClient(endpoint, apiKey, apiSecret)
return NewExoscaleProviderWithClient(endpoint, apiKey, apiSecret, client, dryRun, opts...)
}
// NewExoscaleProviderWithClient returns ExoscaleProvider DNS provider interface implementation (Client provided)
func NewExoscaleProviderWithClient(endpoint, apiKey, apiSecret string, client EgoscaleClientI, dryRun bool, opts ...ExoscaleOption) *ExoscaleProvider {
ep := &ExoscaleProvider{
filter: &zoneFilter{},
OnApplyChanges: func(changes *plan.Changes) {},
domain: NewDomainFilter([]string{""}),
client: client,
dryRun: dryRun,
}
for _, opt := range opts {
opt(ep)
}
return ep
}
func (ep *ExoscaleProvider) getZones() (map[int64]string, error) {
dom, err := ep.client.GetDomains()
if err != nil {
return nil, err
}
zones := map[int64]string{}
for _, d := range dom {
zones[d.ID] = d.Name
}
return zones, nil
}
// ApplyChanges simply modifies DNS via exoscale API
func (ep *ExoscaleProvider) ApplyChanges(changes *plan.Changes) error {
ep.OnApplyChanges(changes)
if ep.dryRun {
log.Infof("Will NOT delete these records: %+v", changes.Delete)
log.Infof("Will NOT create these records: %+v", changes.Create)
log.Infof("Will NOT update these records: %+v", merge(changes.UpdateOld, changes.UpdateNew))
return nil
}
zones, err := ep.getZones()
if err != nil {
return err
}
for _, epoint := range changes.Create {
if ep.domain.Match(epoint.DNSName) {
if zoneID, name := ep.filter.EndpointZoneID(epoint, zones); zoneID != 0 {
rec := egoscale.DNSRecord{
Name: name,
RecordType: epoint.RecordType,
TTL: int(epoint.RecordTTL),
Content: epoint.Targets[0],
}
_, err := ep.client.CreateRecord(zones[zoneID], rec)
if err != nil {
return err
}
}
}
}
for _, epoint := range changes.UpdateNew {
if ep.domain.Match(epoint.DNSName) {
if zoneID, name := ep.filter.EndpointZoneID(epoint, zones); zoneID != 0 {
records, err := ep.client.GetRecords(zones[zoneID])
if err != nil {
return err
}
for _, r := range records {
if r.Name == name {
rec := egoscale.UpdateDNSRecord{
ID: r.ID,
DomainID: r.DomainID,
Name: name,
RecordType: epoint.RecordType,
TTL: int(epoint.RecordTTL),
Content: epoint.Targets[0],
Prio: r.Prio,
}
if _, err := ep.client.UpdateRecord(zones[zoneID], rec); err != nil {
return err
}
break
}
}
}
}
}
for _, epoint := range changes.UpdateOld {
// Since Exoscale "Patches", we ignore UpdateOld
// We leave this logging here for information
log.Debugf("UPDATE-OLD (ignored) for epoint: %+v", epoint)
}
for _, epoint := range changes.Delete {
if ep.domain.Match(epoint.DNSName) {
if zoneID, name := ep.filter.EndpointZoneID(epoint, zones); zoneID != 0 {
records, err := ep.client.GetRecords(zones[zoneID])
if err != nil {
return err
}
for _, r := range records {
if r.Name == name {
if err := ep.client.DeleteRecord(zones[zoneID], r.ID); err != nil {
return err
}
break
}
}
}
}
}
return nil
}
// Records returns the list of endpoints
func (ep *ExoscaleProvider) Records() ([]*endpoint.Endpoint, error) {
endpoints := make([]*endpoint.Endpoint, 0)
dom, err := ep.client.GetDomains()
if err != nil {
return nil, err
}
for _, d := range dom {
record, err := ep.client.GetRecords(d.Name)
if err != nil {
return nil, err
}
for _, r := range record {
switch r.RecordType {
case "A", "CNAME", "TXT":
break
default:
continue
}
ep := endpoint.NewEndpointWithTTL(r.Name+"."+d.Name, r.RecordType, endpoint.TTL(r.TTL), r.Content)
endpoints = append(endpoints, ep)
}
}
log.Infof("called Records() with %d items", len(endpoints))
return endpoints, nil
}
// ExoscaleWithDomain modifies the domain on which dns zones are filtered
func ExoscaleWithDomain(domainFilter DomainFilter) ExoscaleOption {
return func(p *ExoscaleProvider) {
p.domain = domainFilter
}
}
// ExoscaleWithLogging injects logging when ApplyChanges is called
func ExoscaleWithLogging() ExoscaleOption {
return func(p *ExoscaleProvider) {
p.OnApplyChanges = func(changes *plan.Changes) {
for _, v := range changes.Create {
log.Infof("CREATE: %v", v)
}
for _, v := range changes.UpdateOld {
log.Infof("UPDATE (old): %v", v)
}
for _, v := range changes.UpdateNew {
log.Infof("UPDATE (new): %v", v)
}
for _, v := range changes.Delete {
log.Infof("DELETE: %v", v)
}
}
}
}
type zoneFilter struct {
domain string
}
// Zones filters map[zoneID]zoneName for names having f.domain as suffix
func (f *zoneFilter) Zones(zones map[int64]string) map[int64]string {
result := map[int64]string{}
for zoneID, zoneName := range zones {
if strings.HasSuffix(zoneName, f.domain) {
result[zoneID] = zoneName
}
}
return result
}
// EndpointZoneID determines zoneID for endpoint from map[zoneID]zoneName by taking longest suffix zoneName match in endpoint DNSName
// returns 0 if no match found
func (f *zoneFilter) EndpointZoneID(endpoint *endpoint.Endpoint, zones map[int64]string) (zoneID int64, name string) {
var matchZoneID int64
var matchZoneName string
for zoneID, zoneName := range zones {
if strings.HasSuffix(endpoint.DNSName, "."+zoneName) && len(zoneName) > len(matchZoneName) {
matchZoneName = zoneName
matchZoneID = zoneID
name = strings.TrimSuffix(endpoint.DNSName, "."+zoneName)
}
}
return matchZoneID, name
}
/*
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 (
"strings"
"testing"
"github.com/exoscale/egoscale"
"github.com/kubernetes-incubator/external-dns/endpoint"
"github.com/kubernetes-incubator/external-dns/plan"
"github.com/stretchr/testify/assert"
)
type createRecordExoscale struct {
name string
rec egoscale.DNSRecord
}
type deleteRecordExoscale struct {
name string
recordID int64
}
type updateRecordExoscale struct {
name string
updateDNSRecord egoscale.UpdateDNSRecord
}
var createExoscale []createRecordExoscale
var deleteExoscale []deleteRecordExoscale
var updateExoscale []updateRecordExoscale
type ExoscaleClientStub struct {
}
func NewExoscaleClientStub() EgoscaleClientI {
ep := &ExoscaleClientStub{}
return ep
}
func (ep *ExoscaleClientStub) DeleteRecord(name string, recordID int64) error {
deleteExoscale = append(deleteExoscale, deleteRecordExoscale{name: name, recordID: recordID})
return nil
}
func (ep *ExoscaleClientStub) GetRecords(name string) ([]egoscale.DNSRecord, error) {
init := []egoscale.DNSRecord{
{ID: 0, Name: "v4.barfoo.com", RecordType: "ALIAS"},
{ID: 1, Name: "v1.foo.com", RecordType: "TXT"},
{ID: 2, Name: "v2.bar.com", RecordType: "A"},
{ID: 3, Name: "v3.bar.com", RecordType: "ALIAS"},
{ID: 4, Name: "v2.foo.com", RecordType: "CNAME"},
{ID: 5, Name: "v1.foobar.com", RecordType: "TXT"},
}
rec := make([]egoscale.DNSRecord, 0)
for _, r := range init {
if strings.HasSuffix(r.Name, "."+name) {
r.Name = strings.TrimSuffix(r.Name, "."+name)
rec = append(rec, r)
}
}
return rec, nil
}
func (ep *ExoscaleClientStub) UpdateRecord(name string, rec egoscale.UpdateDNSRecord) (*egoscale.DNSRecord, error) {
updateExoscale = append(updateExoscale, updateRecordExoscale{name: name, updateDNSRecord: rec})
return nil, nil
}
func (ep *ExoscaleClientStub) CreateRecord(name string, rec egoscale.DNSRecord) (*egoscale.DNSRecord, error) {
createExoscale = append(createExoscale, createRecordExoscale{name: name, rec: rec})
return nil, nil
}
func (ep *ExoscaleClientStub) GetDomains() ([]egoscale.DNSDomain, error) {
dom := []egoscale.DNSDomain{
{ID: 1, Name: "foo.com"},
{ID: 2, Name: "bar.com"},
}
return dom, nil
}
func contains(arr []*endpoint.Endpoint, name string) bool {
for _, a := range arr {
if a.DNSName == name {
return true
}
}
return false
}
func TestExoscaleGetRecords(t *testing.T) {
provider := NewExoscaleProviderWithClient("", "", "", NewExoscaleClientStub(), false)
if recs, err := provider.Records(); err == nil {
assert.Equal(t, 3, len(recs))
assert.True(t, contains(recs, "v1.foo.com"))
assert.True(t, contains(recs, "v2.bar.com"))
assert.True(t, contains(recs, "v2.foo.com"))
assert.False(t, contains(recs, "v3.bar.com"))
assert.False(t, contains(recs, "v1.foobar.com"))
} else {
assert.Error(t, err)
}
}
func TestExoscaleApplyChanges(t *testing.T) {
provider := NewExoscaleProviderWithClient("", "", "", NewExoscaleClientStub(), false)
plan := &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "v1.foo.com",
RecordType: "A",
Targets: []string{""},
},
{
DNSName: "v1.foobar.com",
RecordType: "TXT",
Targets: []string{""},
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "v1.foo.com",
RecordType: "A",
Targets: []string{""},
},
{
DNSName: "v1.foobar.com",
RecordType: "TXT",
Targets: []string{""},
},
},
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "v1.foo.com",
RecordType: "A",
Targets: []string{""},
},
{
DNSName: "v1.foobar.com",
RecordType: "TXT",
Targets: []string{""},
},
},
UpdateNew: []*endpoint.Endpoint{
{
DNSName: "v1.foo.com",
RecordType: "A",
Targets: []string{""},
},
{
DNSName: "v1.foobar.com",
RecordType: "TXT",
Targets: []string{""},
},
},
}
createExoscale = make([]createRecordExoscale, 0)
deleteExoscale = make([]deleteRecordExoscale, 0)
provider.ApplyChanges(plan)
assert.Equal(t, 1, len(createExoscale))
assert.Equal(t, "foo.com", createExoscale[0].name)
assert.Equal(t, "v1", createExoscale[0].rec.Name)
assert.Equal(t, 1, len(deleteExoscale))
assert.Equal(t, "foo.com", deleteExoscale[0].name)
assert.Equal(t, int64(1), deleteExoscale[0].recordID)
assert.Equal(t, 1, len(updateExoscale))
assert.Equal(t, "foo.com", updateExoscale[0].name)
assert.Equal(t, int64(1), updateExoscale[0].updateDNSRecord.ID)
}
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