mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-07 01:56:57 +02:00
Add RcodeZero Anycast DNS provider
This commit is contained in:
parent
623ecd07f0
commit
79bf8c807e
@ -29,6 +29,7 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected
|
|||||||
* [AWS Service Discovery](https://docs.aws.amazon.com/Route53/latest/APIReference/overview-service-discovery.html)
|
* [AWS Service Discovery](https://docs.aws.amazon.com/Route53/latest/APIReference/overview-service-discovery.html)
|
||||||
* [AzureDNS](https://azure.microsoft.com/en-us/services/dns)
|
* [AzureDNS](https://azure.microsoft.com/en-us/services/dns)
|
||||||
* [CloudFlare](https://www.cloudflare.com/dns)
|
* [CloudFlare](https://www.cloudflare.com/dns)
|
||||||
|
* [RcodeZero](https://www.rcodezero.at/)
|
||||||
* [DigitalOcean](https://www.digitalocean.com/products/networking)
|
* [DigitalOcean](https://www.digitalocean.com/products/networking)
|
||||||
* [DNSimple](https://dnsimple.com/)
|
* [DNSimple](https://dnsimple.com/)
|
||||||
* [Infoblox](https://www.infoblox.com/products/dns/)
|
* [Infoblox](https://www.infoblox.com/products/dns/)
|
||||||
@ -57,6 +58,7 @@ The following tutorials are provided:
|
|||||||
* [Azure](docs/tutorials/azure.md)
|
* [Azure](docs/tutorials/azure.md)
|
||||||
* [CoreDNS](docs/tutorials/coredns.md)
|
* [CoreDNS](docs/tutorials/coredns.md)
|
||||||
* [Cloudflare](docs/tutorials/cloudflare.md)
|
* [Cloudflare](docs/tutorials/cloudflare.md)
|
||||||
|
* [RcodeZero](docs/tutorials/rcodezero.md)
|
||||||
* [DigitalOcean](docs/tutorials/digitalocean.md)
|
* [DigitalOcean](docs/tutorials/digitalocean.md)
|
||||||
* [Infoblox](docs/tutorials/infoblox.md)
|
* [Infoblox](docs/tutorials/infoblox.md)
|
||||||
* [Dyn](docs/tutorials/dyn.md)
|
* [Dyn](docs/tutorials/dyn.md)
|
||||||
|
194
docs/tutorials/rcodezero.md
Normal file
194
docs/tutorials/rcodezero.md
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
# Setting up ExternalDNS for Services on RcodeZero
|
||||||
|
|
||||||
|
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using RcodeZero Anycast DNS.
|
||||||
|
|
||||||
|
Make sure to use **>=0.5.0** version of ExternalDNS for this tutorial.
|
||||||
|
|
||||||
|
## Creating a RcodeZero DNS zone
|
||||||
|
|
||||||
|
After logging into RcodeZero Dashboard add a master domain under [RcodeZero Add Zone](https://my.rcodezero.at/domain/create). Use it throughout this guide (substitute example.com).
|
||||||
|
|
||||||
|
## Creating RcodeZero Credentials
|
||||||
|
|
||||||
|
> The RcodeZero Anycast-Network is provisioned via web interface or REST-API.
|
||||||
|
|
||||||
|
RcodeZero API can be enabled and a key generated on [RcodeZero API](https://my.rcodezero.at/enableapi)
|
||||||
|
|
||||||
|
The environment var `RC0_API_KEY` will be needed to run ExternalDNS with RcodeZero.
|
||||||
|
|
||||||
|
## Deploy ExternalDNS
|
||||||
|
|
||||||
|
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||||
|
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||||
|
|
||||||
|
### 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=rcodezero
|
||||||
|
- --rc0-enc-txt # (optional) encrypt TXT records; encryption key has to be provided with RC0_ENC_KEY env var.
|
||||||
|
env:
|
||||||
|
- name: RC0_API_KEY
|
||||||
|
value: "YOUR_RCODEZERO_API_KEY"
|
||||||
|
- name: RC0_ENC_VAR
|
||||||
|
value: "YOUR_ENCRYPTION_KEY_STRING"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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=rcodezero
|
||||||
|
- --rc0-enc-txt # (optional) encrypt TXT records; encryption key has to be provided with RC0_ENC_KEY env var.
|
||||||
|
env:
|
||||||
|
- name: RC0_API_KEY
|
||||||
|
value: "YOUR_RCODEZERO_API_KEY"
|
||||||
|
- name: RC0_ENC_VAR
|
||||||
|
value: "YOUR_ENCRYPTION_KEY_STRING"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the annotation on the service; use the same hostname as the RcodeZero DNS zone created above. The annotation may also be a subdomain
|
||||||
|
of the DNS zone (e.g. 'www.example.com').
|
||||||
|
|
||||||
|
By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above.
|
||||||
|
This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300.
|
||||||
|
|
||||||
|
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation
|
||||||
|
will cause ExternalDNS to remove the corresponding DNS records.
|
||||||
|
|
||||||
|
Create the deployment and service:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl create -f nginx.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.
|
||||||
|
|
||||||
|
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize
|
||||||
|
the RcodeZero DNS records.
|
||||||
|
|
||||||
|
## Verifying RcodeZero DNS records
|
||||||
|
|
||||||
|
Check your [RcodeZero Configured Zones](https://my.rcodezero.at/domain) and select the ExternalDNS managed domain.
|
||||||
|
|
||||||
|
Substitute the zone for the one created above if a different domain was used.
|
||||||
|
|
||||||
|
This should show the external IP address of the service as the A record for your domain.
|
||||||
|
|
||||||
|
## Cleanup
|
||||||
|
|
||||||
|
Now that we have verified that ExternalDNS will automatically manage RcodeZero DNS records, we can delete the tutorial's example:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl delete -f nginx.yaml
|
||||||
|
$ kubectl delete -f externaldns.yaml
|
||||||
|
```
|
4
main.go
4
main.go
@ -132,7 +132,9 @@ func main() {
|
|||||||
p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
|
p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
|
||||||
case "cloudflare":
|
case "cloudflare":
|
||||||
p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun)
|
p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun)
|
||||||
case "google":
|
case "rcodezero":
|
||||||
|
p, err = provider.NewRcodeZeroProvider(domainFilter, cfg.DryRun, cfg.RcodezeroTXTEncrypt)
|
||||||
|
case "google":
|
||||||
p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
|
p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
|
||||||
case "digitalocean":
|
case "digitalocean":
|
||||||
p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
|
p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
|
||||||
|
@ -67,6 +67,7 @@ type Config struct {
|
|||||||
AzureResourceGroup string
|
AzureResourceGroup string
|
||||||
CloudflareProxied bool
|
CloudflareProxied bool
|
||||||
CloudflareZonesPerPage int
|
CloudflareZonesPerPage int
|
||||||
|
RcodezeroTXTEncrypt bool
|
||||||
InfobloxGridHost string
|
InfobloxGridHost string
|
||||||
InfobloxWapiPort int
|
InfobloxWapiPort int
|
||||||
InfobloxWapiUsername string
|
InfobloxWapiUsername string
|
||||||
@ -142,6 +143,7 @@ var defaultConfig = &Config{
|
|||||||
AzureResourceGroup: "",
|
AzureResourceGroup: "",
|
||||||
CloudflareProxied: false,
|
CloudflareProxied: false,
|
||||||
CloudflareZonesPerPage: 50,
|
CloudflareZonesPerPage: 50,
|
||||||
|
RcodezeroTXTEncrypt: false,
|
||||||
InfobloxGridHost: "",
|
InfobloxGridHost: "",
|
||||||
InfobloxWapiPort: 443,
|
InfobloxWapiPort: 443,
|
||||||
InfobloxWapiUsername: "admin",
|
InfobloxWapiUsername: "admin",
|
||||||
@ -243,7 +245,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)
|
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
|
// 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, exoscale, linode, rfc2136)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136")
|
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("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("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)
|
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)
|
||||||
@ -271,6 +273,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
app.Flag("dyn-password", "When using the Dyn provider, specify the pasword").Default("").StringVar(&cfg.DynPassword)
|
app.Flag("dyn-password", "When using the Dyn provider, specify the pasword").Default("").StringVar(&cfg.DynPassword)
|
||||||
app.Flag("dyn-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.DynMinTTLSeconds)
|
app.Flag("dyn-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.DynMinTTLSeconds)
|
||||||
app.Flag("oci-config-file", "When using the OCI provider, specify the OCI configuration file (required when --provider=oci").Default(defaultConfig.OCIConfigFile).StringVar(&cfg.OCIConfigFile)
|
app.Flag("oci-config-file", "When using the OCI provider, specify the OCI configuration file (required when --provider=oci").Default(defaultConfig.OCIConfigFile).StringVar(&cfg.OCIConfigFile)
|
||||||
|
app.Flag("rc0-txt-enc", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").BoolVar(&cfg.RcodezeroTXTEncrypt)
|
||||||
|
|
||||||
app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones)
|
app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones)
|
||||||
app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer)
|
app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer)
|
||||||
|
336
provider/rcode0.go
Normal file
336
provider/rcode0.go
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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"
|
||||||
|
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||||
|
"github.com/kubernetes-incubator/external-dns/plan"
|
||||||
|
rc0 "github.com/nic-at/rc0go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RcodeZeroProvider implements the DNS provider for RcodeZero Anycast DNS.
|
||||||
|
type RcodeZeroProvider struct {
|
||||||
|
Client *rc0.Client
|
||||||
|
|
||||||
|
DomainFilter DomainFilter
|
||||||
|
DryRun bool
|
||||||
|
TXTEncrypt bool
|
||||||
|
Key []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRcodeZeroProvider creates a new RcodeZero Anycast DNS provider.
|
||||||
|
//
|
||||||
|
// Returns the provider or an error if a provider could not be created.
|
||||||
|
func NewRcodeZeroProvider(domainFilter DomainFilter, dryRun bool, txtEnc bool) (*RcodeZeroProvider, error){
|
||||||
|
|
||||||
|
client, err := rc0.NewClient(os.Getenv("RC0_API_KEY"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
value := os.Getenv("RC0_BASE_URL")
|
||||||
|
if len(value) != 0 {
|
||||||
|
client.BaseURL, _ = url.Parse(os.Getenv("RC0_BASE_URL"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize rcodezero provider: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{
|
||||||
|
Client: client,
|
||||||
|
DomainFilter: domainFilter,
|
||||||
|
DryRun: dryRun,
|
||||||
|
TXTEncrypt: txtEnc,
|
||||||
|
}
|
||||||
|
|
||||||
|
if txtEnc {
|
||||||
|
provider.Key = []byte(os.Getenv("RC0_ENC_KEY"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns filtered zones if filter is set
|
||||||
|
func (p *RcodeZeroProvider) Zones() ([]*rc0.Zone, error) {
|
||||||
|
|
||||||
|
var result []*rc0.Zone
|
||||||
|
|
||||||
|
zones, err := p.fetchZones()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, zone := range zones {
|
||||||
|
if p.DomainFilter.Match(zone.Domain) {
|
||||||
|
result = append(result, zone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns resource records
|
||||||
|
//
|
||||||
|
// Decrypts TXT records if TXT-Encrypt flag is set and key is provided
|
||||||
|
func (p *RcodeZeroProvider) Records() ([]*endpoint.Endpoint, error) {
|
||||||
|
|
||||||
|
zones, err := p.Zones()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var endpoints []*endpoint.Endpoint
|
||||||
|
|
||||||
|
for _, zone := range zones {
|
||||||
|
|
||||||
|
rrset, err := p.fetchRecords(zone.Domain)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rrset {
|
||||||
|
|
||||||
|
if supportedRecordType(r.Type) {
|
||||||
|
|
||||||
|
if p.TXTEncrypt && (p.Key != nil) && strings.EqualFold(r.Type, "TXT") {
|
||||||
|
p.Client.RRSet.DecryptTXT(p.Key, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.Records) > 1 {
|
||||||
|
|
||||||
|
for _, _r := range r.Records {
|
||||||
|
if !_r.Disabled {
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), _r.Content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if !r.Records[0].Disabled {
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Records[0].Content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyChanges applies a given set of changes in a given zone.
|
||||||
|
func (p *RcodeZeroProvider) ApplyChanges(changes *plan.Changes) error {
|
||||||
|
|
||||||
|
combinedChanges := make([]*rc0.RRSetChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete))
|
||||||
|
|
||||||
|
combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeADD, changes.Create)...)
|
||||||
|
combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeUPDATE, changes.UpdateNew)...)
|
||||||
|
combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeDELETE, changes.Delete)...)
|
||||||
|
|
||||||
|
return p.submitChanges(combinedChanges)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function
|
||||||
|
func rcodezeroChangesByZone(zones []*rc0.Zone, changeSet []*rc0.RRSetChange) map[string][]*rc0.RRSetChange {
|
||||||
|
|
||||||
|
changes := make(map[string][]*rc0.RRSetChange)
|
||||||
|
zoneNameIDMapper := zoneIDName{}
|
||||||
|
for _, z := range zones {
|
||||||
|
zoneNameIDMapper.Add(z.Domain, z.Domain)
|
||||||
|
changes[z.Domain] = []*rc0.RRSetChange{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range changeSet {
|
||||||
|
zone, _ := zoneNameIDMapper.FindZone(c.Name)
|
||||||
|
if zone == "" {
|
||||||
|
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changes[zone] = append(changes[zone], c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function
|
||||||
|
func (p *RcodeZeroProvider) fetchRecords(zoneName string) ([]*rc0.RRType, error) {
|
||||||
|
|
||||||
|
var allRecords []*rc0.RRType
|
||||||
|
|
||||||
|
listOptions := rc0.NewListOptions()
|
||||||
|
|
||||||
|
for {
|
||||||
|
records, page, err := p.Client.RRSet.List(zoneName, listOptions)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allRecords = append(allRecords, records...)
|
||||||
|
|
||||||
|
if page == nil || (page.CurrentPage == page.LastPage) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
listOptions.SetPageNumber(page.CurrentPage + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRecords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function
|
||||||
|
func (p *RcodeZeroProvider) fetchZones() ([]*rc0.Zone, error) {
|
||||||
|
|
||||||
|
var allZones []*rc0.Zone
|
||||||
|
|
||||||
|
listOptions := rc0.NewListOptions()
|
||||||
|
|
||||||
|
for {
|
||||||
|
zones, page, err := p.Client.Zones.List(listOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allZones = append(allZones, zones...)
|
||||||
|
|
||||||
|
if page == nil || page.IsLastPage() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
listOptions.SetPageNumber(page.CurrentPage + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allZones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to submit changes.
|
||||||
|
//
|
||||||
|
// Changes are submitted by change type.
|
||||||
|
func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error {
|
||||||
|
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
zones, err := p.Zones()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// separate into per-zone change sets to be passed to the API.
|
||||||
|
changesByZone := rcodezeroChangesByZone(zones, changes)
|
||||||
|
|
||||||
|
for zoneName, changes := range changesByZone {
|
||||||
|
|
||||||
|
for _, change := range changes {
|
||||||
|
|
||||||
|
logFields := log.Fields{
|
||||||
|
"record" : change.Name,
|
||||||
|
"content" : change.Records[0].Content,
|
||||||
|
"type" : change.Type,
|
||||||
|
"action" : change.ChangeType,
|
||||||
|
"zone" : zoneName,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(logFields).Info("Changing record.")
|
||||||
|
|
||||||
|
if p.DryRun {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// to avoid accidentally adding extra dot if already present
|
||||||
|
change.Name = strings.TrimSuffix(change.Name, ".") + "."
|
||||||
|
|
||||||
|
switch change.ChangeType {
|
||||||
|
case rc0.ChangeTypeADD:
|
||||||
|
sr, err := p.Client.RRSet.Create(zoneName, []*rc0.RRSetChange{change})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.HasError() {
|
||||||
|
return fmt.Errorf("adding new RR resulted in an error: %v", sr.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
case rc0.ChangeTypeUPDATE:
|
||||||
|
sr, err := p.Client.RRSet.Edit(zoneName, []*rc0.RRSetChange{change})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.HasError() {
|
||||||
|
return fmt.Errorf("updating existing RR resulted in an error: %v", sr.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
case rc0.ChangeTypeDELETE:
|
||||||
|
sr, err := p.Client.RRSet.Delete(zoneName, []*rc0.RRSetChange{change})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.HasError() {
|
||||||
|
return fmt.Errorf("deleting existing RR resulted in an error: %v", sr.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported changeType submitted: %v", change.ChangeType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a RcodeZero specific array with rrset change objects.
|
||||||
|
func (p *RcodeZeroProvider) NewRcodezeroChanges(action string, endpoints []*endpoint.Endpoint) []*rc0.RRSetChange {
|
||||||
|
|
||||||
|
changes := make([]*rc0.RRSetChange, 0, len(endpoints))
|
||||||
|
|
||||||
|
for _, _endpoint := range endpoints {
|
||||||
|
changes = append(changes, p.NewRcodezeroChange(action, _endpoint))
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a RcodeZero specific rrset change object.
|
||||||
|
func (p *RcodeZeroProvider) NewRcodezeroChange(action string, endpoint *endpoint.Endpoint) *rc0.RRSetChange {
|
||||||
|
|
||||||
|
change := &rc0.RRSetChange{
|
||||||
|
Type: endpoint.RecordType,
|
||||||
|
ChangeType: action,
|
||||||
|
Name: endpoint.DNSName,
|
||||||
|
Records: []*rc0.Record{{
|
||||||
|
Disabled: false,
|
||||||
|
Content: endpoint.Targets[0],
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.TXTEncrypt && (p.Key != nil) && strings.EqualFold(endpoint.RecordType, "TXT") {
|
||||||
|
p.Client.RRSet.EncryptTXT(p.Key, change)
|
||||||
|
}
|
||||||
|
|
||||||
|
return change
|
||||||
|
}
|
417
provider/rcode0_test.go
Normal file
417
provider/rcode0_test.go
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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"
|
||||||
|
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||||
|
"github.com/kubernetes-incubator/external-dns/plan"
|
||||||
|
rc0 "github.com/nic-at/rc0go"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testZoneOne = "testzone1.at"
|
||||||
|
testZoneTwo = "testzone2.at"
|
||||||
|
|
||||||
|
rrsetChangesUnsupportedChangeType = 0
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockRcodeZeroClient rc0.Client
|
||||||
|
|
||||||
|
type mockZoneManagementService struct {
|
||||||
|
TestNilZonesReturned bool
|
||||||
|
TestErrorReturned bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockRRSetService struct {
|
||||||
|
TestErrorReturned bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockRcodeZeroClient) resetMockServices() {
|
||||||
|
m.Zones = &mockZoneManagementService{}
|
||||||
|
m.RRSet = &mockRRSetService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockZoneManagementService) resetTestConditions() {
|
||||||
|
m.TestNilZonesReturned = false
|
||||||
|
m.TestErrorReturned = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockRRSetService) resetTestConditions() {
|
||||||
|
m.TestErrorReturned = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRcodeZeroProvider_Records(t *testing.T) {
|
||||||
|
|
||||||
|
mockRRSetService := &mockRRSetService{}
|
||||||
|
mockZoneManagementService := &mockZoneManagementService{}
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{
|
||||||
|
Client: (*rc0.Client)(&mockRcodeZeroClient{
|
||||||
|
Zones: mockZoneManagementService,
|
||||||
|
RRSet: mockRRSetService,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints, err := provider.Records() // should return 6 rrs
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not fail, %s", err)
|
||||||
|
}
|
||||||
|
require.Equal(t, 6, len(endpoints))
|
||||||
|
|
||||||
|
mockRRSetService.TestErrorReturned = true
|
||||||
|
|
||||||
|
_, err = provider.Records()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected to fail, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRcodeZeroProvider_ApplyChanges(t *testing.T) {
|
||||||
|
|
||||||
|
mockRRSetService := &mockRRSetService{}
|
||||||
|
mockZoneManagementService := &mockZoneManagementService{}
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{
|
||||||
|
Client: (*rc0.Client)(&mockRcodeZeroClient{
|
||||||
|
Zones: mockZoneManagementService,
|
||||||
|
RRSet: mockRRSetService,
|
||||||
|
}),
|
||||||
|
DomainFilter: NewDomainFilter([]string{testZoneOne}),
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := mockChanges()
|
||||||
|
|
||||||
|
err := provider.ApplyChanges(changes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not fail, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRcodeZeroProvider_NewRcodezeroChanges(t *testing.T) {
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{}
|
||||||
|
|
||||||
|
changes := mockChanges()
|
||||||
|
|
||||||
|
createChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Create)
|
||||||
|
require.Equal(t, 4, len(createChanges))
|
||||||
|
|
||||||
|
deleteChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Delete)
|
||||||
|
require.Equal(t, 1, len(deleteChanges))
|
||||||
|
|
||||||
|
updateOldChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateOld)
|
||||||
|
require.Equal(t, 1, len(updateOldChanges))
|
||||||
|
|
||||||
|
updateNewChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateNew)
|
||||||
|
require.Equal(t, 1, len(updateNewChanges))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRcodeZeroProvider_NewRcodezeroChange(t *testing.T) {
|
||||||
|
|
||||||
|
_endpoint := &endpoint.Endpoint{
|
||||||
|
RecordType: "A",
|
||||||
|
DNSName: "app." + testZoneOne,
|
||||||
|
RecordTTL: 300,
|
||||||
|
Targets: endpoint.Targets{"target"},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{}
|
||||||
|
|
||||||
|
rrsetChange := provider.NewRcodezeroChange(testZoneOne, _endpoint)
|
||||||
|
|
||||||
|
require.Equal(t, _endpoint.RecordType, rrsetChange.Type)
|
||||||
|
require.Equal(t, _endpoint.DNSName, rrsetChange.Name)
|
||||||
|
require.Equal(t, _endpoint.Targets[0], rrsetChange.Records[0].Content)
|
||||||
|
//require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_submitChanges(t *testing.T) {
|
||||||
|
|
||||||
|
mockRRSetService := &mockRRSetService{}
|
||||||
|
mockZoneManagementService := &mockZoneManagementService{}
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{
|
||||||
|
Client: (*rc0.Client)(&mockRcodeZeroClient{
|
||||||
|
Zones: mockZoneManagementService,
|
||||||
|
RRSet: mockRRSetService,
|
||||||
|
}),
|
||||||
|
DomainFilter: NewDomainFilter([]string{testZoneOne}),
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := mockRRSetChanges(rrsetChangesUnsupportedChangeType)
|
||||||
|
|
||||||
|
err := provider.submitChanges(changes)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected to fail, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockRRSetChanges(condition int) []*rc0.RRSetChange {
|
||||||
|
|
||||||
|
switch condition {
|
||||||
|
case rrsetChangesUnsupportedChangeType:
|
||||||
|
return []*rc0.RRSetChange{
|
||||||
|
{
|
||||||
|
Name: testZoneOne,
|
||||||
|
Type: "A",
|
||||||
|
ChangeType: "UNSUPPORTED",
|
||||||
|
Records: []*rc0.Record{{Content:"fail"}},
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockChanges() *plan.Changes {
|
||||||
|
|
||||||
|
changes := &plan.Changes{}
|
||||||
|
|
||||||
|
changes.Create = []*endpoint.Endpoint{
|
||||||
|
{DNSName: "new.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A"},
|
||||||
|
{DNSName: "new.ext-dns-test-with-ttl."+testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A", RecordTTL: 100},
|
||||||
|
{DNSName: "new.ext-dns-test.unexpected.com", Targets: endpoint.Targets{"target"}, RecordType: "AAAA"},
|
||||||
|
{DNSName: testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "CNAME"},
|
||||||
|
}
|
||||||
|
changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target"}}}
|
||||||
|
changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target-old"}}}
|
||||||
|
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 100}}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRcodeZeroProvider_Zones(t *testing.T) {
|
||||||
|
|
||||||
|
mockRRSetService := &mockRRSetService{}
|
||||||
|
mockZoneManagementService := &mockZoneManagementService{}
|
||||||
|
|
||||||
|
provider := &RcodeZeroProvider{
|
||||||
|
Client: (*rc0.Client)(&mockRcodeZeroClient{
|
||||||
|
Zones: mockZoneManagementService,
|
||||||
|
RRSet: mockRRSetService,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
mockZoneManagementService.TestNilZonesReturned = true
|
||||||
|
|
||||||
|
zones, err := provider.Zones()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
require.Equal(t, 0, len(zones))
|
||||||
|
mockZoneManagementService.resetTestConditions()
|
||||||
|
|
||||||
|
mockZoneManagementService.TestErrorReturned = true
|
||||||
|
|
||||||
|
zones, err = provider.Zones()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected to fail, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRcodeZeroProvider(t *testing.T) {
|
||||||
|
|
||||||
|
_ = os.Setenv("RC0_API_KEY", "123")
|
||||||
|
p, err := NewRcodeZeroProvider(NewDomainFilter([]string{"ext-dns-test."+testZoneOne+"."}), true, true)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not fail, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, true, p.DryRun)
|
||||||
|
require.Equal(t, true, p.TXTEncrypt)
|
||||||
|
require.Equal(t, true, p.DomainFilter.IsConfigured())
|
||||||
|
require.Equal(t, false, p.DomainFilter.Match("ext-dns-test."+testZoneTwo+".")) // filter is set, so it should match only provided domains
|
||||||
|
|
||||||
|
p, err = NewRcodeZeroProvider(DomainFilter{}, false, false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not fail, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, false, p.DryRun)
|
||||||
|
require.Equal(t, false, p.DomainFilter.IsConfigured())
|
||||||
|
require.Equal(t, true, p.DomainFilter.Match("ext-dns-test."+testZoneOne+".")) // filter is not set, so it should match any
|
||||||
|
|
||||||
|
_ = os.Unsetenv("RC0_API_KEY")
|
||||||
|
_, err = NewRcodeZeroProvider(DomainFilter{}, false, false)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mocking mockRRSetServiceInterface */
|
||||||
|
|
||||||
|
func (m *mockRRSetService) List(zone string, options *rc0.ListOptions) ([]*rc0.RRType, *rc0.Page, error) {
|
||||||
|
|
||||||
|
if m.TestErrorReturned {
|
||||||
|
return nil, nil, fmt.Errorf("operation RRSet.List failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return mockRRSet(zone), nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockRRSet(zone string) []*rc0.RRType {
|
||||||
|
return []*rc0.RRType{
|
||||||
|
{
|
||||||
|
Name: "app."+zone+".",
|
||||||
|
Type: "TXT",
|
||||||
|
TTL: 300,
|
||||||
|
Records: []*rc0.Record{
|
||||||
|
{
|
||||||
|
Content: "\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/default/app\"",
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "app."+zone+".",
|
||||||
|
Type: "A",
|
||||||
|
TTL: 300,
|
||||||
|
Records: []*rc0.Record{
|
||||||
|
{
|
||||||
|
Content: "127.0.0.1",
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "www."+zone+".",
|
||||||
|
Type: "A",
|
||||||
|
TTL: 300,
|
||||||
|
Records: []*rc0.Record{
|
||||||
|
{
|
||||||
|
Content: "127.0.0.1",
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: zone+".",
|
||||||
|
Type: "SOA",
|
||||||
|
TTL: 3600,
|
||||||
|
Records: []*rc0.Record{
|
||||||
|
{
|
||||||
|
Content: "sec1.rcode0.net. rcodezero-soa.ipcom.at. 2019011616 10800 3600 604800 3600",
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: zone+".",
|
||||||
|
Type: "NS",
|
||||||
|
TTL: 3600,
|
||||||
|
Records: []*rc0.Record{
|
||||||
|
{
|
||||||
|
Content: "sec2.rcode0.net.",
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Content: "sec1.rcode0.net.",
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockRRSetService) Create(zone string, rrsetCreate []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
|
||||||
|
|
||||||
|
return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
func (m *mockRRSetService) Edit(zone string, rrsetEdit []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
|
||||||
|
|
||||||
|
return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
|
||||||
|
}
|
||||||
|
func (m *mockRRSetService) Delete(zone string, rrsetDelete []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
|
||||||
|
|
||||||
|
return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
|
||||||
|
}
|
||||||
|
func (m *mockRRSetService) SubmitChangeSet(zone string, changeSet []*rc0.RRSetChange) (*rc0.StatusResponse, error) {
|
||||||
|
|
||||||
|
return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockRRSetService) EncryptTXT(key []byte, rrType *rc0.RRSetChange) {}
|
||||||
|
|
||||||
|
func (m *mockRRSetService) DecryptTXT(key []byte, rrType *rc0.RRType) {}
|
||||||
|
|
||||||
|
/* mocking ZoneManagementServiceInterface */
|
||||||
|
|
||||||
|
func (m *mockZoneManagementService) List(options *rc0.ListOptions) ([]*rc0.Zone, *rc0.Page, error) {
|
||||||
|
|
||||||
|
if m.TestNilZonesReturned {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.TestErrorReturned {
|
||||||
|
return nil, nil, fmt.Errorf("operation Zone.List failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
zones := []*rc0.Zone{
|
||||||
|
{
|
||||||
|
Domain: testZoneOne,
|
||||||
|
Type: "SLAVE",
|
||||||
|
// "dnssec": "yes", @todo: add this
|
||||||
|
// "created": "2018-04-09T09:27:31Z", @todo: add this
|
||||||
|
LastCheck: "",
|
||||||
|
Serial: 20180411,
|
||||||
|
Masters: []string{
|
||||||
|
"193.0.2.2",
|
||||||
|
"2001:db8::2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: testZoneTwo,
|
||||||
|
Type: "MASTER",
|
||||||
|
// "dnssec": "no", @todo: add this
|
||||||
|
// "created": "2019-01-15T13:20:10Z", @todo: add this
|
||||||
|
LastCheck: "",
|
||||||
|
Serial: 2019011616,
|
||||||
|
Masters: []string{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return zones, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockZoneManagementService) Get(zone string) (*rc0.Zone, error) { return nil, nil }
|
||||||
|
func (m *mockZoneManagementService) Create(zoneCreate *rc0.ZoneCreate) (*rc0.StatusResponse, error) { return nil, nil }
|
||||||
|
func (m *mockZoneManagementService) Edit(zone string, zoneEdit *rc0.ZoneEdit) (*rc0.StatusResponse, error) { return nil, nil }
|
||||||
|
func (m *mockZoneManagementService) Delete(zone string) (*rc0.StatusResponse, error) { return nil, nil }
|
||||||
|
func (m *mockZoneManagementService) Transfer(zone string) (*rc0.StatusResponse, error) { return nil ,nil }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user