mirror of
				https://github.com/kubernetes-sigs/external-dns.git
				synced 2025-11-04 04:31:00 +01:00 
			
		
		
		
	Merge branch 'master' into fix-alibaba-domain-filter
This commit is contained in:
		
						commit
						29ce78decd
					
				
							
								
								
									
										7
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@ -6,9 +6,16 @@ on:
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read  #  to fetch code (actions/checkout)
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  build:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read  #  to fetch code (actions/checkout)
 | 
			
		||||
      checks: write  #  to create a new check based on the results (shogo82148/actions-goveralls)
 | 
			
		||||
 | 
			
		||||
    name: Build
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							@ -5,8 +5,12 @@ on:
 | 
			
		||||
    tags:
 | 
			
		||||
      - "v*"
 | 
			
		||||
 | 
			
		||||
permissions: {}
 | 
			
		||||
jobs:
 | 
			
		||||
  release_docs:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  #  for mike to push
 | 
			
		||||
 | 
			
		||||
    name: Release Docs
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/lint-test-chart.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lint-test-chart.yaml
									
									
									
									
										vendored
									
									
								
							@ -38,7 +38,7 @@ jobs:
 | 
			
		||||
          python-version: "3.x"
 | 
			
		||||
 | 
			
		||||
      - name: Set-up chart-testing
 | 
			
		||||
        uses: helm/chart-testing-action@dae259e86a35ff09145c0805e2d7dd3f7207064a
 | 
			
		||||
        uses: helm/chart-testing-action@afea100a513515fbd68b0e72a7bb0ae34cb62aec
 | 
			
		||||
 | 
			
		||||
      - name: Run chart-testing (list-changed)
 | 
			
		||||
        id: list-changed
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/workflows/release-chart.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/release-chart.yaml
									
									
									
									
										vendored
									
									
								
							@ -7,8 +7,13 @@ on:
 | 
			
		||||
    paths:
 | 
			
		||||
      - "charts/external-dns/Chart.yaml"
 | 
			
		||||
 | 
			
		||||
permissions: {}
 | 
			
		||||
jobs:
 | 
			
		||||
  release:
 | 
			
		||||
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  #  to push chart release and create a release (helm/chart-releaser-action)
 | 
			
		||||
 | 
			
		||||
    if: github.repository == 'kubernetes-sigs/external-dns'
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    defaults:
 | 
			
		||||
@ -53,7 +58,7 @@ jobs:
 | 
			
		||||
          version: latest
 | 
			
		||||
 | 
			
		||||
      - name: Run chart-releaser
 | 
			
		||||
        uses: helm/chart-releaser-action@a3454e46a6f5ac4811069a381e646961dda2e1bf
 | 
			
		||||
        uses: helm/chart-releaser-action@98bccfd32b0f76149d188912ac8e45ddd3f8695f
 | 
			
		||||
        env:
 | 
			
		||||
          CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
 | 
			
		||||
          CR_RELEASE_NAME_TEMPLATE: "external-dns-helm-chart-{{ .Version }}"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/trivy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/trivy.yml
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,10 @@
 | 
			
		||||
name: trivy vulnerability scanner
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read  #  to fetch code (actions/checkout)
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    name: Build
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,7 @@ ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchroniz
 | 
			
		||||
* [AWS Cloud Map](https://docs.aws.amazon.com/cloud-map/)
 | 
			
		||||
* [AzureDNS](https://azure.microsoft.com/en-us/services/dns)
 | 
			
		||||
* [BlueCat](https://bluecatnetworks.com)
 | 
			
		||||
* [Civo](https://www.civo.com)
 | 
			
		||||
* [CloudFlare](https://www.cloudflare.com/dns)
 | 
			
		||||
* [RcodeZero](https://www.rcodezero.at/)
 | 
			
		||||
* [DigitalOcean](https://www.digitalocean.com/products/networking)
 | 
			
		||||
@ -101,6 +102,7 @@ The following table clarifies the current status of the providers according to t
 | 
			
		||||
| Akamai Edge DNS | Beta | |
 | 
			
		||||
| AzureDNS | Beta | |
 | 
			
		||||
| BlueCat | Alpha | @seanmalloy  @vinny-sabatini |
 | 
			
		||||
| Civo | Alpha | @alejandrojnm |
 | 
			
		||||
| CloudFlare | Beta | |
 | 
			
		||||
| RcodeZero | Alpha | |
 | 
			
		||||
| DigitalOcean | Alpha | |
 | 
			
		||||
@ -160,6 +162,7 @@ The following tutorials are provided:
 | 
			
		||||
	* [Kube Ingress AWS Controller](docs/tutorials/kube-ingress-aws.md)
 | 
			
		||||
* [Azure DNS](docs/tutorials/azure.md)
 | 
			
		||||
* [Azure Private DNS](docs/tutorials/azure-private-dns.md)
 | 
			
		||||
* [Civo](docs/tutorials/civo.md)
 | 
			
		||||
* [Cloudflare](docs/tutorials/cloudflare.md)
 | 
			
		||||
* [BlueCat](docs/tutorials/bluecat.md)
 | 
			
		||||
* [CoreDNS](docs/tutorials/coredns.md)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										187
									
								
								docs/tutorials/civo.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								docs/tutorials/civo.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,187 @@
 | 
			
		||||
# Setting up ExternalDNS for Services on Civo
 | 
			
		||||
 | 
			
		||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Civo DNS Manager.
 | 
			
		||||
 | 
			
		||||
Make sure to use **>0.12.2** version of ExternalDNS for this tutorial.
 | 
			
		||||
 | 
			
		||||
## Managing DNS with Civo
 | 
			
		||||
 | 
			
		||||
If you want to learn about how to use Civo DNS Manager read the following tutorials:
 | 
			
		||||
 | 
			
		||||
[An Introduction to Managing DNS](https://www.civo.com/learn/configure-dns)
 | 
			
		||||
 | 
			
		||||
## Get Civo Token
 | 
			
		||||
 | 
			
		||||
Copy the token in the settings fo your account
 | 
			
		||||
 | 
			
		||||
The environment variable `CIVO_TOKEN` will be needed to run ExternalDNS with Civo.
 | 
			
		||||
 | 
			
		||||
## 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: apps/v1
 | 
			
		||||
kind: Deployment
 | 
			
		||||
metadata:
 | 
			
		||||
  name: external-dns
 | 
			
		||||
spec:
 | 
			
		||||
  strategy:
 | 
			
		||||
    type: Recreate
 | 
			
		||||
  selector:
 | 
			
		||||
    matchLabels:
 | 
			
		||||
      app: external-dns
 | 
			
		||||
  template:
 | 
			
		||||
    metadata:
 | 
			
		||||
      labels:
 | 
			
		||||
        app: external-dns
 | 
			
		||||
    spec:
 | 
			
		||||
      containers:
 | 
			
		||||
      - name: external-dns
 | 
			
		||||
        image: registry.k8s.io/external-dns/external-dns:v0.12.2
 | 
			
		||||
        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=civo
 | 
			
		||||
        env:
 | 
			
		||||
        - name: CIVO_TOKEN
 | 
			
		||||
          value: "YOUR_CIVO_API_TOKEN"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 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","endpoints","pods"]
 | 
			
		||||
  verbs: ["get","watch","list"]
 | 
			
		||||
- apiGroups: ["extensions","networking.k8s.io"]
 | 
			
		||||
  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: apps/v1
 | 
			
		||||
kind: Deployment
 | 
			
		||||
metadata:
 | 
			
		||||
  name: external-dns
 | 
			
		||||
spec:
 | 
			
		||||
  strategy:
 | 
			
		||||
    type: Recreate
 | 
			
		||||
  selector:
 | 
			
		||||
    matchLabels:
 | 
			
		||||
      app: external-dns
 | 
			
		||||
  template:
 | 
			
		||||
    metadata:
 | 
			
		||||
      labels:
 | 
			
		||||
        app: external-dns
 | 
			
		||||
    spec:
 | 
			
		||||
      serviceAccountName: external-dns
 | 
			
		||||
      containers:
 | 
			
		||||
      - name: external-dns
 | 
			
		||||
        image: registry.k8s.io/external-dns/external-dns:v0.12.2
 | 
			
		||||
        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=civo
 | 
			
		||||
        env:
 | 
			
		||||
        - name: CIVO_TOKEN
 | 
			
		||||
          value: "YOUR_CIVO_API_TOKEN"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Deploying an Nginx Service
 | 
			
		||||
 | 
			
		||||
Create a service file called 'nginx.yaml' with the following contents:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: apps/v1
 | 
			
		||||
kind: Deployment
 | 
			
		||||
metadata:
 | 
			
		||||
  name: nginx
 | 
			
		||||
spec:
 | 
			
		||||
  selector:
 | 
			
		||||
    matchLabels:
 | 
			
		||||
      app: nginx
 | 
			
		||||
  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: my-app.example.com
 | 
			
		||||
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 Civo DNS zone created above.
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ 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 Civo DNS records.
 | 
			
		||||
 | 
			
		||||
## Verifying Civo DNS records
 | 
			
		||||
 | 
			
		||||
Check your [Civo UI](https://www.civo.com/account/dns) to view the records for your Civo DNS zone.
 | 
			
		||||
 | 
			
		||||
Click on 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 Civo DNS records, we can delete the tutorial's example:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ kubectl delete service -f nginx.yaml
 | 
			
		||||
$ kubectl delete service -f externaldns.yaml
 | 
			
		||||
```
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
# Setting up ExternalDNS for Tencent Cloud
 | 
			
		||||
 | 
			
		||||
## External Dns Version
 | 
			
		||||
* Make sure to use **>=1.7.2** version of ExternalDNS for this tutorial
 | 
			
		||||
* Make sure to use **>=0.13.1** version of ExternalDNS for this tutorial
 | 
			
		||||
 | 
			
		||||
## Set up PrivateDns or DNSPod
 | 
			
		||||
 | 
			
		||||
@ -101,7 +101,8 @@ data:
 | 
			
		||||
      "regionId": "ap-shanghai",
 | 
			
		||||
      "secretId": "******",  
 | 
			
		||||
      "secretKey": "******",
 | 
			
		||||
      "vpcId": "vpc-******"
 | 
			
		||||
      "vpcId": "vpc-******",
 | 
			
		||||
      "internetEndpoint": false  # Default: false. Access the Tencent API through the intranet. If you need to deploy on the public network, you need to change to true
 | 
			
		||||
    }
 | 
			
		||||
---
 | 
			
		||||
apiVersion: apps/v1
 | 
			
		||||
@ -128,7 +129,7 @@ spec:
 | 
			
		||||
        - --policy=sync # set `upsert-only` would prevent ExternalDNS from deleting any records
 | 
			
		||||
        - --tencent-cloud-zone-type=private # only look at private hosted zones. set `public` to use the public dns service.
 | 
			
		||||
        - --tencent-cloud-config-file=/etc/kubernetes/tencent-cloud.json
 | 
			
		||||
        image: k8s.gcr.io/external-dns/external-dns:v1.7.2
 | 
			
		||||
        image: registry.k8s.io/external-dns/external-dns:v0.13.1
 | 
			
		||||
        imagePullPolicy: Always
 | 
			
		||||
        name: external-dns
 | 
			
		||||
        resources: {}
 | 
			
		||||
@ -139,6 +140,11 @@ spec:
 | 
			
		||||
          name: config-volume
 | 
			
		||||
          readOnly: true
 | 
			
		||||
      dnsPolicy: ClusterFirst
 | 
			
		||||
      hostAliases:
 | 
			
		||||
      - hostnames:
 | 
			
		||||
        - privatedns.internal.tencentcloudapi.com
 | 
			
		||||
        - dnspod.internal.tencentcloudapi.com
 | 
			
		||||
        ip: 169.254.0.95
 | 
			
		||||
      restartPolicy: Always
 | 
			
		||||
      schedulerName: default-scheduler
 | 
			
		||||
      securityContext: {}
 | 
			
		||||
 | 
			
		||||
@ -73,9 +73,11 @@ type DomainFilter struct {
 | 
			
		||||
 | 
			
		||||
// prepareFilters provides consistent trimming for filters/exclude params
 | 
			
		||||
func prepareFilters(filters []string) []string {
 | 
			
		||||
	fs := make([]string, len(filters))
 | 
			
		||||
	for i, domain := range filters {
 | 
			
		||||
		fs[i] = strings.ToLower(strings.TrimSuffix(strings.TrimSpace(domain), "."))
 | 
			
		||||
	var fs []string
 | 
			
		||||
	for _, filter := range filters {
 | 
			
		||||
		if domain := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(filter), ".")); domain != "" {
 | 
			
		||||
			fs = append(fs, domain)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fs
 | 
			
		||||
}
 | 
			
		||||
@ -98,7 +100,7 @@ func NewRegexDomainFilter(regexDomainFilter *regexp.Regexp, regexDomainExclusion
 | 
			
		||||
// Match checks whether a domain can be found in the DomainFilter.
 | 
			
		||||
// RegexFilter takes precedence over Filters
 | 
			
		||||
func (df DomainFilter) Match(domain string) bool {
 | 
			
		||||
	if df.regex != nil && df.regex.String() != "" {
 | 
			
		||||
	if df.regex != nil && df.regex.String() != "" || df.regexExclusion != nil && df.regexExclusion.String() != "" {
 | 
			
		||||
		return matchRegex(df.regex, df.regexExclusion, domain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -113,12 +115,13 @@ func matchFilter(filters []string, domain string, emptyval bool) bool {
 | 
			
		||||
		return emptyval
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, filter := range filters {
 | 
			
		||||
	strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
 | 
			
		||||
 | 
			
		||||
	for _, filter := range filters {
 | 
			
		||||
		if filter == "" {
 | 
			
		||||
			return emptyval
 | 
			
		||||
		} else if strings.HasPrefix(filter, ".") && strings.HasSuffix(strippedDomain, filter) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(filter, ".") && strings.HasSuffix(strippedDomain, filter) {
 | 
			
		||||
			return true
 | 
			
		||||
		} else if strings.Count(strippedDomain, ".") == strings.Count(filter, ".") {
 | 
			
		||||
			if strippedDomain == filter {
 | 
			
		||||
@ -146,35 +149,32 @@ func matchRegex(regex *regexp.Regexp, negativeRegex *regexp.Regexp, domain strin
 | 
			
		||||
 | 
			
		||||
// MatchParent checks wether DomainFilter matches a given parent domain.
 | 
			
		||||
func (df DomainFilter) MatchParent(domain string) bool {
 | 
			
		||||
	if !df.IsConfigured() {
 | 
			
		||||
		return true
 | 
			
		||||
	if matchFilter(df.exclude, domain, false) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, filter := range df.Filters {
 | 
			
		||||
		if strings.HasPrefix(filter, ".") {
 | 
			
		||||
			// We don't check parents if the filter is prefixed with "."
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if filter == "" {
 | 
			
		||||
	if len(df.Filters) == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
 | 
			
		||||
		if strings.HasSuffix(filter, "."+strippedDomain) && !matchFilter(df.exclude, domain, false) {
 | 
			
		||||
	for _, filter := range df.Filters {
 | 
			
		||||
		if filter == "" || strings.HasPrefix(filter, ".") {
 | 
			
		||||
			// We don't check parents if the filter is prefixed with "."
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if strings.HasSuffix(filter, "."+strippedDomain) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsConfigured returns true if DomainFilter is configured, false otherwise
 | 
			
		||||
// IsConfigured returns true if any inclusion or exclusion rules have been specified.
 | 
			
		||||
func (df DomainFilter) IsConfigured() bool {
 | 
			
		||||
	if df.regex != nil && df.regex.String() != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	} else if len(df.Filters) == 1 {
 | 
			
		||||
		return df.Filters[0] != ""
 | 
			
		||||
	} else if df.regexExclusion != nil && df.regexExclusion.String() != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return len(df.Filters) > 0
 | 
			
		||||
	return len(df.Filters) > 0 || len(df.exclude) > 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -379,15 +379,15 @@ func TestPrepareFiltersStripsWhitespaceAndDotSuffix(t *testing.T) {
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			[]string{},
 | 
			
		||||
			[]string{},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			[]string{""},
 | 
			
		||||
			[]string{""},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			[]string{" ", "   ", ""},
 | 
			
		||||
			[]string{"", "", ""},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			[]string{"  foo   ", "  bar. ", "baz."},
 | 
			
		||||
@ -429,7 +429,7 @@ func TestDomainFilterIsConfigured(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			[]string{"", ""},
 | 
			
		||||
			[]string{""},
 | 
			
		||||
			true,
 | 
			
		||||
			false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			[]string{" . "},
 | 
			
		||||
@ -446,6 +446,11 @@ func TestDomainFilterIsConfigured(t *testing.T) {
 | 
			
		||||
			[]string{"  thisdoesntmatter.com "},
 | 
			
		||||
			true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			[]string{""},
 | 
			
		||||
			[]string{"  thisdoesntmatter.com "},
 | 
			
		||||
			true,
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		t.Run("test IsConfigured", func(t *testing.T) {
 | 
			
		||||
			df := NewDomainFilterWithExclusions(tt.filters, tt.exclude)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								go.mod
									
									
									
									
									
								
							@ -8,16 +8,17 @@ require (
 | 
			
		||||
	github.com/Azure/go-autorest/autorest v0.11.27
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/adal v0.9.20
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/to v0.4.0
 | 
			
		||||
	github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.11.0
 | 
			
		||||
	github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0
 | 
			
		||||
	github.com/IBM/go-sdk-core/v5 v5.8.0
 | 
			
		||||
	github.com/IBM/networking-go-sdk v0.32.0
 | 
			
		||||
	github.com/StackExchange/dnscontrol v0.2.8
 | 
			
		||||
	github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1
 | 
			
		||||
	github.com/alecthomas/kingpin v2.2.5+incompatible
 | 
			
		||||
	github.com/aliyun/alibaba-cloud-sdk-go v1.61.1742
 | 
			
		||||
	github.com/ans-group/sdk-go v1.8.1
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.44.81
 | 
			
		||||
	github.com/ans-group/sdk-go v1.10.4
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.44.119
 | 
			
		||||
	github.com/bodgit/tsig v1.2.0
 | 
			
		||||
	github.com/civo/civogo v0.3.14
 | 
			
		||||
	github.com/cloudflare/cloudflare-go v0.50.0
 | 
			
		||||
	github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
 | 
			
		||||
	github.com/datawire/ambassador v1.6.0
 | 
			
		||||
@ -60,7 +61,7 @@ require (
 | 
			
		||||
	go.etcd.io/etcd/api/v3 v3.5.4
 | 
			
		||||
	go.etcd.io/etcd/client/v3 v3.5.2
 | 
			
		||||
	go.uber.org/ratelimit v0.2.0
 | 
			
		||||
	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
 | 
			
		||||
	golang.org/x/net v0.1.0
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
 | 
			
		||||
	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
 | 
			
		||||
	google.golang.org/api v0.93.0
 | 
			
		||||
@ -128,7 +129,7 @@ require (
 | 
			
		||||
	github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.2 // indirect
 | 
			
		||||
	github.com/imdario/mergo v0.3.12 // indirect
 | 
			
		||||
	github.com/imdario/mergo v0.3.13 // indirect
 | 
			
		||||
	github.com/jcmturner/aescts/v2 v2.0.0 // indirect
 | 
			
		||||
	github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
 | 
			
		||||
	github.com/jcmturner/gofork v1.0.0 // indirect
 | 
			
		||||
@ -173,9 +174,9 @@ require (
 | 
			
		||||
	go.uber.org/zap v1.19.1 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
 | 
			
		||||
	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	golang.org/x/sys v0.1.0 // indirect
 | 
			
		||||
	golang.org/x/term v0.1.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.4.0 // indirect
 | 
			
		||||
	golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
 | 
			
		||||
	golang.org/x/tools v0.1.12 // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								go.sum
									
									
									
									
									
								
							@ -116,8 +116,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 | 
			
		||||
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 | 
			
		||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 | 
			
		||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 | 
			
		||||
github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.11.0 h1:75KEvjN+5lXcFzvW7RBoZY1YALJSNctcXFEfUyFz5Vo=
 | 
			
		||||
github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.11.0/go.mod h1:+GAqrO/rFsYnhzTIxYLXCHxHVZyrtzBLyKjV6hi73YQ=
 | 
			
		||||
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0 h1:2gzVSELk4I4ncZNrsaKI6fvZ3to60iYnig+lTFcGCEM=
 | 
			
		||||
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0/go.mod h1:P9YNyJaJazc49fLNFG4uQ61VZVptykWqNU2vWLWcxu0=
 | 
			
		||||
github.com/IBM/go-sdk-core/v5 v5.8.0 h1:Bn9BxTaKYKWpd+BDpVsL6XOOJl4QDgxux4gSdWi31vE=
 | 
			
		||||
github.com/IBM/go-sdk-core/v5 v5.8.0/go.mod h1:+YbdhrjCHC84ls4MeBp+Hj4NZCni+tDAc0XQUqRO9Jc=
 | 
			
		||||
github.com/IBM/networking-go-sdk v0.32.0 h1:QWd7CxC+Wzap+zWFfXMjbqB5LpvrB1KvNtIbKrWIkhA=
 | 
			
		||||
@ -200,8 +200,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
 | 
			
		||||
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
 | 
			
		||||
github.com/ans-group/go-durationstring v1.2.0 h1:UJIuQATkp0t1rBvZsHRwki33YHV9E+Ulro+3NbMB7MM=
 | 
			
		||||
github.com/ans-group/go-durationstring v1.2.0/go.mod h1:QGF9Mdpq9058QXaut8r55QWu6lcHX6i/GvF1PZVkV6o=
 | 
			
		||||
github.com/ans-group/sdk-go v1.8.1 h1:PHPcYHujevWvCMiI7ujXvvrYHf5zpHIcZw7FfJ1rXw0=
 | 
			
		||||
github.com/ans-group/sdk-go v1.8.1/go.mod h1:XSKXEDfKobnDtZoyia5DhJxxaDMcCjr76e1KJ9dU/xc=
 | 
			
		||||
github.com/ans-group/sdk-go v1.10.4 h1:wZzojt99wtVIEHs8zNQzp1Xhqme5tD5NqMM1VLmG6xQ=
 | 
			
		||||
github.com/ans-group/sdk-go v1.10.4/go.mod h1:XSKXEDfKobnDtZoyia5DhJxxaDMcCjr76e1KJ9dU/xc=
 | 
			
		||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 | 
			
		||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
 | 
			
		||||
github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 | 
			
		||||
@ -231,6 +231,8 @@ github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/
 | 
			
		||||
github.com/aws/aws-sdk-go v1.40.14/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.81 h1:C8oBZ+a+ka0qk3Q24MohQIFq0tkbO8IAu5tfpAMKVWE=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.81/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.119 h1:TPkpDsanBMcZaF5wHwpKhjkapRV/b7d2qdC+a+IPbmY=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.119/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 | 
			
		||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 | 
			
		||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
 | 
			
		||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 | 
			
		||||
@ -275,6 +277,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
 | 
			
		||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
 | 
			
		||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 | 
			
		||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
 | 
			
		||||
github.com/civo/civogo v0.3.14 h1:W+o+hFXtEhWyJOmZOm2C5s8OEorSXGP6eyPYOa69NA8=
 | 
			
		||||
github.com/civo/civogo v0.3.14/go.mod h1:SbS06e0JPgIF27r1sLC97gjU1xWmONQeHgzF1hfLpak=
 | 
			
		||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI=
 | 
			
		||||
@ -863,8 +867,9 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
 | 
			
		||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
			
		||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 | 
			
		||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 | 
			
		||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 | 
			
		||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 | 
			
		||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 | 
			
		||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
			
		||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 | 
			
		||||
github.com/infobloxopen/infoblox-go-client/v2 v2.1.2-0.20220407114022-6f4c71443168 h1:EXKtVoP/44ckXpw3v2/vrtMEdKx/PA+YBl+REoV27XQ=
 | 
			
		||||
@ -1116,6 +1121,7 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
 | 
			
		||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 | 
			
		||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
 | 
			
		||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 | 
			
		||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
@ -1128,6 +1134,7 @@ github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7
 | 
			
		||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
 | 
			
		||||
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
 | 
			
		||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 | 
			
		||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
 | 
			
		||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
 | 
			
		||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 | 
			
		||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
			
		||||
@ -1681,6 +1688,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
 | 
			
		||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
 | 
			
		||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
 | 
			
		||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
@ -1848,12 +1857,16 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc
 | 
			
		||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
 | 
			
		||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
 | 
			
		||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
@ -1865,6 +1878,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
			
		||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
			
		||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
 | 
			
		||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
			
		||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
@ -2251,6 +2266,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ spec:
 | 
			
		||||
      serviceAccountName: external-dns
 | 
			
		||||
      containers:
 | 
			
		||||
        - name: external-dns
 | 
			
		||||
          image: k8s.gcr.io/external-dns/external-dns
 | 
			
		||||
          image: registry.k8s.io/external-dns/external-dns
 | 
			
		||||
          args:
 | 
			
		||||
            - --source=service
 | 
			
		||||
            - --source=ingress
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ kind: Kustomization
 | 
			
		||||
 | 
			
		||||
images:
 | 
			
		||||
  - name: registry.k8s.io/external-dns/external-dns
 | 
			
		||||
    newTag: v0.12.2
 | 
			
		||||
    newTag: v0.13.1
 | 
			
		||||
 | 
			
		||||
resources:
 | 
			
		||||
  - ./external-dns-deployment.yaml
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								main.go
									
									
									
									
									
								
							@ -41,6 +41,7 @@ import (
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/awssd"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/azure"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/bluecat"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/civo"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/cloudflare"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/coredns"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider/designate"
 | 
			
		||||
@ -228,6 +229,8 @@ func main() {
 | 
			
		||||
		p, err = vultr.NewVultrProvider(ctx, domainFilter, cfg.DryRun)
 | 
			
		||||
	case "ultradns":
 | 
			
		||||
		p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
 | 
			
		||||
	case "civo":
 | 
			
		||||
		p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
 | 
			
		||||
	case "cloudflare":
 | 
			
		||||
		p, err = cloudflare.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun)
 | 
			
		||||
	case "rcodezero":
 | 
			
		||||
 | 
			
		||||
@ -373,7 +373,7 @@ func (p *AlibabaCloudProvider) records() ([]alidns.Record, error) {
 | 
			
		||||
	log.Infof("Retrieving Alibaba Cloud DNS Domain Records")
 | 
			
		||||
	var results []alidns.Record
 | 
			
		||||
 | 
			
		||||
	if (len(p.domainFilter.Filters) == 1 && p.domainFilter.Filters[0] == "") || len(p.domainFilter.Filters) == 0 {
 | 
			
		||||
	if len(p.domainFilter.Filters) == 0 {
 | 
			
		||||
		domainNames, tmpErr := p.getDomainList()
 | 
			
		||||
		if tmpErr != nil {
 | 
			
		||||
			log.Errorf("AlibabaCloudProvider getDomainList error %v", tmpErr)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										538
									
								
								provider/civo/civo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										538
									
								
								provider/civo/civo.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,538 @@
 | 
			
		||||
/*
 | 
			
		||||
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 civo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/civo/civogo"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"sigs.k8s.io/external-dns/endpoint"
 | 
			
		||||
	"sigs.k8s.io/external-dns/pkg/apis/externaldns"
 | 
			
		||||
	"sigs.k8s.io/external-dns/plan"
 | 
			
		||||
	"sigs.k8s.io/external-dns/provider"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CivoProvider is an implementation of Provider for Civo's DNS.
 | 
			
		||||
type CivoProvider struct {
 | 
			
		||||
	provider.BaseProvider
 | 
			
		||||
	Client       civogo.Client
 | 
			
		||||
	domainFilter endpoint.DomainFilter
 | 
			
		||||
	DryRun       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CivoChanges All API calls calculated from the plan
 | 
			
		||||
type CivoChanges struct {
 | 
			
		||||
	Creates []*CivoChangeCreate
 | 
			
		||||
	Deletes []*CivoChangeDelete
 | 
			
		||||
	Updates []*CivoChangeUpdate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CivoChangeCreate Civo Domain Record Creates
 | 
			
		||||
type CivoChangeCreate struct {
 | 
			
		||||
	Domain  civogo.DNSDomain
 | 
			
		||||
	Options *civogo.DNSRecordConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CivoChangeUpdate Civo Domain Record Updates
 | 
			
		||||
type CivoChangeUpdate struct {
 | 
			
		||||
	Domain       civogo.DNSDomain
 | 
			
		||||
	DomainRecord civogo.DNSRecord
 | 
			
		||||
	Options      civogo.DNSRecordConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CivoChangeDelete Civo Domain Record Deletes
 | 
			
		||||
type CivoChangeDelete struct {
 | 
			
		||||
	Domain       civogo.DNSDomain
 | 
			
		||||
	DomainRecord civogo.DNSRecord
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCivoProvider initializes a new Civo DNS based Provider.
 | 
			
		||||
func NewCivoProvider(domainFilter endpoint.DomainFilter, dryRun bool) (*CivoProvider, error) {
 | 
			
		||||
	token, ok := os.LookupEnv("CIVO_TOKEN")
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("no token found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Declare a default region just for the client is not used for anything else
 | 
			
		||||
	// as the DNS API is global and not region based
 | 
			
		||||
	region := "LON1"
 | 
			
		||||
 | 
			
		||||
	civoClient, err := civogo.NewClient(token, region)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userAgent := &civogo.Component{
 | 
			
		||||
		Name:    "external-dns",
 | 
			
		||||
		Version: externaldns.Version,
 | 
			
		||||
	}
 | 
			
		||||
	civoClient.SetUserAgent(userAgent)
 | 
			
		||||
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client:       *civoClient,
 | 
			
		||||
		domainFilter: domainFilter,
 | 
			
		||||
		DryRun:       dryRun,
 | 
			
		||||
	}
 | 
			
		||||
	return provider, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Zones returns the list of hosted zones.
 | 
			
		||||
func (p *CivoProvider) Zones(ctx context.Context) ([]civogo.DNSDomain, error) {
 | 
			
		||||
	zones, err := p.fetchZones(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return zones, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Records returns the list of records in a given zone.
 | 
			
		||||
func (p *CivoProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
 | 
			
		||||
	zones, err := p.Zones(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var endpoints []*endpoint.Endpoint
 | 
			
		||||
 | 
			
		||||
	for _, zone := range zones {
 | 
			
		||||
		records, err := p.fetchRecords(ctx, zone.ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, r := range records {
 | 
			
		||||
			toUpper := strings.ToUpper(string(r.Type))
 | 
			
		||||
			if provider.SupportedRecordType(toUpper) {
 | 
			
		||||
				name := fmt.Sprintf("%s.%s", r.Name, zone.Name)
 | 
			
		||||
 | 
			
		||||
				// root name is identified by the empty string and should be
 | 
			
		||||
				// translated to zone name for the endpoint entry.
 | 
			
		||||
				if r.Name == "" {
 | 
			
		||||
					name = zone.Name
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, toUpper, endpoint.TTL(r.TTL), r.Value))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return endpoints, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *CivoProvider) fetchRecords(ctx context.Context, domainID string) ([]civogo.DNSRecord, error) {
 | 
			
		||||
	records, err := p.Client.ListDNSRecords(domainID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return records, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *CivoProvider) fetchZones(ctx context.Context) ([]civogo.DNSDomain, error) {
 | 
			
		||||
	var zones []civogo.DNSDomain
 | 
			
		||||
 | 
			
		||||
	allZones, err := p.Client.ListDNSDomains()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, zone := range allZones {
 | 
			
		||||
		if !p.domainFilter.Match(zone.Name) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		zones = append(zones, zone)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return zones, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// submitChanges takes a zone and a collection of Changes and sends them as a single transaction.
 | 
			
		||||
func (p *CivoProvider) submitChanges(ctx context.Context, changes CivoChanges) error {
 | 
			
		||||
	for _, change := range changes.Creates {
 | 
			
		||||
		logFields := log.Fields{
 | 
			
		||||
			"Type":     change.Options.Type,
 | 
			
		||||
			"Name":     change.Options.Name,
 | 
			
		||||
			"Value":    change.Options.Value,
 | 
			
		||||
			"Priority": change.Options.Priority,
 | 
			
		||||
			"TTL":      change.Options.TTL,
 | 
			
		||||
			"action":   "Create",
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.WithFields(logFields).Info("Creating record.")
 | 
			
		||||
 | 
			
		||||
		if p.DryRun {
 | 
			
		||||
			log.WithFields(logFields).Info("Would create record.")
 | 
			
		||||
		} else if _, err := p.Client.CreateDNSRecord(change.Domain.ID, change.Options); err != nil {
 | 
			
		||||
			log.WithFields(logFields).Errorf(
 | 
			
		||||
				"Failed to Create record: %v",
 | 
			
		||||
				err,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, change := range changes.Deletes {
 | 
			
		||||
		logFields := log.Fields{
 | 
			
		||||
			"Type":     change.DomainRecord.Type,
 | 
			
		||||
			"Name":     change.DomainRecord.Name,
 | 
			
		||||
			"Value":    change.DomainRecord.Value,
 | 
			
		||||
			"Priority": change.DomainRecord.Priority,
 | 
			
		||||
			"TTL":      change.DomainRecord.TTL,
 | 
			
		||||
			"action":   "Delete",
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.WithFields(logFields).Info("Deleting record.")
 | 
			
		||||
 | 
			
		||||
		if p.DryRun {
 | 
			
		||||
			log.WithFields(logFields).Info("Would delete record.")
 | 
			
		||||
		} else if _, err := p.Client.DeleteDNSRecord(&change.DomainRecord); err != nil {
 | 
			
		||||
			log.WithFields(logFields).Errorf(
 | 
			
		||||
				"Failed to Delete record: %v",
 | 
			
		||||
				err,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, change := range changes.Updates {
 | 
			
		||||
		logFields := log.Fields{
 | 
			
		||||
			"Type":     change.DomainRecord.Type,
 | 
			
		||||
			"Name":     change.DomainRecord.Name,
 | 
			
		||||
			"Value":    change.DomainRecord.Value,
 | 
			
		||||
			"Priority": change.DomainRecord.Priority,
 | 
			
		||||
			"TTL":      change.DomainRecord.TTL,
 | 
			
		||||
			"action":   "Update",
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.WithFields(logFields).Info("Updating record.")
 | 
			
		||||
 | 
			
		||||
		if p.DryRun {
 | 
			
		||||
			log.WithFields(logFields).Info("Would update record.")
 | 
			
		||||
		} else if _, err := p.Client.UpdateDNSRecord(&change.DomainRecord, &change.Options); err != nil {
 | 
			
		||||
			log.WithFields(logFields).Errorf(
 | 
			
		||||
				"Failed to Update record: %v",
 | 
			
		||||
				err,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// processCreateActions return a list of changes to create records.
 | 
			
		||||
func processCreateActions(zonesByID map[string]civogo.DNSDomain, recordsByZoneID map[string][]civogo.DNSRecord, createsByZone map[string][]*endpoint.Endpoint, civoChange *CivoChanges) error {
 | 
			
		||||
	for zoneID, creates := range createsByZone {
 | 
			
		||||
		zone := zonesByID[zoneID]
 | 
			
		||||
 | 
			
		||||
		if len(creates) == 0 {
 | 
			
		||||
			log.WithFields(log.Fields{
 | 
			
		||||
				"zoneID":   zoneID,
 | 
			
		||||
				"zoneName": zone.Name,
 | 
			
		||||
			}).Info("Skipping Zone, no creates found.")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		records := recordsByZoneID[zoneID]
 | 
			
		||||
 | 
			
		||||
		// Generate Create
 | 
			
		||||
		for _, ep := range creates {
 | 
			
		||||
			matchedRecords := getRecordID(records, zone, *ep)
 | 
			
		||||
 | 
			
		||||
			if len(matchedRecords) != 0 {
 | 
			
		||||
				log.WithFields(log.Fields{
 | 
			
		||||
					"zoneID":     zoneID,
 | 
			
		||||
					"zoneName":   zone.Name,
 | 
			
		||||
					"dnsName":    ep.DNSName,
 | 
			
		||||
					"recordType": ep.RecordType,
 | 
			
		||||
				}).Warn("Records found which should not exist")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			recordType, err := convertRecordType(ep.RecordType)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, target := range ep.Targets {
 | 
			
		||||
				civoChange.Creates = append(civoChange.Creates, &CivoChangeCreate{
 | 
			
		||||
					Domain: zone,
 | 
			
		||||
					Options: &civogo.DNSRecordConfig{
 | 
			
		||||
						Value:    target,
 | 
			
		||||
						Name:     getStrippedRecordName(zone, *ep),
 | 
			
		||||
						Type:     recordType,
 | 
			
		||||
						Priority: 0,
 | 
			
		||||
						TTL:      int(ep.RecordTTL),
 | 
			
		||||
					},
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// processUpdateActions return a list of changes to update records.
 | 
			
		||||
func processUpdateActions(zonesByID map[string]civogo.DNSDomain, recordsByZoneID map[string][]civogo.DNSRecord, updatesByZone map[string][]*endpoint.Endpoint, civoChange *CivoChanges) error {
 | 
			
		||||
	for zoneID, updates := range updatesByZone {
 | 
			
		||||
		zone := zonesByID[zoneID]
 | 
			
		||||
 | 
			
		||||
		if len(updates) == 0 {
 | 
			
		||||
			log.WithFields(log.Fields{
 | 
			
		||||
				"zoneID":   zoneID,
 | 
			
		||||
				"zoneName": zone.Name,
 | 
			
		||||
			}).Debug("Skipping Zone, no updates found.")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		records := recordsByZoneID[zoneID]
 | 
			
		||||
 | 
			
		||||
		for _, ep := range updates {
 | 
			
		||||
			matchedRecords := getRecordID(records, zone, *ep)
 | 
			
		||||
			if len(matchedRecords) == 0 {
 | 
			
		||||
				log.WithFields(log.Fields{
 | 
			
		||||
					"zoneID":     zoneID,
 | 
			
		||||
					"dnsName":    ep.DNSName,
 | 
			
		||||
					"zoneName":   zone.Name,
 | 
			
		||||
					"recordType": ep.RecordType,
 | 
			
		||||
				}).Warn("Update Records not found.")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			recordType, err := convertRecordType(ep.RecordType)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			matchedRecordsByTarget := make(map[string]civogo.DNSRecord)
 | 
			
		||||
			for _, record := range matchedRecords {
 | 
			
		||||
				matchedRecordsByTarget[record.Value] = record
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, target := range ep.Targets {
 | 
			
		||||
				if record, ok := matchedRecordsByTarget[target]; ok {
 | 
			
		||||
					log.WithFields(log.Fields{
 | 
			
		||||
						"zoneID":     zoneID,
 | 
			
		||||
						"dnsName":    ep.DNSName,
 | 
			
		||||
						"zoneName":   zone.Name,
 | 
			
		||||
						"recordType": ep.RecordType,
 | 
			
		||||
						"target":     target,
 | 
			
		||||
					}).Warn("Updating Existing Target")
 | 
			
		||||
 | 
			
		||||
					civoChange.Updates = append(civoChange.Updates, &CivoChangeUpdate{
 | 
			
		||||
						Domain:       zone,
 | 
			
		||||
						DomainRecord: record,
 | 
			
		||||
						Options: civogo.DNSRecordConfig{
 | 
			
		||||
							Value:    target,
 | 
			
		||||
							Name:     getStrippedRecordName(zone, *ep),
 | 
			
		||||
							Type:     recordType,
 | 
			
		||||
							Priority: 0,
 | 
			
		||||
							TTL:      int(ep.RecordTTL),
 | 
			
		||||
						},
 | 
			
		||||
					})
 | 
			
		||||
 | 
			
		||||
					delete(matchedRecordsByTarget, target)
 | 
			
		||||
				} else {
 | 
			
		||||
					// Record did not previously exist, create new 'target'
 | 
			
		||||
					log.WithFields(log.Fields{
 | 
			
		||||
						"zoneID":     zoneID,
 | 
			
		||||
						"dnsName":    ep.DNSName,
 | 
			
		||||
						"zoneName":   zone.Name,
 | 
			
		||||
						"recordType": ep.RecordType,
 | 
			
		||||
						"target":     target,
 | 
			
		||||
					}).Warn("Creating New Target")
 | 
			
		||||
 | 
			
		||||
					civoChange.Creates = append(civoChange.Creates, &CivoChangeCreate{
 | 
			
		||||
						Domain: zone,
 | 
			
		||||
						Options: &civogo.DNSRecordConfig{
 | 
			
		||||
							Value:    target,
 | 
			
		||||
							Name:     getStrippedRecordName(zone, *ep),
 | 
			
		||||
							Type:     recordType,
 | 
			
		||||
							Priority: 0,
 | 
			
		||||
							TTL:      int(ep.RecordTTL),
 | 
			
		||||
						},
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Any remaining records have been removed, delete them
 | 
			
		||||
			for _, record := range matchedRecordsByTarget {
 | 
			
		||||
				log.WithFields(log.Fields{
 | 
			
		||||
					"zoneID":     zoneID,
 | 
			
		||||
					"dnsName":    ep.DNSName,
 | 
			
		||||
					"recordType": ep.RecordType,
 | 
			
		||||
					"target":     record.Value,
 | 
			
		||||
				}).Warn("Deleting target")
 | 
			
		||||
 | 
			
		||||
				civoChange.Deletes = append(civoChange.Deletes, &CivoChangeDelete{
 | 
			
		||||
					Domain:       zone,
 | 
			
		||||
					DomainRecord: record,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// processDeleteActions return a list of changes to delete records.
 | 
			
		||||
func processDeleteActions(zonesByID map[string]civogo.DNSDomain, recordsByZoneID map[string][]civogo.DNSRecord, deletesByZone map[string][]*endpoint.Endpoint, civoChange *CivoChanges) error {
 | 
			
		||||
	for zoneID, deletes := range deletesByZone {
 | 
			
		||||
		zone := zonesByID[zoneID]
 | 
			
		||||
 | 
			
		||||
		if len(deletes) == 0 {
 | 
			
		||||
			log.WithFields(log.Fields{
 | 
			
		||||
				"zoneID":   zoneID,
 | 
			
		||||
				"zoneName": zone.Name,
 | 
			
		||||
			}).Debug("Skipping Zone, no deletes found.")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		records := recordsByZoneID[zoneID]
 | 
			
		||||
 | 
			
		||||
		for _, ep := range deletes {
 | 
			
		||||
			matchedRecords := getRecordID(records, zone, *ep)
 | 
			
		||||
 | 
			
		||||
			if len(matchedRecords) == 0 {
 | 
			
		||||
				log.WithFields(log.Fields{
 | 
			
		||||
					"zoneID":     zoneID,
 | 
			
		||||
					"dnsName":    ep.DNSName,
 | 
			
		||||
					"zoneName":   zone.Name,
 | 
			
		||||
					"recordType": ep.RecordType,
 | 
			
		||||
				}).Warn("Records to Delete not found.")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, record := range matchedRecords {
 | 
			
		||||
				civoChange.Deletes = append(civoChange.Deletes, &CivoChangeDelete{
 | 
			
		||||
					Domain:       zone,
 | 
			
		||||
					DomainRecord: record,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ApplyChanges applies a given set of changes in a given zone.
 | 
			
		||||
func (p *CivoProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
 | 
			
		||||
	var civoChange CivoChanges
 | 
			
		||||
	recordsByZoneID := make(map[string][]civogo.DNSRecord)
 | 
			
		||||
 | 
			
		||||
	zones, err := p.fetchZones(ctx)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zonesByID := make(map[string]civogo.DNSDomain)
 | 
			
		||||
 | 
			
		||||
	zoneNameIDMapper := provider.ZoneIDName{}
 | 
			
		||||
 | 
			
		||||
	for _, z := range zones {
 | 
			
		||||
		zoneNameIDMapper.Add(z.ID, z.Name)
 | 
			
		||||
		zonesByID[z.ID] = z
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Fetch records for each zone
 | 
			
		||||
	for _, zone := range zones {
 | 
			
		||||
		records, err := p.fetchRecords(ctx, zone.ID)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		recordsByZoneID[zone.ID] = append(recordsByZoneID[zone.ID], records...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createsByZone := endpointsByZone(zoneNameIDMapper, changes.Create)
 | 
			
		||||
	updatesByZone := endpointsByZone(zoneNameIDMapper, changes.UpdateNew)
 | 
			
		||||
	deletesByZone := endpointsByZone(zoneNameIDMapper, changes.Delete)
 | 
			
		||||
 | 
			
		||||
	// Generate Creates
 | 
			
		||||
	err = processCreateActions(zonesByID, recordsByZoneID, createsByZone, &civoChange)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Generate Updates
 | 
			
		||||
	err = processUpdateActions(zonesByID, recordsByZoneID, updatesByZone, &civoChange)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Generate Deletes
 | 
			
		||||
	err = processDeleteActions(zonesByID, recordsByZoneID, deletesByZone, &civoChange)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p.submitChanges(ctx, civoChange)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]*endpoint.Endpoint {
 | 
			
		||||
	endpointsByZone := make(map[string][]*endpoint.Endpoint)
 | 
			
		||||
 | 
			
		||||
	for _, ep := range endpoints {
 | 
			
		||||
		zoneID, _ := zoneNameIDMapper.FindZone(ep.DNSName)
 | 
			
		||||
		if zoneID == "" {
 | 
			
		||||
			log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", ep.DNSName)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		endpointsByZone[zoneID] = append(endpointsByZone[zoneID], ep)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return endpointsByZone
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertRecordType(recordType string) (civogo.DNSRecordType, error) {
 | 
			
		||||
	switch recordType {
 | 
			
		||||
	case "A":
 | 
			
		||||
		return civogo.DNSRecordTypeA, nil
 | 
			
		||||
	case "CNAME":
 | 
			
		||||
		return civogo.DNSRecordTypeCName, nil
 | 
			
		||||
	case "TXT":
 | 
			
		||||
		return civogo.DNSRecordTypeTXT, nil
 | 
			
		||||
	case "SRV":
 | 
			
		||||
		return civogo.DNSRecordTypeSRV, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return "", fmt.Errorf("invalid Record Type: %s", recordType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getStrippedRecordName(zone civogo.DNSDomain, ep endpoint.Endpoint) string {
 | 
			
		||||
	if ep.DNSName == zone.Name {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strings.TrimSuffix(ep.DNSName, "."+zone.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getRecordID(records []civogo.DNSRecord, zone civogo.DNSDomain, ep endpoint.Endpoint) []civogo.DNSRecord {
 | 
			
		||||
	var matchedRecords []civogo.DNSRecord
 | 
			
		||||
 | 
			
		||||
	for _, record := range records {
 | 
			
		||||
		stripedName := getStrippedRecordName(zone, ep)
 | 
			
		||||
		toUpper := strings.ToUpper(string(record.Type))
 | 
			
		||||
		if record.Name == stripedName && toUpper == ep.RecordType {
 | 
			
		||||
			matchedRecords = append(matchedRecords, record)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return matchedRecords
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										874
									
								
								provider/civo/civo_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										874
									
								
								provider/civo/civo_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,874 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2020 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 civo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/civo/civogo"
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"sigs.k8s.io/external-dns/endpoint"
 | 
			
		||||
	"sigs.k8s.io/external-dns/plan"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestNewCivoProvider(t *testing.T) {
 | 
			
		||||
	_ = os.Setenv("CIVO_TOKEN", "xxxxxxxxxxxxxxx")
 | 
			
		||||
	_, err := NewCivoProvider(endpoint.NewDomainFilter([]string{"test.civo.com"}), true)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	_ = os.Unsetenv("CIVO_TOKEN")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNewCivoProviderNoToken(t *testing.T) {
 | 
			
		||||
	_, err := NewCivoProvider(endpoint.NewDomainFilter([]string{"test.civo.com"}), true)
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, "no token found", err.Error())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderZones(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns": `[
 | 
			
		||||
			{"id": "12345", "account_id": "1", "name": "example.com"},
 | 
			
		||||
			{"id": "12346", "account_id": "1", "name": "example.net"}
 | 
			
		||||
			]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected, err := client.ListDNSDomains()
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	zones, err := provider.Zones(context.Background())
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	// Check if the return is a DNSDomain type
 | 
			
		||||
	assert.Equal(t, reflect.TypeOf(zones), reflect.TypeOf(expected))
 | 
			
		||||
	assert.ElementsMatch(t, zones, expected)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderZonesWithError(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns-error": `[]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := provider.Zones(context.Background())
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderRecords(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{
 | 
			
		||||
		{
 | 
			
		||||
			Method: "GET",
 | 
			
		||||
			Value: []civogo.ValueAdvanceClientForTesting{
 | 
			
		||||
				{
 | 
			
		||||
					RequestBody: ``,
 | 
			
		||||
					URL:         "/v2/dns/12345/records",
 | 
			
		||||
					ResponseBody: `[
 | 
			
		||||
						{"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600},
 | 
			
		||||
						{"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600}
 | 
			
		||||
						]`,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					RequestBody: ``,
 | 
			
		||||
					URL:         "/v2/dns",
 | 
			
		||||
					ResponseBody: `[
 | 
			
		||||
						{"id": "12345", "account_id": "1", "name": "example.com"},
 | 
			
		||||
						{"id": "12346", "account_id": "1", "name": "example.net"}
 | 
			
		||||
						]`,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client:       *client,
 | 
			
		||||
		domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected, err := client.ListDNSRecords("12345")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	records, err := provider.Records(context.Background())
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, strings.TrimSuffix(records[0].DNSName, ".example.com"), expected[0].Name)
 | 
			
		||||
	assert.Equal(t, records[0].RecordType, string(expected[0].Type))
 | 
			
		||||
	assert.Equal(t, int(records[0].RecordTTL), expected[0].TTL)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, strings.TrimSuffix(records[1].DNSName, ".example.com"), expected[1].Name)
 | 
			
		||||
	assert.Equal(t, records[1].RecordType, string(expected[1].Type))
 | 
			
		||||
	assert.Equal(t, int(records[1].RecordTTL), expected[1].TTL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderWithoutRecords(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns/12345/records": `[]`,
 | 
			
		||||
		"/v2/dns": `[
 | 
			
		||||
			{"id": "12345", "account_id": "1", "name": "example.com"},
 | 
			
		||||
			{"id": "12346", "account_id": "1", "name": "example.net"}
 | 
			
		||||
			]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client:       *client,
 | 
			
		||||
		domainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	records, err := provider.Records(context.Background())
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(records), 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProcessCreateActions(t *testing.T) {
 | 
			
		||||
	zoneByID := map[string]civogo.DNSDomain{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			ID:        "1",
 | 
			
		||||
			AccountID: "1",
 | 
			
		||||
			Name:      "example.com",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recordsByZoneID := map[string][]civogo.DNSRecord{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "1",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "txt",
 | 
			
		||||
				Value:       "12.12.12.1",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createsByZone := map[string][]*endpoint.Endpoint{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
 | 
			
		||||
			endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var changes CivoChanges
 | 
			
		||||
	err := processCreateActions(zoneByID, recordsByZoneID, createsByZone, &changes)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, 2, len(changes.Creates))
 | 
			
		||||
	assert.Equal(t, 0, len(changes.Updates))
 | 
			
		||||
	assert.Equal(t, 0, len(changes.Deletes))
 | 
			
		||||
 | 
			
		||||
	expectedCreates := []*CivoChangeCreate{
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Options: &civogo.DNSRecordConfig{
 | 
			
		||||
				Type:  "A",
 | 
			
		||||
				Name:  "foo",
 | 
			
		||||
				Value: "1.2.3.4",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Options: &civogo.DNSRecordConfig{
 | 
			
		||||
				Type:  "CNAME",
 | 
			
		||||
				Name:  "txt",
 | 
			
		||||
				Value: "foo.example.com",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !elementsMatch(t, expectedCreates, changes.Creates) {
 | 
			
		||||
		assert.Failf(t, "diff: %s", cmp.Diff(expectedCreates, changes.Creates))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProcessCreateActionsWithError(t *testing.T) {
 | 
			
		||||
	zoneByID := map[string]civogo.DNSDomain{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			ID:        "1",
 | 
			
		||||
			AccountID: "1",
 | 
			
		||||
			Name:      "example.com",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recordsByZoneID := map[string][]civogo.DNSRecord{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "1",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "txt",
 | 
			
		||||
				Value:       "12.12.12.1",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createsByZone := map[string][]*endpoint.Endpoint{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			endpoint.NewEndpoint("foo.example.com", "AAAA", "1.2.3.4"),
 | 
			
		||||
			endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var changes CivoChanges
 | 
			
		||||
	err := processCreateActions(zoneByID, recordsByZoneID, createsByZone, &changes)
 | 
			
		||||
	require.Error(t, err)
 | 
			
		||||
	assert.Equal(t, "invalid Record Type: AAAA", err.Error())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProcessUpdateActions(t *testing.T) {
 | 
			
		||||
	zoneByID := map[string]civogo.DNSDomain{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			ID:        "1",
 | 
			
		||||
			AccountID: "1",
 | 
			
		||||
			Name:      "example.com",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recordsByZoneID := map[string][]civogo.DNSRecord{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "1",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "txt",
 | 
			
		||||
				Value:       "1.2.3.4",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "2",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "foo",
 | 
			
		||||
				Value:       "foo.example.com",
 | 
			
		||||
				Type:        "CNAME",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "3",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "bar",
 | 
			
		||||
				Value:       "10.10.10.1",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updatesByZone := map[string][]*endpoint.Endpoint{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeA, "10.20.30.40"),
 | 
			
		||||
			endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeCNAME, "bar.example.com"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var changes CivoChanges
 | 
			
		||||
	err := processUpdateActions(zoneByID, recordsByZoneID, updatesByZone, &changes)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, 2, len(changes.Creates))
 | 
			
		||||
	assert.Equal(t, 0, len(changes.Updates))
 | 
			
		||||
	assert.Equal(t, 2, len(changes.Deletes))
 | 
			
		||||
 | 
			
		||||
	expectedUpdate := []*CivoChangeCreate{
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Options: &civogo.DNSRecordConfig{
 | 
			
		||||
				Type:  "A",
 | 
			
		||||
				Name:  "txt",
 | 
			
		||||
				Value: "10.20.30.40",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Options: &civogo.DNSRecordConfig{
 | 
			
		||||
				Type:  "CNAME",
 | 
			
		||||
				Name:  "foo",
 | 
			
		||||
				Value: "bar.example.com",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !elementsMatch(t, expectedUpdate, changes.Creates) {
 | 
			
		||||
		assert.Failf(t, "diff: %s", cmp.Diff(expectedUpdate, changes.Creates))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedDelete := []*CivoChangeDelete{
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			DomainRecord: civogo.DNSRecord{
 | 
			
		||||
				ID:          "1",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "txt",
 | 
			
		||||
				Value:       "1.2.3.4",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				Priority:    0,
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			DomainRecord: civogo.DNSRecord{
 | 
			
		||||
				ID:          "2",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "foo",
 | 
			
		||||
				Value:       "foo.example.com",
 | 
			
		||||
				Type:        "CNAME",
 | 
			
		||||
				Priority:    0,
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !elementsMatch(t, expectedDelete, changes.Deletes) {
 | 
			
		||||
		assert.Failf(t, "diff: %s", cmp.Diff(expectedDelete, changes.Deletes))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProcessDeleteAction(t *testing.T) {
 | 
			
		||||
	zoneByID := map[string]civogo.DNSDomain{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			ID:        "1",
 | 
			
		||||
			AccountID: "1",
 | 
			
		||||
			Name:      "example.com",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recordsByZoneID := map[string][]civogo.DNSRecord{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "1",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "txt",
 | 
			
		||||
				Value:       "1.2.3.4",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "2",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "foo",
 | 
			
		||||
				Value:       "5.6.7.8",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ID:          "3",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "bar",
 | 
			
		||||
				Value:       "10.10.10.1",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	deleteByDomain := map[string][]*endpoint.Endpoint{
 | 
			
		||||
		"example.com": {
 | 
			
		||||
			endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeA, "1.2.3.4"),
 | 
			
		||||
			endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "5.6.7.8"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var changes CivoChanges
 | 
			
		||||
	err := processDeleteActions(zoneByID, recordsByZoneID, deleteByDomain, &changes)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, 0, len(changes.Creates))
 | 
			
		||||
	assert.Equal(t, 0, len(changes.Updates))
 | 
			
		||||
	assert.Equal(t, 2, len(changes.Deletes))
 | 
			
		||||
 | 
			
		||||
	expectedDelete := []*CivoChangeDelete{
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			DomainRecord: civogo.DNSRecord{
 | 
			
		||||
				ID:          "1",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Name:        "txt",
 | 
			
		||||
				Value:       "1.2.3.4",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Domain: civogo.DNSDomain{
 | 
			
		||||
				ID:        "1",
 | 
			
		||||
				AccountID: "1",
 | 
			
		||||
				Name:      "example.com",
 | 
			
		||||
			},
 | 
			
		||||
			DomainRecord: civogo.DNSRecord{
 | 
			
		||||
				ID:          "2",
 | 
			
		||||
				AccountID:   "1",
 | 
			
		||||
				DNSDomainID: "1",
 | 
			
		||||
				Type:        "A",
 | 
			
		||||
				Name:        "foo",
 | 
			
		||||
				Value:       "5.6.7.8",
 | 
			
		||||
				TTL:         600,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !elementsMatch(t, expectedDelete, changes.Deletes) {
 | 
			
		||||
		assert.Failf(t, "diff: %s", cmp.Diff(expectedDelete, changes.Deletes))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoApplyChanges(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{
 | 
			
		||||
		{
 | 
			
		||||
			Method: "GET",
 | 
			
		||||
			Value: []civogo.ValueAdvanceClientForTesting{
 | 
			
		||||
				{
 | 
			
		||||
					RequestBody:  "",
 | 
			
		||||
					URL:          "/v2/dns",
 | 
			
		||||
					ResponseBody: `[{"id": "12345", "account_id": "1", "name": "example.com"}]`,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					RequestBody:  "",
 | 
			
		||||
					URL:          "/v2/dns/12345/records",
 | 
			
		||||
					ResponseBody: `[]`,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
 | 
			
		||||
	changes := &plan.Changes{}
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
	}
 | 
			
		||||
	changes.Create = []*endpoint.Endpoint{
 | 
			
		||||
		{DNSName: "new.ext-dns-test.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeA},
 | 
			
		||||
		{DNSName: "new.ext-dns-test-with-ttl.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeA, RecordTTL: 100},
 | 
			
		||||
	}
 | 
			
		||||
	changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"target"}}}
 | 
			
		||||
	changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.example.de", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"target-old"}}}
 | 
			
		||||
	changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.foo.com", Targets: endpoint.Targets{"target-new"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100}}
 | 
			
		||||
	err := provider.ApplyChanges(context.Background(), changes)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderFetchZones(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns": `[
 | 
			
		||||
			{"id": "12345", "account_id": "1", "name": "example.com"},
 | 
			
		||||
			{"id": "12346", "account_id": "1", "name": "example.net"}
 | 
			
		||||
			]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected, err := client.ListDNSDomains()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("should not fail, %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	zones, err := provider.fetchZones(context.Background())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	assert.ElementsMatch(t, zones, expected)
 | 
			
		||||
}
 | 
			
		||||
func TestCivoProviderFetchZonesWithFilter(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns": `[
 | 
			
		||||
			{"id": "12345", "account_id": "1", "name": "example.com"},
 | 
			
		||||
			{"id": "12346", "account_id": "1", "name": "example.net"}
 | 
			
		||||
			]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client:       *client,
 | 
			
		||||
		domainFilter: endpoint.NewDomainFilter([]string{".com"}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []civogo.DNSDomain{
 | 
			
		||||
		{ID: "12345", Name: "example.com", AccountID: "1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	actual, err := provider.fetchZones(context.Background())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	assert.ElementsMatch(t, expected, actual)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderFetchRecords(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns/12345/records": `[
 | 
			
		||||
			{"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600},
 | 
			
		||||
			{"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600}
 | 
			
		||||
			]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected, err := client.ListDNSRecords("12345")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	actual, err := provider.fetchRecords(context.Background(), "12345")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.ElementsMatch(t, expected, actual)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderFetchRecordsWithError(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns/12345/records": `[
 | 
			
		||||
			{"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600},
 | 
			
		||||
			{"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600}
 | 
			
		||||
			]`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := provider.fetchRecords(context.Background(), "235698")
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivo_getStrippedRecordName(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, "", getStrippedRecordName(civogo.DNSDomain{
 | 
			
		||||
		Name: "foo.com",
 | 
			
		||||
	}, endpoint.Endpoint{
 | 
			
		||||
		DNSName: "foo.com",
 | 
			
		||||
	}))
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, "api", getStrippedRecordName(civogo.DNSDomain{
 | 
			
		||||
		Name: "foo.com",
 | 
			
		||||
	}, endpoint.Endpoint{
 | 
			
		||||
		DNSName: "api.foo.com",
 | 
			
		||||
	}))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivo_convertRecordType(t *testing.T) {
 | 
			
		||||
	record, err := convertRecordType("A")
 | 
			
		||||
	recordA := civogo.DNSRecordType(civogo.DNSRecordTypeA)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, recordA, record)
 | 
			
		||||
 | 
			
		||||
	record, err = convertRecordType("CNAME")
 | 
			
		||||
	recordCName := civogo.DNSRecordType(civogo.DNSRecordTypeCName)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, recordCName, record)
 | 
			
		||||
 | 
			
		||||
	record, err = convertRecordType("TXT")
 | 
			
		||||
	recordTXT := civogo.DNSRecordType(civogo.DNSRecordTypeTXT)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, recordTXT, record)
 | 
			
		||||
 | 
			
		||||
	record, err = convertRecordType("SRV")
 | 
			
		||||
	recordSRV := civogo.DNSRecordType(civogo.DNSRecordTypeSRV)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, recordSRV, record)
 | 
			
		||||
 | 
			
		||||
	_, err = convertRecordType("INVALID")
 | 
			
		||||
	require.Error(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, "invalid Record Type: INVALID", err.Error())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivoProviderGetRecordID(t *testing.T) {
 | 
			
		||||
	zone := civogo.DNSDomain{
 | 
			
		||||
		ID:   "12345",
 | 
			
		||||
		Name: "test.com",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	record := []civogo.DNSRecord{{
 | 
			
		||||
		ID:          "1",
 | 
			
		||||
		Type:        "A",
 | 
			
		||||
		Name:        "www",
 | 
			
		||||
		Value:       "10.0.0.0",
 | 
			
		||||
		DNSDomainID: "12345",
 | 
			
		||||
		TTL:         600,
 | 
			
		||||
	}, {
 | 
			
		||||
		ID:          "2",
 | 
			
		||||
		Type:        "A",
 | 
			
		||||
		Name:        "api",
 | 
			
		||||
		Value:       "10.0.0.1",
 | 
			
		||||
		DNSDomainID: "12345",
 | 
			
		||||
		TTL:         600,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	endPoint := endpoint.Endpoint{DNSName: "www.test.com", Targets: endpoint.Targets{"10.0.0.0"}, RecordType: "A"}
 | 
			
		||||
	id := getRecordID(record, zone, endPoint)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, id[0].ID, record[0].ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivo_submitChangesCreate(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{
 | 
			
		||||
		{
 | 
			
		||||
			Method: "POST",
 | 
			
		||||
			Value: []civogo.ValueAdvanceClientForTesting{
 | 
			
		||||
				{
 | 
			
		||||
					RequestBody: `{"type":"MX","name":"mail","value":"10.0.0.1","priority":10,"ttl":600}`,
 | 
			
		||||
					URL:         "/v2/dns/12345/records",
 | 
			
		||||
					ResponseBody: `{
 | 
			
		||||
						"id": "76cc107f-fbef-4e2b-b97f-f5d34f4075d3",
 | 
			
		||||
						"account_id": "1",
 | 
			
		||||
						"domain_id": "12345",
 | 
			
		||||
						"name": "mail",
 | 
			
		||||
						"value": "10.0.0.1",
 | 
			
		||||
						"type": "MX",
 | 
			
		||||
						"priority": 10,
 | 
			
		||||
						"ttl": 600
 | 
			
		||||
					}`,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
		DryRun: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changes := CivoChanges{
 | 
			
		||||
		Creates: []*CivoChangeCreate{
 | 
			
		||||
			{
 | 
			
		||||
				Domain: civogo.DNSDomain{
 | 
			
		||||
					ID:        "12345",
 | 
			
		||||
					AccountID: "1",
 | 
			
		||||
					Name:      "example.com",
 | 
			
		||||
				},
 | 
			
		||||
				Options: &civogo.DNSRecordConfig{
 | 
			
		||||
					Type:     "MX",
 | 
			
		||||
					Name:     "mail",
 | 
			
		||||
					Value:    "10.0.0.1",
 | 
			
		||||
					Priority: 10,
 | 
			
		||||
					TTL:      600,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := provider.submitChanges(context.Background(), changes)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivo_submitChangesUpdate(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{
 | 
			
		||||
		{
 | 
			
		||||
			Method: "PUT",
 | 
			
		||||
			Value: []civogo.ValueAdvanceClientForTesting{
 | 
			
		||||
				{
 | 
			
		||||
					RequestBody: `{"type":"MX","name":"mail","value":"10.0.0.2","priority":10,"ttl":600}`,
 | 
			
		||||
					URL:         "/v2/dns/12345/records/76cc107f-fbef-4e2b-b97f-f5d34f4075d3",
 | 
			
		||||
					ResponseBody: `{
 | 
			
		||||
						"id": "76cc107f-fbef-4e2b-b97f-f5d34f4075d3",
 | 
			
		||||
						"account_id": "1",
 | 
			
		||||
						"domain_id": "12345",
 | 
			
		||||
						"name": "mail",
 | 
			
		||||
						"value": "10.0.0.2",
 | 
			
		||||
						"type": "MX",
 | 
			
		||||
						"priority": 10,
 | 
			
		||||
						"ttl": 600
 | 
			
		||||
					}`,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
		DryRun: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changes := CivoChanges{
 | 
			
		||||
		Updates: []*CivoChangeUpdate{
 | 
			
		||||
			{
 | 
			
		||||
				Domain: civogo.DNSDomain{ID: "12345", AccountID: "1", Name: "example.com"},
 | 
			
		||||
				DomainRecord: civogo.DNSRecord{
 | 
			
		||||
					ID:          "76cc107f-fbef-4e2b-b97f-f5d34f4075d3",
 | 
			
		||||
					AccountID:   "1",
 | 
			
		||||
					DNSDomainID: "12345",
 | 
			
		||||
					Name:        "mail",
 | 
			
		||||
					Value:       "10.0.0.1",
 | 
			
		||||
					Type:        "MX",
 | 
			
		||||
					Priority:    10,
 | 
			
		||||
					TTL:         600,
 | 
			
		||||
				},
 | 
			
		||||
				Options: civogo.DNSRecordConfig{
 | 
			
		||||
					Type:     "MX",
 | 
			
		||||
					Name:     "mail",
 | 
			
		||||
					Value:    "10.0.0.2",
 | 
			
		||||
					Priority: 10,
 | 
			
		||||
					TTL:      600,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := provider.submitChanges(context.Background(), changes)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCivo_submitChangesDelete(t *testing.T) {
 | 
			
		||||
	client, server, _ := civogo.NewClientForTesting(map[string]string{
 | 
			
		||||
		"/v2/dns/12345/records/76cc107f-fbef-4e2b-b97f-f5d34f4075d3": `{"result": "success"}`,
 | 
			
		||||
	})
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
 | 
			
		||||
	provider := &CivoProvider{
 | 
			
		||||
		Client: *client,
 | 
			
		||||
		DryRun: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changes := CivoChanges{
 | 
			
		||||
		Deletes: []*CivoChangeDelete{
 | 
			
		||||
			{
 | 
			
		||||
				Domain: civogo.DNSDomain{ID: "12345", AccountID: "1", Name: "example.com"},
 | 
			
		||||
				DomainRecord: civogo.DNSRecord{
 | 
			
		||||
					ID:          "76cc107f-fbef-4e2b-b97f-f5d34f4075d3",
 | 
			
		||||
					AccountID:   "1",
 | 
			
		||||
					DNSDomainID: "12345",
 | 
			
		||||
					Name:        "mail",
 | 
			
		||||
					Value:       "10.0.0.2",
 | 
			
		||||
					Type:        "MX",
 | 
			
		||||
					Priority:    10,
 | 
			
		||||
					TTL:         600,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := provider.submitChanges(context.Background(), changes)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is an adapted copy of the testify package's ElementsMatch function with the
 | 
			
		||||
// call to ObjectsAreEqual replaced with cmp.Equal which better handles struct's with pointers to
 | 
			
		||||
// other structs. It also ignores ordering when comparing unlike cmp.Equal.
 | 
			
		||||
func elementsMatch(t *testing.T, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
 | 
			
		||||
	if listA == nil && listB == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	} else if listA == nil {
 | 
			
		||||
		return isEmpty(listB)
 | 
			
		||||
	} else if listB == nil {
 | 
			
		||||
		return isEmpty(listA)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aKind := reflect.TypeOf(listA).Kind()
 | 
			
		||||
	bKind := reflect.TypeOf(listB).Kind()
 | 
			
		||||
 | 
			
		||||
	if aKind != reflect.Array && aKind != reflect.Slice {
 | 
			
		||||
		return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bKind != reflect.Array && bKind != reflect.Slice {
 | 
			
		||||
		return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aValue := reflect.ValueOf(listA)
 | 
			
		||||
	bValue := reflect.ValueOf(listB)
 | 
			
		||||
 | 
			
		||||
	aLen := aValue.Len()
 | 
			
		||||
	bLen := bValue.Len()
 | 
			
		||||
 | 
			
		||||
	if aLen != bLen {
 | 
			
		||||
		return assert.Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mark indexes in bValue that we already used
 | 
			
		||||
	visited := make([]bool, bLen)
 | 
			
		||||
	for i := 0; i < aLen; i++ {
 | 
			
		||||
		element := aValue.Index(i).Interface()
 | 
			
		||||
		found := false
 | 
			
		||||
		for j := 0; j < bLen; j++ {
 | 
			
		||||
			if visited[j] {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if cmp.Equal(bValue.Index(j).Interface(), element) {
 | 
			
		||||
				visited[j] = true
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			return assert.Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isEmpty(xs interface{}) bool {
 | 
			
		||||
	if xs != nil {
 | 
			
		||||
		objValue := reflect.ValueOf(xs)
 | 
			
		||||
		return objValue.Len() == 0
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
@ -188,17 +188,13 @@ func (p *GoogleProvider) Zones(ctx context.Context) (map[string]*dns.ManagedZone
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debugf("Matching zones against domain filters: %v", p.domainFilter.Filters)
 | 
			
		||||
	log.Debugf("Matching zones against domain filters: %v", p.domainFilter)
 | 
			
		||||
	if err := p.managedZonesClient.List(p.project).Pages(ctx, f); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(zones) == 0 {
 | 
			
		||||
		if p.domainFilter.IsConfigured() {
 | 
			
		||||
			log.Warnf("No zones in the project, %s, match domain filters: %v", p.project, p.domainFilter.Filters)
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Warnf("No zones found in the project, %s", p.project)
 | 
			
		||||
		}
 | 
			
		||||
		log.Warnf("No zones in the project, %s, match domain filters: %v", p.project, p.domainFilter)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, zone := range zones {
 | 
			
		||||
 | 
			
		||||
@ -218,7 +218,7 @@ func (c *ibmcloudConfig) Validate(authenticator core.Authenticator, domainFilter
 | 
			
		||||
	var service ibmcloudService
 | 
			
		||||
	isPrivate := false
 | 
			
		||||
	log.Debugf("filters: %v, %v", domainFilter.Filters, zoneIDFilter.ZoneIDs)
 | 
			
		||||
	if domainFilter.Filters[0] == "" && zoneIDFilter.ZoneIDs[0] == "" {
 | 
			
		||||
	if (len(domainFilter.Filters) == 0 || domainFilter.Filters[0] == "") && zoneIDFilter.ZoneIDs[0] == "" {
 | 
			
		||||
		return service, isPrivate, fmt.Errorf("at lease one of filters: 'domain-filter', 'zone-id-filter' needed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -253,7 +253,7 @@ func (c *ibmcloudConfig) Validate(authenticator core.Authenticator, domainFilter
 | 
			
		||||
		}
 | 
			
		||||
		for _, zone := range zonesResp.Result {
 | 
			
		||||
			log.Debugf("zoneName: %s, zoneID: %s", *zone.Name, *zone.ID)
 | 
			
		||||
			if len(domainFilter.Filters[0]) != 0 && domainFilter.Match(*zone.Name) {
 | 
			
		||||
			if len(domainFilter.Filters) > 0 && domainFilter.Filters[0] != "" && domainFilter.Match(*zone.Name) {
 | 
			
		||||
				log.Debugf("zone %s found.", *zone.ID)
 | 
			
		||||
				zoneID = *zone.ID
 | 
			
		||||
				break
 | 
			
		||||
 | 
			
		||||
@ -138,11 +138,7 @@ func (p *OCIProvider) zones(ctx context.Context) (map[string]dns.ZoneSummary, er
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(zones) == 0 {
 | 
			
		||||
		if p.domainFilter.IsConfigured() {
 | 
			
		||||
			log.Warnf("No zones in compartment %q match domain filters %v", p.cfg.CompartmentID, p.domainFilter.Filters)
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Warnf("No zones found in compartment %q", p.cfg.CompartmentID)
 | 
			
		||||
		}
 | 
			
		||||
		log.Warnf("No zones in compartment %q match domain filters %v", p.cfg.CompartmentID, p.domainFilter)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return zones, nil
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ import (
 | 
			
		||||
	"sigs.k8s.io/external-dns/endpoint"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This is a compile-time validation that glooSource is a Source.
 | 
			
		||||
// This is a compile-time validation that kongTCPIngressSource is a Source.
 | 
			
		||||
var _ Source = &kongTCPIngressSource{}
 | 
			
		||||
 | 
			
		||||
const defaultKongNamespace = "kong"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user