Merge pull request #5463 from mloiseleur/chore/unmaintained-providers

chore!: remove unmaintained providers
This commit is contained in:
Kubernetes Prow Robot 2025-05-26 09:18:16 -07:00 committed by GitHub
commit ef7e1af8c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 5 additions and 6379 deletions

2
OWNERS
View File

@ -51,6 +51,6 @@ filters:
"provider/cloudflare": "provider/cloudflare":
labels: labels:
- provider/cloudflare - provider/cloudflare
"provider/(akamai|alibabacloud|civo|designate|digitalocean|dnsimple|exoscale|gandi|godaddy|ibmcloud|linode|ns1|oci|ovh|pihole|plural|scaleway|tencentcloud|transip|ultradns)": "provider/(akamai|alibabacloud|civo|designate|digitalocean|dnsimple|exoscale|gandi|godaddy|linode|ns1|oci|ovh|pihole|plural|scaleway|transip)":
labels: labels:
- provider - provider

View File

@ -63,8 +63,6 @@ ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchroniz
- [GoDaddy](https://www.godaddy.com) - [GoDaddy](https://www.godaddy.com)
- [Gandi](https://www.gandi.net) - [Gandi](https://www.gandi.net)
- [IBM Cloud DNS](https://www.ibm.com/cloud/dns) - [IBM Cloud DNS](https://www.ibm.com/cloud/dns)
- [TencentCloud PrivateDNS](https://cloud.tencent.com/product/privatedns)
- [TencentCloud DNSPod](https://cloud.tencent.com/product/cns)
- [Plural](https://www.plural.sh/) - [Plural](https://www.plural.sh/)
- [Pi-hole](https://pi-hole.net/) - [Pi-hole](https://pi-hole.net/)
@ -149,11 +147,8 @@ The following table clarifies the current status of the providers according to t
| TransIP | Alpha | | | TransIP | Alpha | |
| OVHcloud | Beta | @rbeuque74 | | OVHcloud | Beta | @rbeuque74 |
| Scaleway DNS | Alpha | @Sh4d1 | | Scaleway DNS | Alpha | @Sh4d1 |
| UltraDNS | Alpha | |
| GoDaddy | Alpha | | | GoDaddy | Alpha | |
| Gandi | Alpha | @packi | | Gandi | Alpha | @packi |
| IBMCloud | Alpha | @hughhuangzh |
| TencentCloud | Alpha | @Hyzhou |
| Plural | Alpha | @michaeljguarino | | Plural | Alpha | @michaeljguarino |
| Pi-hole | Alpha | @tinyzimmer | | Pi-hole | Alpha | @tinyzimmer |
@ -211,12 +206,9 @@ The following tutorials are provided:
- [TransIP](docs/tutorials/transip.md) - [TransIP](docs/tutorials/transip.md)
- [OVHcloud](docs/tutorials/ovh.md) - [OVHcloud](docs/tutorials/ovh.md)
- [Scaleway](docs/tutorials/scaleway.md) - [Scaleway](docs/tutorials/scaleway.md)
- [UltraDNS](docs/tutorials/ultradns.md)
- [GoDaddy](docs/tutorials/godaddy.md) - [GoDaddy](docs/tutorials/godaddy.md)
- [Gandi](docs/tutorials/gandi.md) - [Gandi](docs/tutorials/gandi.md)
- [IBM Cloud](docs/tutorials/ibmcloud.md)
- [Nodes as source](docs/sources/nodes.md) - [Nodes as source](docs/sources/nodes.md)
- [TencentCloud](docs/tutorials/tencentcloud.md)
- [Plural](docs/tutorials/plural.md) - [Plural](docs/tutorials/plural.md)
- [Pi-hole](docs/tutorials/pihole.md) - [Pi-hole](docs/tutorials/pihole.md)

View File

@ -53,7 +53,6 @@ import (
"sigs.k8s.io/external-dns/provider/gandi" "sigs.k8s.io/external-dns/provider/gandi"
"sigs.k8s.io/external-dns/provider/godaddy" "sigs.k8s.io/external-dns/provider/godaddy"
"sigs.k8s.io/external-dns/provider/google" "sigs.k8s.io/external-dns/provider/google"
"sigs.k8s.io/external-dns/provider/ibmcloud"
"sigs.k8s.io/external-dns/provider/inmemory" "sigs.k8s.io/external-dns/provider/inmemory"
"sigs.k8s.io/external-dns/provider/linode" "sigs.k8s.io/external-dns/provider/linode"
"sigs.k8s.io/external-dns/provider/ns1" "sigs.k8s.io/external-dns/provider/ns1"
@ -64,9 +63,7 @@ import (
"sigs.k8s.io/external-dns/provider/plural" "sigs.k8s.io/external-dns/provider/plural"
"sigs.k8s.io/external-dns/provider/rfc2136" "sigs.k8s.io/external-dns/provider/rfc2136"
"sigs.k8s.io/external-dns/provider/scaleway" "sigs.k8s.io/external-dns/provider/scaleway"
"sigs.k8s.io/external-dns/provider/tencentcloud"
"sigs.k8s.io/external-dns/provider/transip" "sigs.k8s.io/external-dns/provider/transip"
"sigs.k8s.io/external-dns/provider/ultradns"
"sigs.k8s.io/external-dns/provider/webhook" "sigs.k8s.io/external-dns/provider/webhook"
webhookapi "sigs.k8s.io/external-dns/provider/webhook/api" webhookapi "sigs.k8s.io/external-dns/provider/webhook/api"
"sigs.k8s.io/external-dns/registry" "sigs.k8s.io/external-dns/registry"
@ -189,8 +186,6 @@ func Execute() {
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun)
case "azure-private-dns": case "azure-private-dns":
p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun)
case "ultradns":
p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
case "civo": case "civo":
p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun) p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
case "cloudflare": case "cloudflare":
@ -307,12 +302,8 @@ func Execute() {
APIVersion: cfg.PiholeApiVersion, APIVersion: cfg.PiholeApiVersion,
}, },
) )
case "ibmcloud":
p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun)
case "plural": case "plural":
p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider) p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider)
case "tencentcloud":
p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun)
case "webhook": case "webhook":
p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL) p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL)
default: default:

View File

@ -43,7 +43,6 @@ TTL must be a positive value.
- [x] Linode - [x] Linode
- [x] TransIP - [x] TransIP
- [x] RFC2136 - [x] RFC2136
- [x] UltraDNS
PRs welcome! PRs welcome!
@ -90,7 +89,3 @@ The Linode Provider default TTL is used when the TTL is 0. The default is 24 hou
### TransIP Provider ### TransIP Provider
The TransIP Provider minimal TTL is used when the TTL is 0. The minimal TTL is 60s. The TransIP Provider minimal TTL is used when the TTL is 0. The minimal TTL is 60s.
### UltraDNS
The UltraDNS provider minimal TTL is used when the TTL is not provided. The default TTL is account level default TTL, if defined, otherwise 24 hours.

View File

@ -107,7 +107,6 @@ Some providers define their own annotations. Cloud-specific annotations have key
|------------|------------------------------------------------| |------------|------------------------------------------------|
| AWS | `external-dns.alpha.kubernetes.io/aws-` | | AWS | `external-dns.alpha.kubernetes.io/aws-` |
| CloudFlare | `external-dns.alpha.kubernetes.io/cloudflare-` | | CloudFlare | `external-dns.alpha.kubernetes.io/cloudflare-` |
| IBM Cloud | `external-dns.alpha.kubernetes.io/ibmcloud-` |
| Scaleway | `external-dns.alpha.kubernetes.io/scw-` | | Scaleway | `external-dns.alpha.kubernetes.io/scw-` |
Additional annotations that are currently implemented only by AWS are: Additional annotations that are currently implemented only by AWS are:

View File

@ -51,7 +51,7 @@
| `--target-net-filter=TARGET-NET-FILTER` | Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional) | | `--target-net-filter=TARGET-NET-FILTER` | Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional) |
| `--[no-]traefik-disable-legacy` | Disable listeners on Resources under the traefik.containo.us API Group | | `--[no-]traefik-disable-legacy` | Disable listeners on Resources under the traefik.containo.us API Group |
| `--[no-]traefik-disable-new` | Disable listeners on Resources under the traefik.io API Group | | `--[no-]traefik-disable-new` | Disable listeners on Resources under the traefik.io API Group |
| `--provider=provider` | The DNS provider where the DNS records will be created (required, options: akamai, alibabacloud, aws, aws-sd, azure, azure-dns, azure-private-dns, civo, cloudflare, coredns, digitalocean, dnsimple, exoscale, gandi, godaddy, google, ibmcloud, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, tencentcloud, transip, ultradns, webhook) | | `--provider=provider` | The DNS provider where the DNS records will be created (required, options: akamai, alibabacloud, aws, aws-sd, azure, azure-dns, azure-private-dns, civo, cloudflare, coredns, digitalocean, dnsimple, exoscale, gandi, godaddy, google, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, transip, webhook) |
| `--provider-cache-time=0s` | The time to cache the DNS provider record list requests. | | `--provider-cache-time=0s` | The time to cache the DNS provider record list requests. |
| `--domain-filter=` | Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional) | | `--domain-filter=` | Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional) |
| `--exclude-domains=` | Exclude subdomains (optional) | | `--exclude-domains=` | Exclude subdomains (optional) |
@ -87,8 +87,6 @@
| `--azure-user-assigned-identity-client-id=""` | When using the Azure provider, override the client id of user assigned identity in config file (optional) | | `--azure-user-assigned-identity-client-id=""` | When using the Azure provider, override the client id of user assigned identity in config file (optional) |
| `--azure-zones-cache-duration=0s` | When using the Azure provider, set the zones list cache TTL (0s to disable). | | `--azure-zones-cache-duration=0s` | When using the Azure provider, set the zones list cache TTL (0s to disable). |
| `--azure-maxretries-count=3` | When using the Azure provider, set the number of retries for API calls (When less than 0, it disables retries). (optional) | | `--azure-maxretries-count=3` | When using the Azure provider, set the number of retries for API calls (When less than 0, it disables retries). (optional) |
| `--tencent-cloud-config-file="/etc/kubernetes/tencent-cloud.json"` | When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud) |
| `--tencent-cloud-zone-type=` | When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private) |
| `--[no-]cloudflare-proxied` | When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled) | | `--[no-]cloudflare-proxied` | When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled) |
| `--[no-]cloudflare-custom-hostnames` | When using the Cloudflare provider, specify if the Custom Hostnames feature will be used. Requires "Cloudflare for SaaS" enabled. (default: disabled) | | `--[no-]cloudflare-custom-hostnames` | When using the Cloudflare provider, specify if the Custom Hostnames feature will be used. Requires "Cloudflare for SaaS" enabled. (default: disabled) |
| `--cloudflare-custom-hostnames-min-tls-version=1.0` | When using the Cloudflare provider with the Custom Hostnames, specify which Minimum TLS Version will be used by default. (default: 1.0, options: 1.0, 1.1, 1.2, 1.3) | | `--cloudflare-custom-hostnames-min-tls-version=1.0` | When using the Cloudflare provider with the Custom Hostnames, specify which Minimum TLS Version will be used by default. (default: 1.0, options: 1.0, 1.1, 1.2, 1.3) |
@ -120,8 +118,6 @@
| `--[no-]ns1-ignoressl` | When using the NS1 provider, specify whether to verify the SSL certificate (default: false) | | `--[no-]ns1-ignoressl` | When using the NS1 provider, specify whether to verify the SSL certificate (default: false) |
| `--ns1-min-ttl=NS1-MIN-TTL` | Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this. | | `--ns1-min-ttl=NS1-MIN-TTL` | Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this. |
| `--digitalocean-api-page-size=50` | Configure the page size used when querying the DigitalOcean API. | | `--digitalocean-api-page-size=50` | Configure the page size used when querying the DigitalOcean API. |
| `--ibmcloud-config-file="/etc/kubernetes/ibmcloud.json"` | When using the IBM Cloud provider, specify the IBM Cloud configuration file (required when --provider=ibmcloud |
| `--[no-]ibmcloud-proxied` | When using the IBM provider, specify if the proxy mode must be enabled (default: disabled) |
| `--godaddy-api-key=""` | When using the GoDaddy provider, specify the API Key (required when --provider=godaddy) | | `--godaddy-api-key=""` | When using the GoDaddy provider, specify the API Key (required when --provider=godaddy) |
| `--godaddy-api-secret=""` | When using the GoDaddy provider, specify the API secret (required when --provider=godaddy) | | `--godaddy-api-secret=""` | When using the GoDaddy provider, specify the API secret (required when --provider=godaddy) |
| `--godaddy-api-ttl=GODADDY-API-TTL` | TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is not provided. | | `--godaddy-api-ttl=GODADDY-API-TTL` | TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is not provided. |

View File

@ -18,7 +18,6 @@ Provider supported configurations
| Gandi | n/a | no | 600 | | Gandi | n/a | no | 600 |
| GoDaddy | n/a | yes | 600 | | GoDaddy | n/a | yes | 600 |
| Google GCP | n/a | yes | 300 | | Google GCP | n/a | yes | 300 |
| IBMCloud | n/a | yes | 1 |
| InMemory | n/a | n/a | n/a | | InMemory | n/a | n/a | n/a |
| Linode | n/a | n/a | n/a | | Linode | n/a | n/a | n/a |
| NS1 | n/a | yes | 10 | | NS1 | n/a | yes | 10 |
@ -29,7 +28,5 @@ Provider supported configurations
| Plural | n/a | n/a | n/a | | Plural | n/a | n/a | n/a |
| RFC2136 | n/a | yes | n/a | | RFC2136 | n/a | yes | n/a |
| Scaleway | n/a | n/a | 300 | | Scaleway | n/a | n/a | 300 |
| TencentCloud | n/a | n/a | n/a |
| Transip | n/a | yes | 60 | | Transip | n/a | yes | 60 |
| Ultradns | n/a | yes | n/a |
| Webhook | n/a | n/a | n/a | | Webhook | n/a | n/a | n/a |

View File

@ -1,277 +0,0 @@
# IBMCloud
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using IBMCloud DNS.
This tutorial uses [IBMCloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-getting-started) for all
IBM Cloud commands and assumes that the Kubernetes cluster was created via IBM Cloud Kubernetes Service and `kubectl` commands
are being run on an orchestration node.
## Creating a IBMCloud DNS zone
The IBMCloud provider for ExternalDNS will find suitable zones for domains it manages; it will
not automatically create zones.
For public zone, This tutorial assume that the [IBMCloud Internet Services](https://cloud.ibm.com/catalog/services/internet-services) was provisioned and the [cis cli plugin](https://cloud.ibm.com/docs/cis?topic=cis-cli-plugin-cis-cli) was installed with IBMCloud CLI
For private zone, This tutorial assume that the [IBMCloud DNS Services](https://cloud.ibm.com/catalog/services/dns-services) was provisioned and the [dns cli plugin](https://cloud.ibm.com/docs/dns-svcs?topic=dns-svcs-cli-plugin-dns-services-cli-commands) was installed with IBMCloud CLI
### Public Zone
For this tutorial, we create public zone named `example.com` on IBMCloud Internet Services instance `external-dns-public`
```sh
ibmcloud cis domain-add example.com -i external-dns-public
```
Follow [step](https://cloud.ibm.com/docs/cis?topic=cis-getting-started#configure-your-name-servers-with-the-registrar-or-existing-dns-provider) to active your zone
### Private Zone
For this tutorial, we create private zone named `example.com` on IBMCloud DNS Services instance `external-dns-private`
```sh
ibmcloud dns zone-create example.com -i external-dns-private
```
## Creating configuration file
The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named azure.json with content similar to this:
```json
{
"apiKey": "1234567890abcdefghijklmnopqrstuvwxyz",
"instanceCrn": "crn:v1:bluemix:public:internet-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:b950da8a-5be6-4691-810e-36388c77b0a3::"
}
```
You can create or find the `apiKey` in your ibmcloud IAM --> [API Keys page](https://cloud.ibm.com/iam/apikeys)
You can find the `instanceCrn` in your service instance details
Now you can create a file named 'ibmcloud.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret:
```sh
kubectl create secret generic ibmcloud-config-file --from-file=/local/path/to/ibmcloud.json
```
## 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.17.0
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=ibmcloud
- --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud
volumeMounts:
- name: ibmcloud-config-file
mountPath: /etc/kubernetes
readOnly: true
volumes:
- name: ibmcloud-config-file
secret:
secretName: ibmcloud-config-file
items:
- key: externaldns-config.json
path: ibmcloud.json
```
### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
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", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
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.17.0
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=ibmcloud
- --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud public zone
volumeMounts:
- name: ibmcloud-config-file
mountPath: /etc/kubernetes
readOnly: true
volumes:
- name: ibmcloud-config-file
secret:
secretName: ibmcloud-config-file
items:
- key: externaldns-config.json
path: ibmcloud.json
```
## 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: www.example.com
external-dns.alpha.kubernetes.io/ttl: "120" #optional
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
```
Note the annotation on the service; use the hostname as the IBMCloud DNS zone created above. The annotation may also be a subdomain
of the DNS zone (e.g. 'www.example.com').
By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above.
This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300.
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation
will cause ExternalDNS to remove the corresponding DNS records.
Create the deployment and service:
```sh
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 IBMCloud DNS records.
## Verifying IBMCloud DNS records
Run the following command to view the A records:
### Public Zone
```sh
# Get the domain ID with below command on IBMCloud Internet Services instance `external-dns-public`
$ ibmcloud cis domains -i external-dns-public
# Get the records with domain ID
$ ibmcloud cis dns-records DOMAIN_ID -i external-dns-public
```
### Private Zone
```sh
# Get the domain ID with below command on IBMCloud DNS Services instance `external-dns-private`
$ ibmcloud dns zones -i external-dns-private
# Get the records with domain ID
$ ibmcloud dns resource-records ZONE_ID -i external-dns-public
```
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 IBMCloud DNS records, we can delete the tutorial's example:
```sh
kubectl delete -f nginx.yaml
kubectl delete -f externaldns.yaml
```
## Setting proxied records on public zone
Using the `external-dns.alpha.kubernetes.io/ibmcloud-proxied: "true"` annotation on your ingress or service, you can specify if the proxy feature of IBMCloud public DNS should be enabled for that record. This setting will override the global `--ibmcloud-proxied` setting.
## Active priviate zone with VPC allocated
By default, IBMCloud DNS Services don't active your private zone with new zone added.
With External DNS, you can use `external-dns.alpha.kubernetes.io/ibmcloud-vpc: "crn:v1:bluemix:public:is:us-south:a/bcf1865e99742d38d2d5fc3fb80a5496::vpc:r006-74353823-a60d-42e4-97c5-5e2551278435"` annotation on your ingress or service.
It will active your private zone with in specific VPC for that record created in.
This setting won't work if the private zone was active already.
Note: the annotaion value is the VPC CRN, every IBM Cloud service have a valid CRN.

View File

@ -1,216 +0,0 @@
# Tencent Cloud
## External Dns Version
* Make sure to use **>=0.13.1** version of ExternalDNS for this tutorial
## Set up PrivateDns or DNSPod
Tencent Cloud DNSPod Service is the domain name resolution and management service for public access.
Tencent Cloud PrivateDNS Service is the domain name resolution and management service for VPC internal access.
* If you want to use internal dns service in Tencent Cloud.
1. Set up the args `--tencent-cloud-zone-type=private`
2. Create a DNS domain in PrivateDNS console. DNS domain which will contain the managed DNS records.
* If you want to use public dns service in Tencent Cloud.
1. Set up the args `--tencent-cloud-zone-type=public`
2. Create a Domain in DnsPod console. DNS domain which will contain the managed DNS records.
## Set up CAM for API Key
In Tencent CAM Console. you may get the secretId and secretKey pair. make sure the key pair has those Policy.
```json
{
"version": "2.0",
"statement": [
{
"effect": "allow",
"action": [
"dnspod:ModifyRecord",
"dnspod:DeleteRecord",
"dnspod:CreateRecord",
"dnspod:DescribeRecordList",
"dnspod:DescribeDomainList"
],
"resource": [
"*"
]
},
{
"effect": "allow",
"action": [
"privatedns:DescribePrivateZoneList",
"privatedns:DescribePrivateZoneRecordList",
"privatedns:CreatePrivateZoneRecord",
"privatedns:DeletePrivateZoneRecord",
"privatedns:ModifyPrivateZoneRecord"
],
"resource": [
"*"
]
}
]
}
```
## Deploy ExternalDNS
### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
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/v1
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: v1
kind: ConfigMap
metadata:
name: external-dns
data:
tencent-cloud.json: |
{
"regionId": "ap-shanghai",
"secretId": "******",
"secretKey": "******",
"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
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
containers:
- args:
- --source=service
- --source=ingress
- --domain-filter=external-dns-test.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
- --provider=tencentcloud
- --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: registry.k8s.io/external-dns/external-dns:v0.17.0
imagePullPolicy: Always
name: external-dns
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/kubernetes
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: {}
serviceAccount: external-dns
serviceAccountName: external-dns
terminationGracePeriodSeconds: 30
volumes:
- configMap:
defaultMode: 420
items:
- key: tencent-cloud.json
path: tencent-cloud.json
name: external-dns
name: config-volume
```
## Example
### Service
```yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.com
external-dns.alpha.kubernetes.io/internal-hostname: nginx-internal.external-dns-test.com
external-dns.alpha.kubernetes.io/ttl: "600"
spec:
type: LoadBalancer
ports:
- port: 80
name: http
targetPort: 80
selector:
app: nginx
---
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
name: http
```
`nginx.external-dns-test.com` will record to the Loadbalancer VIP.
`nginx-internal.external-dns-test.com` will record to the ClusterIP.
all of the DNS Record ttl will be 600.
> [!WARNING]
> This makes ExternalDNS safe for running in environments where there are other records managed via other means.

View File

@ -1,665 +0,0 @@
# UltraDNS
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using UltraDNS.
For this tutorial, please make sure that you are using a version **> 0.7.2** of ExternalDNS.
## Managing DNS with UltraDNS
If you would like to read-up on the UltraDNS service, you can find additional details here: [Introduction to UltraDNS](https://docs.ultradns.com/)
Before proceeding, please create a new DNS Zone that you will create your records in for this tutorial process. For the examples in this tutorial, we will be using `example.com` as our Zone.
## Setting Up UltraDNS Credentials
The following environment variables will be needed to run ExternalDNS with UltraDNS.
`ULTRADNS_USERNAME`,`ULTRADNS_PASSWORD`, &`ULTRADNS_BASEURL`
`ULTRADNS_ACCOUNTNAME`(optional variable).
## Deploying 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.
- Note: We are assuming the zone is already present within UltraDNS.
- Note: While creating CNAMES as target endpoints, the `--txt-prefix` option is mandatory.
### 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.17.0
args:
- --source=service
- --source=ingress # ingress is also possible
- --domain-filter=example.com # (Recommended) We recommend to use this filter as it minimize the time to propagate changes, as there are less number of zones to look into..
- --provider=ultradns
- --txt-prefix=txt-
env:
- name: ULTRADNS_USERNAME
value: ""
- name: ULTRADNS_PASSWORD # The password is required to be BASE64 encrypted.
value: ""
- name: ULTRADNS_BASEURL
value: "https://api.ultradns.com/"
- name: ULTRADNS_ACCOUNTNAME
value: ""
```
### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
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.17.0
args:
- --source=service
- --source=ingress
- --domain-filter=example.com #(Recommended) We recommend to use this filter as it minimize the time to propagate changes, as there are less number of zones to look into..
- --provider=ultradns
- --txt-prefix=txt-
env:
- name: ULTRADNS_USERNAME
value: ""
- name: ULTRADNS_PASSWORD # The password is required to be BASE64 encrypted.
value: ""
- name: ULTRADNS_BASEURL
value: "https://api.ultradns.com/"
- name: ULTRADNS_ACCOUNTNAME
value: ""
```
## 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
```
Please note the annotation on the service. Use the same hostname as the UltraDNS 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.
## Creating the Deployment and Service
```console
kubectl create -f nginx.yaml
kubectl create -f external-dns.yaml
```
Depending on where you run your service from, it can take a few minutes 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 will synchronize the UltraDNS records.
## Verifying UltraDNS Records
Please verify on the [UltraDNS UI](https://portal.ultradns.com/login) that the records are created under the zone "example.com".
For more information on UltraDNS UI, refer to (https://docs.ultradns.com/Content/MSP_User_Guide/Content/User%20Guides/MSP_User_Guide/Navigation/Moving%20Around%20the%20UI.htm#_Toc2780722).
Select the zone that was created above (or select the appropriate zone if a different zone was used.)
The external IP address will be displayed as a CNAME record for your zone.
## Cleaning Up the Deployment and Service
Now that we have verified that ExternalDNS will automatically manage your UltraDNS records, you can delete example zones that you created in this tutorial:
```sh
kubectl delete service -f nginx.yaml
kubectl delete service -f externaldns.yaml
```
## Examples to Manage your Records
### Creating Multiple A Records Target
- First, you want to create a service file called 'apple-banana-echo.yaml'
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: example-app
labels:
app: apple
spec:
containers:
- name: example-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
```
- Then, create service file called 'expose-apple-banana-app.yaml' to expose the services. For more information to deploy ingress controller, refer to (https://kubernetes.github.io/ingress-nginx/deploy/)
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.1,10.10.10.23
spec:
rules:
- http:
paths:
- path: /apple
pathType: Prefix
backend:
service:
name: example-service
port:
number: 5678
```
- Then, create the deployment and service:
```console
kubectl create -f apple-banana-echo.yaml
kubectl create -f expose-apple-banana-app.yaml
kubectl create -f external-dns.yaml
```
- Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
- Please verify on the [UltraDNS UI](https://portal.ultradns.com/login) that the records have been created under the zone "example.com".
- Finally, you will need to clean up the deployment and service. Please verify on the UI afterwards that the records have been deleted from the zone "example.com":
```console
kubectl delete -f apple-banana-echo.yaml
kubectl delete -f expose-apple-banana-app.yaml
kubectl delete -f external-dns.yaml
```
### Creating CNAME Record
- Please note, that prior to deploying the external-dns service, you will need to add the option txt-prefix=txt- into external-dns.yaml. If this not provided, your records will not be created.
- First, create a service file called 'apple-banana-echo.yaml'
- _Config File Example kubernetes cluster is on-premise not on cloud_
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: example-app
labels:
app: apple
spec:
containers:
- name: example-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: apple.cname.com.
spec:
rules:
- http:
paths:
- path: /apple
backend:
service:
name: example-service
port:
number: 5678
```
- _Config File Example Kubernetes cluster service from different cloud vendors_
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: example-app
labels:
app: apple
spec:
containers:
- name: example-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service
annotations:
external-dns.alpha.kubernetes.io/hostname: my-app.example.com.
spec:
selector:
app: apple
type: LoadBalancer
ports:
- protocol: TCP
port: 5678
targetPort: 5678
```
- Then, create the deployment and service:
```console
kubectl create -f apple-banana-echo.yaml
kubectl create -f external-dns.yaml
```
- Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
- Please verify on the [UltraDNS UI](https://portal.ultradns.com/login), that the records have been created under the zone "example.com".
- Finally, you will need to clean up the deployment and service. Please verify on the UI afterwards that the records have been deleted from the zone "example.com":
```console
kubectl delete -f apple-banana-echo.yaml
kubectl delete -f external-dns.yaml
```
### Creating Multiple Types Of Records
- Please note, that prior to deploying the external-dns service, you will need to add the option txt-prefix=txt- into external-dns.yaml. Since you will also be created a CNAME record, If this not provided, your records will not be created.
- First, create a service file called 'apple-banana-echo.yaml'
- _Config File Example kubernetes cluster is on-premise not on cloud_
```yaml
---
kind: Pod
apiVersion: v1
metadata:
name: example-app
labels:
app: apple
spec:
containers:
- name: example-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
name: example-app1
labels:
app: apple1
spec:
containers:
- name: example-app1
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service1
spec:
selector:
app: apple1
ports:
- port: 5679 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
name: example-app2
labels:
app: apple2
spec:
containers:
- name: example-app2
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service2
spec:
selector:
app: apple2
ports:
- port: 5680 # Default port for image
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: apple.cname.com.
spec:
rules:
- http:
paths:
- path: /apple
backend:
service:
name: example-service
port:
number: 5678
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress1
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple-banana.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3
spec:
rules:
- http:
paths:
- path: /apple
backend:
service:
name: example-service1
port:
number: 5679
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress2
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: banana.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3,10.10.10.20
spec:
rules:
- http:
paths:
- path: /apple
backend:
service:
name: example-service2
port:
number: 5680
```
- _Config File Example Kubernetes cluster service from different cloud vendors_
```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
---
kind: Pod
apiVersion: v1
metadata:
name: example-app
labels:
app: apple
spec:
containers:
- name: example-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
name: example-app1
labels:
app: apple1
spec:
containers:
- name: example-app1
image: hashicorp/http-echo
args:
- "-text=apple"
---
apiVersion: extensions/v1beta1
kind: Service
apiVersion: v1
metadata:
name: example-service1
spec:
selector:
app: apple1
ports:
- port: 5679 # Default port for image
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3,10.10.10.25
spec:
rules:
- http:
paths:
- path: /apple
backend:
service:
name: example-service
port:
number: 5678
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress1
annotations:
ingress.kubernetes.io/rewrite-target: /
ingress.kubernetes.io/scheme: internet-facing
external-dns.alpha.kubernetes.io/hostname: apple-banana.example.com.
external-dns.alpha.kubernetes.io/target: 10.10.10.3
spec:
rules:
- http:
paths:
- path: /apple
backend:
service:
name: example-service1
port:
number: 5679
```
- Then, create the deployment and service:
```console
kubectl create -f apple-banana-echo.yaml
kubectl create -f external-dns.yaml
```
- Depending on where you run your service from, it can take a few minutes for your cloud provider to create an external IP for the service.
- Please verify on the [UltraDNS UI](https://portal.ultradns.com/login), that the records have been created under the zone "example.com".
- Finally, you will need to clean up the deployment and service. Please verify on the UI afterwards that the records have been deleted from the zone "example.com":
```console
kubectl delete -f apple-banana-echo.yaml
kubectl delete -f external-dns.yaml```

27
go.mod
View File

@ -9,9 +9,6 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1 github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2
github.com/IBM/go-sdk-core/v5 v5.19.1
github.com/IBM/networking-go-sdk v0.51.5
github.com/Yamashou/gqlgenc v0.32.1 github.com/Yamashou/gqlgenc v0.32.1
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/kingpin/v2 v2.4.0
@ -44,7 +41,6 @@ require (
github.com/linode/linodego v1.50.0 github.com/linode/linodego v1.50.0
github.com/maxatome/go-testdeep v1.14.0 github.com/maxatome/go-testdeep v1.14.0
github.com/miekg/dns v1.1.66 github.com/miekg/dns v1.1.66
github.com/onsi/ginkgo v1.16.5
github.com/openshift/api v0.0.0-20230607130528-611114dca681 github.com/openshift/api v0.0.0-20230607130528-611114dca681
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3 github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
github.com/oracle/oci-go-sdk/v65 v65.91.0 github.com/oracle/oci-go-sdk/v65 v65.91.0
@ -56,11 +52,7 @@ require (
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1165
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145
github.com/transip/gotransip/v6 v6.26.0 github.com/transip/gotransip/v6 v6.26.0
github.com/ultradns/ultradns-sdk-go v1.3.7
go.etcd.io/etcd/client/v3 v3.6.0 go.etcd.io/etcd/client/v3 v3.6.0
go.uber.org/ratelimit v0.3.1 go.uber.org/ratelimit v0.3.1
golang.org/x/net v0.40.0 golang.org/x/net v0.40.0
@ -76,6 +68,7 @@ require (
k8s.io/apimachinery v0.33.1 k8s.io/apimachinery v0.33.1
k8s.io/client-go v0.33.1 k8s.io/client-go v0.33.1
k8s.io/klog/v2 v2.130.1 k8s.io/klog/v2 v2.130.1
sigs.k8s.io/controller-runtime v0.20.4
sigs.k8s.io/gateway-api v1.3.0 sigs.k8s.io/gateway-api v1.3.0
) )
@ -89,7 +82,6 @@ require (
github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
@ -109,20 +101,12 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deepmap/oapi-codegen v1.9.1 // indirect github.com/deepmap/oapi-codegen v1.9.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-resty/resty/v2 v2.16.5 // indirect github.com/go-resty/resty/v2 v2.16.5 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/flock v0.8.1 // indirect
@ -153,17 +137,14 @@ require (
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/peterhellberg/link v1.1.0 // indirect github.com/peterhellberg/link v1.1.0 // indirect
@ -180,14 +161,12 @@ require (
github.com/sosodev/duration v1.3.1 // indirect github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/terra-farm/udnssdk v1.3.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.25 // indirect github.com/vektah/gqlparser/v2 v2.5.25 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.etcd.io/etcd/api/v3 v3.6.0 // indirect go.etcd.io/etcd/api/v3 v3.6.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.6.0 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect
@ -208,13 +187,11 @@ require (
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
moul.io/http2curl v1.0.0 // indirect moul.io/http2curl v1.0.0 // indirect
sigs.k8s.io/controller-runtime v0.20.4 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect

45
go.sum
View File

@ -51,12 +51,6 @@ github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1 h1:NHWjSBeXbL8mlx+0QyCl4OrUvytCZ3nkEIRqX7t97wQ= github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1 h1:NHWjSBeXbL8mlx+0QyCl4OrUvytCZ3nkEIRqX7t97wQ=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1/go.mod h1:JwdtGjHFTmUM1zjzvvCotCCyP55S146IuVPOJZ7D/Jw= github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1/go.mod h1:JwdtGjHFTmUM1zjzvvCotCCyP55S146IuVPOJZ7D/Jw=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2 h1:eW5o8NpblAyqPjwOlZ+XISdhlYynjf7B7dsCmsvfC/s=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2/go.mod h1:HulyrJLLc9FSZlwKQ9vu5Jq83thNlUfg1afonOdhrRA=
github.com/IBM/go-sdk-core/v5 v5.19.1 h1:sleVks1O4XjgF4YEGvyDh6PZbP6iZhlTPeDkQc8nWDs=
github.com/IBM/go-sdk-core/v5 v5.19.1/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw=
github.com/IBM/networking-go-sdk v0.51.5 h1:75lKAx17y++hirXK5GcEM23mTRhHnhsv6gmhz70ex1Q=
github.com/IBM/networking-go-sdk v0.51.5/go.mod h1:wyEnRnBnROgGmSn5UrryycIrbBujHKXf0PmI1NSwcjY=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
@ -116,8 +110,6 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@ -306,8 +298,6 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 h1:jmwW6QWvUO2OPe22YfgFvBaaZlSr8Rlrac5lZvG6IdM= github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 h1:jmwW6QWvUO2OPe22YfgFvBaaZlSr8Rlrac5lZvG6IdM=
@ -321,8 +311,6 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -360,8 +348,6 @@ github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
@ -394,8 +380,6 @@ github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pL
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
@ -407,20 +391,12 @@ github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -632,8 +608,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
@ -686,8 +660,6 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtB
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
@ -766,8 +738,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -799,7 +769,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@ -1021,16 +990,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1145/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1165/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166 h1:WgdCsNxde/flDGxOy+dXZqm2wrUQA/4kzaaY69XC/2A=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1165 h1:bHT5sLou90UwrVm7Km2nQ7hyBk0+LK4xi7JOZXoDQ2c=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1165/go.mod h1:6pN5Nh0PuYJ+XHkjiV7SQqXgc1gecB9FHd456W9ZNdE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145 h1:K5N0Uxqm9kM7KU6DFBekCTKbldlXq6UD1ekOyXn4zEc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145/go.mod h1:mgwxarWzLxIylWtHmkoMg0dX8aqhO5lwfrHkK4IGLKE=
github.com/terra-farm/udnssdk v1.3.5 h1:MNR3adfuuEK/l04+jzo8WW/0fnorY+nW515qb3vEr6I=
github.com/terra-farm/udnssdk v1.3.5/go.mod h1:8RnM56yZTR7mYyUIvrDgXzdRaEyFIzqdEi7+um26Sv8=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -1047,8 +1006,6 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ultradns/ultradns-sdk-go v1.3.7 h1:P4CaM+npeXIybbLL27ezR316NnyILI1Y8IvfZtNE+Co=
github.com/ultradns/ultradns-sdk-go v1.3.7/go.mod h1:43vmy6GEvRuVMpGEWfJ/JoEM6RIqUQI1/tb8JqZR1zI=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@ -1092,8 +1049,6 @@ go.etcd.io/etcd/client/v3 v3.6.0/go.mod h1:Jzk/Knqe06pkOZPHXsQ0+vNDvMQrgIqJ0W8Dw
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=

View File

@ -199,10 +199,6 @@ type Config struct {
GoDaddyTTL int64 GoDaddyTTL int64
GoDaddyOTE bool GoDaddyOTE bool
OCPRouterName string OCPRouterName string
IBMCloudProxied bool
IBMCloudConfigFile string
TencentCloudConfigFile string
TencentCloudZoneType string
PiholeServer string PiholeServer string
PiholePassword string `secure:"yes"` PiholePassword string `secure:"yes"`
PiholeTLSInsecureSkipVerify bool PiholeTLSInsecureSkipVerify bool
@ -293,8 +289,6 @@ var defaultConfig = &Config{
GoogleBatchChangeSize: 1000, GoogleBatchChangeSize: 1000,
GoogleProject: "", GoogleProject: "",
GoogleZoneVisibility: "", GoogleZoneVisibility: "",
IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json",
IBMCloudProxied: false,
IgnoreHostnameAnnotation: false, IgnoreHostnameAnnotation: false,
IgnoreIngressRulesSpec: false, IgnoreIngressRulesSpec: false,
IgnoreIngressTLSSpec: false, IgnoreIngressTLSSpec: false,
@ -360,8 +354,6 @@ var defaultConfig = &Config{
SkipperRouteGroupVersion: "zalando.org/v1", SkipperRouteGroupVersion: "zalando.org/v1",
Sources: nil, Sources: nil,
TargetNetFilter: []string{}, TargetNetFilter: []string{},
TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json",
TencentCloudZoneType: "",
TLSCA: "", TLSCA: "",
TLSClientCert: "", TLSClientCert: "",
TLSClientCertKey: "", TLSClientCertKey: "",
@ -495,7 +487,7 @@ func App(cfg *Config) *kingpin.Application {
app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew) app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew)
// Flags related to providers // Flags related to providers
providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"} providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "transip", "webhook"}
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: "+strings.Join(providers, ", ")+")").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, providers...) app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: "+strings.Join(providers, ", ")+")").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, providers...)
app.Flag("provider-cache-time", "The time to cache the DNS provider record list requests.").Default(defaultConfig.ProviderCacheTime.String()).DurationVar(&cfg.ProviderCacheTime) app.Flag("provider-cache-time", "The time to cache the DNS provider record list requests.").Default(defaultConfig.ProviderCacheTime.String()).DurationVar(&cfg.ProviderCacheTime)
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
@ -532,8 +524,6 @@ func App(cfg *Config) *kingpin.Application {
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID) app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
app.Flag("azure-zones-cache-duration", "When using the Azure provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AzureZonesCacheDuration.String()).DurationVar(&cfg.AzureZonesCacheDuration) app.Flag("azure-zones-cache-duration", "When using the Azure provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AzureZonesCacheDuration.String()).DurationVar(&cfg.AzureZonesCacheDuration)
app.Flag("azure-maxretries-count", "When using the Azure provider, set the number of retries for API calls (When less than 0, it disables retries). (optional)").Default(strconv.Itoa(defaultConfig.AzureMaxRetriesCount)).IntVar(&cfg.AzureMaxRetriesCount) app.Flag("azure-maxretries-count", "When using the Azure provider, set the number of retries for API calls (When less than 0, it disables retries). (optional)").Default(strconv.Itoa(defaultConfig.AzureMaxRetriesCount)).IntVar(&cfg.AzureMaxRetriesCount)
app.Flag("tencent-cloud-config-file", "When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud)").Default(defaultConfig.TencentCloudConfigFile).StringVar(&cfg.TencentCloudConfigFile)
app.Flag("tencent-cloud-zone-type", "When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private)").Default(defaultConfig.TencentCloudZoneType).EnumVar(&cfg.TencentCloudZoneType, "", "public", "private")
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied) app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
app.Flag("cloudflare-custom-hostnames", "When using the Cloudflare provider, specify if the Custom Hostnames feature will be used. Requires \"Cloudflare for SaaS\" enabled. (default: disabled)").BoolVar(&cfg.CloudflareCustomHostnames) app.Flag("cloudflare-custom-hostnames", "When using the Cloudflare provider, specify if the Custom Hostnames feature will be used. Requires \"Cloudflare for SaaS\" enabled. (default: disabled)").BoolVar(&cfg.CloudflareCustomHostnames)
@ -567,8 +557,6 @@ func App(cfg *Config) *kingpin.Application {
app.Flag("ns1-ignoressl", "When using the NS1 provider, specify whether to verify the SSL certificate (default: false)").Default(strconv.FormatBool(defaultConfig.NS1IgnoreSSL)).BoolVar(&cfg.NS1IgnoreSSL) app.Flag("ns1-ignoressl", "When using the NS1 provider, specify whether to verify the SSL certificate (default: false)").Default(strconv.FormatBool(defaultConfig.NS1IgnoreSSL)).BoolVar(&cfg.NS1IgnoreSSL)
app.Flag("ns1-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.NS1MinTTLSeconds) app.Flag("ns1-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.NS1MinTTLSeconds)
app.Flag("digitalocean-api-page-size", "Configure the page size used when querying the DigitalOcean API.").Default(strconv.Itoa(defaultConfig.DigitalOceanAPIPageSize)).IntVar(&cfg.DigitalOceanAPIPageSize) app.Flag("digitalocean-api-page-size", "Configure the page size used when querying the DigitalOcean API.").Default(strconv.Itoa(defaultConfig.DigitalOceanAPIPageSize)).IntVar(&cfg.DigitalOceanAPIPageSize)
app.Flag("ibmcloud-config-file", "When using the IBM Cloud provider, specify the IBM Cloud configuration file (required when --provider=ibmcloud").Default(defaultConfig.IBMCloudConfigFile).StringVar(&cfg.IBMCloudConfigFile)
app.Flag("ibmcloud-proxied", "When using the IBM provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.IBMCloudProxied)
// GoDaddy flags // GoDaddy flags
app.Flag("godaddy-api-key", "When using the GoDaddy provider, specify the API Key (required when --provider=godaddy)").Default(defaultConfig.GoDaddyAPIKey).StringVar(&cfg.GoDaddyAPIKey) app.Flag("godaddy-api-key", "When using the GoDaddy provider, specify the API Key (required when --provider=godaddy)").Default(defaultConfig.GoDaddyAPIKey).StringVar(&cfg.GoDaddyAPIKey)
app.Flag("godaddy-api-secret", "When using the GoDaddy provider, specify the API secret (required when --provider=godaddy)").Default(defaultConfig.GoDaddySecretKey).StringVar(&cfg.GoDaddySecretKey) app.Flag("godaddy-api-secret", "When using the GoDaddy provider, specify the API secret (required when --provider=godaddy)").Default(defaultConfig.GoDaddySecretKey).StringVar(&cfg.GoDaddySecretKey)

View File

@ -125,10 +125,6 @@ var (
RFC2136Host: []string{""}, RFC2136Host: []string{""},
RFC2136LoadBalancingStrategy: "disabled", RFC2136LoadBalancingStrategy: "disabled",
OCPRouterName: "default", OCPRouterName: "default",
IBMCloudProxied: false,
IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json",
TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json",
TencentCloudZoneType: "",
PiholeApiVersion: "5", PiholeApiVersion: "5",
WebhookProviderURL: "http://localhost:8888", WebhookProviderURL: "http://localhost:8888",
WebhookProviderReadTimeout: 5 * time.Second, WebhookProviderReadTimeout: 5 * time.Second,
@ -242,10 +238,6 @@ var (
RFC2136BatchChangeSize: 100, RFC2136BatchChangeSize: 100,
RFC2136Host: []string{"rfc2136-host1", "rfc2136-host2"}, RFC2136Host: []string{"rfc2136-host1", "rfc2136-host2"},
RFC2136LoadBalancingStrategy: "round-robin", RFC2136LoadBalancingStrategy: "round-robin",
IBMCloudProxied: true,
IBMCloudConfigFile: "ibmcloud.json",
TencentCloudConfigFile: "tencent-cloud.json",
TencentCloudZoneType: "private",
PiholeApiVersion: "6", PiholeApiVersion: "6",
WebhookProviderURL: "http://localhost:8888", WebhookProviderURL: "http://localhost:8888",
WebhookProviderReadTimeout: 5 * time.Second, WebhookProviderReadTimeout: 5 * time.Second,
@ -396,10 +388,6 @@ func TestParseFlags(t *testing.T) {
"--rfc2136-load-balancing-strategy=round-robin", "--rfc2136-load-balancing-strategy=round-robin",
"--rfc2136-host=rfc2136-host1", "--rfc2136-host=rfc2136-host1",
"--rfc2136-host=rfc2136-host2", "--rfc2136-host=rfc2136-host2",
"--ibmcloud-proxied",
"--ibmcloud-config-file=ibmcloud.json",
"--tencent-cloud-config-file=tencent-cloud.json",
"--tencent-cloud-zone-type=private",
}, },
envVars: map[string]string{}, envVars: map[string]string{},
expected: overriddenConfig, expected: overriddenConfig,
@ -516,10 +504,6 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100", "EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100",
"EXTERNAL_DNS_RFC2136_LOAD_BALANCING_STRATEGY": "round-robin", "EXTERNAL_DNS_RFC2136_LOAD_BALANCING_STRATEGY": "round-robin",
"EXTERNAL_DNS_RFC2136_HOST": "rfc2136-host1\nrfc2136-host2", "EXTERNAL_DNS_RFC2136_HOST": "rfc2136-host1\nrfc2136-host2",
"EXTERNAL_DNS_IBMCLOUD_PROXIED": "1",
"EXTERNAL_DNS_IBMCLOUD_CONFIG_FILE": "ibmcloud.json",
"EXTERNAL_DNS_TENCENT_CLOUD_CONFIG_FILE": "tencent-cloud.json",
"EXTERNAL_DNS_TENCENT_CLOUD_ZONE_TYPE": "private",
}, },
expected: overriddenConfig, expected: overriddenConfig,
}, },

File diff suppressed because it is too large Load Diff

View File

@ -1,953 +0,0 @@
/*
Copyright 2022 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 ibmcloud
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/IBM/go-sdk-core/v5/core"
"github.com/IBM/networking-go-sdk/dnsrecordsv1"
"github.com/stretchr/testify/require"
"github.com/IBM/networking-go-sdk/dnssvcsv1"
. "github.com/onsi/ginkgo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)
func NewMockIBMCloudDNSAPI() *mockIbmcloudClientInterface {
// Setup public example responses
firstPublicRecord := dnsrecordsv1.DnsrecordDetails{
ID: core.StringPtr("123"),
Name: core.StringPtr("test.example.com"),
Type: core.StringPtr("A"),
Content: core.StringPtr("1.2.3.4"),
Proxied: core.BoolPtr(true),
TTL: core.Int64Ptr(int64(120)),
}
secondPublicRecord := dnsrecordsv1.DnsrecordDetails{
ID: core.StringPtr("456"),
Name: core.StringPtr("test.example.com"),
Type: core.StringPtr("TXT"),
Proxied: core.BoolPtr(false),
Content: core.StringPtr("\"heritage=external-dns,external-dns/owner=tower-pdns\""),
TTL: core.Int64Ptr(int64(120)),
}
publicRecordsResult := []dnsrecordsv1.DnsrecordDetails{firstPublicRecord, secondPublicRecord}
publicRecordsResultInfo := &dnsrecordsv1.ResultInfo{
Page: core.Int64Ptr(int64(1)),
TotalCount: core.Int64Ptr(int64(1)),
}
publicRecordsResp := &dnsrecordsv1.ListDnsrecordsResp{
Result: publicRecordsResult,
ResultInfo: publicRecordsResultInfo,
}
// Setup private example responses
firstPrivateZone := dnssvcsv1.Dnszone{
ID: core.StringPtr("123"),
Name: core.StringPtr("example.com"),
State: core.StringPtr(zoneStatePendingNetwork),
}
secondPrivateZone := dnssvcsv1.Dnszone{
ID: core.StringPtr("456"),
Name: core.StringPtr("example1.com"),
State: core.StringPtr(zoneStateActive),
}
privateZones := []dnssvcsv1.Dnszone{firstPrivateZone, secondPrivateZone}
listZonesResp := &dnssvcsv1.ListDnszones{
Dnszones: privateZones,
}
firstPrivateRecord := dnssvcsv1.ResourceRecord{
ID: core.StringPtr("123"),
Name: core.StringPtr("test.example.com"),
Type: core.StringPtr("A"),
Rdata: map[string]interface{}{"ip": "1.2.3.4"},
TTL: core.Int64Ptr(int64(120)),
}
secondPrivateRecord := dnssvcsv1.ResourceRecord{
ID: core.StringPtr("456"),
Name: core.StringPtr("testCNAME.example.com"),
Type: core.StringPtr("CNAME"),
Rdata: map[string]interface{}{"cname": "test.example.com"},
TTL: core.Int64Ptr(int64(120)),
}
thirdPrivateRecord := dnssvcsv1.ResourceRecord{
ID: core.StringPtr("789"),
Name: core.StringPtr("test.example.com"),
Type: core.StringPtr("TXT"),
Rdata: map[string]interface{}{"text": "\"heritage=external-dns,external-dns/owner=tower-pdns\""},
TTL: core.Int64Ptr(int64(120)),
}
privateRecords := []dnssvcsv1.ResourceRecord{firstPrivateRecord, secondPrivateRecord, thirdPrivateRecord}
privateRecordsResop := &dnssvcsv1.ListResourceRecords{
ResourceRecords: privateRecords,
Offset: core.Int64Ptr(int64(0)),
TotalCount: core.Int64Ptr(int64(1)),
}
// Setup record rData
inputARecord := &dnssvcsv1.ResourceRecordInputRdataRdataARecord{
Ip: core.StringPtr("1.2.3.4"),
}
inputCnameRecord := &dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord{
Cname: core.StringPtr("test.example.com"),
}
inputTxtRecord := &dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord{
Text: core.StringPtr("test"),
}
updateARecord := &dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord{
Ip: core.StringPtr("1.2.3.4"),
}
updateCnameRecord := &dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord{
Cname: core.StringPtr("test.example.com"),
}
updateTxtRecord := &dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord{
Text: core.StringPtr("test"),
}
// Setup mock services
mockDNSClient := &mockIbmcloudClientInterface{}
mockDNSClient.On("CreateDNSRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("UpdateDNSRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("DeleteDNSRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("ListAllDDNSRecordsWithContext", mock.Anything, mock.Anything).Return(publicRecordsResp, nil, nil)
mockDNSClient.On("ListDnszonesWithContext", mock.Anything, mock.Anything).Return(listZonesResp, nil, nil)
mockDNSClient.On("GetDnszoneWithContext", mock.Anything, mock.Anything).Return(&firstPrivateZone, nil, nil)
mockDNSClient.On("ListResourceRecordsWithContext", mock.Anything, mock.Anything).Return(privateRecordsResop, nil, nil)
mockDNSClient.On("CreatePermittedNetworkWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("CreateResourceRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("DeleteResourceRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("UpdateResourceRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil)
mockDNSClient.On("NewResourceRecordInputRdataRdataARecord", mock.Anything).Return(inputARecord, nil)
mockDNSClient.On("NewResourceRecordInputRdataRdataCnameRecord", mock.Anything).Return(inputCnameRecord, nil)
mockDNSClient.On("NewResourceRecordInputRdataRdataTxtRecord", mock.Anything).Return(inputTxtRecord, nil)
mockDNSClient.On("NewResourceRecordUpdateInputRdataRdataARecord", mock.Anything).Return(updateARecord, nil)
mockDNSClient.On("NewResourceRecordUpdateInputRdataRdataCnameRecord", mock.Anything).Return(updateCnameRecord, nil)
mockDNSClient.On("NewResourceRecordUpdateInputRdataRdataTxtRecord", mock.Anything).Return(updateTxtRecord, nil)
return mockDNSClient
}
func newTestIBMCloudProvider(private bool) *IBMCloudProvider {
mockSource := &mockSource{}
endpoints := []*endpoint.Endpoint{
{
DNSName: "new.example.com",
Targets: endpoint.Targets{"4.3.2.1"},
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: "ibmcloud-vpc",
Value: "crn:v1:staging:public:is:us-south:a/0821fa9f9ebcc7b7c9a0d6e9bf9442a4::vpc:be33cdad-9a03-4bfa-82ca-eadb9f1de688",
},
},
},
}
mockSource.On("Endpoints", mock.Anything).Return(endpoints, nil, nil)
domainFilterTest := endpoint.NewDomainFilter([]string{"example.com"})
return &IBMCloudProvider{
Client: NewMockIBMCloudDNSAPI(),
source: mockSource,
domainFilter: domainFilterTest,
DryRun: false,
instanceID: "test123",
privateZone: private,
}
}
func TestPublic_Records(t *testing.T) {
p := newTestIBMCloudProvider(false)
endpoints, err := p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
}
}
}
func TestPrivate_Records(t *testing.T) {
p := newTestIBMCloudProvider(true)
endpoints, err := p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 3 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
}
}
}
func TestPublic_ApplyChanges(t *testing.T) {
p := newTestIBMCloudProvider(false)
changes := plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "newA.example.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("4.3.2.1"),
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: "ibmcloud-proxied",
Value: "false",
},
},
},
},
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordType: "A",
RecordTTL: 180,
Targets: endpoint.NewTargets("1.2.3.4"),
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: "ibmcloud-proxied",
Value: "false",
},
},
},
},
UpdateNew: []*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordType: "A",
RecordTTL: 180,
Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"),
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: "ibmcloud-proxied",
Value: "true",
},
},
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordType: "TXT",
RecordTTL: 300,
Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=tower-pdns\""),
},
},
}
ctx := context.Background()
err := p.ApplyChanges(ctx, &changes)
if err != nil {
t.Errorf("should not fail, %s", err)
}
}
func TestPrivate_ApplyChanges(t *testing.T) {
p := newTestIBMCloudProvider(true)
endpointsCreate, err := p.AdjustEndpoints([]*endpoint.Endpoint{
{
DNSName: "newA.example.com",
RecordType: "A",
RecordTTL: 120,
Targets: endpoint.NewTargets("4.3.2.1"),
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: "ibmcloud-vpc",
Value: "crn:v1:staging:public:is:us-south:a/0821fa9f9ebcc7b7c9a0d6e9bf9442a4::vpc:be33cdad-9a03-4bfa-82ca-eadb9f1de688",
},
},
},
{
DNSName: "newCNAME.example.com",
RecordType: "CNAME",
RecordTTL: 180,
Targets: endpoint.NewTargets("newA.example.com"),
},
{
DNSName: "newTXT.example.com",
RecordType: "TXT",
RecordTTL: 240,
Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=tower-pdns\""),
},
})
require.NoError(t, err)
endpointsUpdate, err := p.AdjustEndpoints([]*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordType: "A",
RecordTTL: 180,
Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"),
},
})
require.NoError(t, err)
changes := plan.Changes{
Create: endpointsCreate,
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordType: "A",
RecordTTL: 180,
Targets: endpoint.NewTargets("1.2.3.4"),
},
},
UpdateNew: endpointsUpdate,
Delete: []*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordType: "TXT",
RecordTTL: 300,
Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=tower-pdns\""),
},
},
}
ctx := context.Background()
err = p.ApplyChanges(ctx, &changes)
if err != nil {
t.Errorf("should not fail, %s", err)
}
}
func TestAdjustEndpoints(t *testing.T) {
p := newTestIBMCloudProvider(false)
endpoints := []*endpoint.Endpoint{
{
DNSName: "test.example.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: 300,
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
Name: "ibmcloud-proxied",
Value: "1",
},
},
},
}
ep, err := p.AdjustEndpoints(endpoints)
assert.NoError(t, err)
assert.Equal(t, endpoint.TTL(0), ep[0].RecordTTL)
assert.Equal(t, "test.example.com", ep[0].DNSName)
proxied, _ := ep[0].GetProviderSpecificProperty("ibmcloud-proxied")
assert.Equal(t, "true", proxied)
}
func TestPrivateZone_withFilterID(t *testing.T) {
p := newTestIBMCloudProvider(true)
p.zoneIDFilter = provider.NewZoneIDFilter([]string{"123", "456"})
zones, err := p.privateZones(context.Background())
if err != nil {
t.Errorf("should not fail, %s", err)
} else {
if len(zones) != 2 {
t.Errorf("Incorrect number of zones: %d", len(zones))
}
for _, zone := range zones {
t.Logf("zone %s", *zone.ID)
}
}
}
func TestPublicConfig_Validate(t *testing.T) {
// mock http server
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
defer GinkgoRecover()
time.Sleep(0)
// Set mock response
res.Header().Set("Content-type", "application/json")
res.WriteHeader(http.StatusOK)
fmt.Fprintf(res, "%s", `{"success": true, "errors": [["Errors"]], "messages": [["Messages"]], "result": [{"id": "123", "created_on": "2014-01-01T05:20:00.12345Z", "modified_on": "2014-01-01T05:20:00.12345Z", "name": "example.com", "original_registrar": "GoDaddy", "original_dnshost": "NameCheap", "status": "active", "paused": false, "original_name_servers": ["ns1.originaldnshost.com"], "name_servers": ["ns001.name.cloud.ibm.com"]}], "result_info": {"page": 1, "per_page": 20, "count": 1, "total_count": 2000}}`)
}))
zoneIDFilterTest := provider.NewZoneIDFilter([]string{"123"})
domainFilterTest := endpoint.NewDomainFilter([]string{"example.com"})
cfg := &ibmcloudConfig{
Endpoint: testServer.URL,
CRN: "crn:v1:bluemix:public:internet-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:a6338168-9510-4951-9d67-425612de96f0::",
}
crn := cfg.CRN
authenticator := &core.NoAuthAuthenticator{}
service, isPrivate, err := cfg.Validate(authenticator, domainFilterTest, provider.NewZoneIDFilter([]string{""}))
assert.NoError(t, err)
assert.False(t, isPrivate)
assert.Equal(t, crn, *service.publicRecordsService.Crn)
assert.Equal(t, "123", *service.publicRecordsService.ZoneIdentifier)
service, isPrivate, err = cfg.Validate(authenticator, endpoint.NewDomainFilter([]string{""}), zoneIDFilterTest)
assert.NoError(t, err)
assert.False(t, isPrivate)
assert.Equal(t, crn, *service.publicRecordsService.Crn)
assert.Equal(t, "123", *service.publicRecordsService.ZoneIdentifier)
testServer.Close()
}
func TestPrivateConfig_Validate(t *testing.T) {
zoneIDFilterTest := provider.NewZoneIDFilter([]string{"123"})
domainFilterTest := endpoint.NewDomainFilter([]string{"example.com"})
authenticator := &core.NoAuthAuthenticator{}
cfg := &ibmcloudConfig{
Endpoint: "XXX",
CRN: "crn:v1:bluemix:public:dns-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:a6338168-9510-4951-9d67-425612de96f0::",
}
_, isPrivate, err := cfg.Validate(authenticator, domainFilterTest, zoneIDFilterTest)
assert.NoError(t, err)
assert.True(t, isPrivate)
}
// mockIbmcloudClientInterface is an autogenerated mock type for the ibmcloudClient type
type mockIbmcloudClientInterface struct {
mock.Mock
}
// CreateDNSRecordWithContext provides a mock function with given fields: ctx, createDnsRecordOptions
func (_m *mockIbmcloudClientInterface) CreateDNSRecordWithContext(ctx context.Context, createDnsRecordOptions *dnsrecordsv1.CreateDnsRecordOptions) (*dnsrecordsv1.DnsrecordResp, *core.DetailedResponse, error) {
ret := _m.Called(ctx, createDnsRecordOptions)
var r0 *dnsrecordsv1.DnsrecordResp
if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.CreateDnsRecordOptions) *dnsrecordsv1.DnsrecordResp); ok {
r0 = rf(ctx, createDnsRecordOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnsrecordsv1.DnsrecordResp)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.CreateDnsRecordOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, createDnsRecordOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.CreateDnsRecordOptions) error); ok {
r2 = rf(ctx, createDnsRecordOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// CreatePermittedNetworkWithContext provides a mock function with given fields: ctx, createPermittedNetworkOptions
func (_m *mockIbmcloudClientInterface) CreatePermittedNetworkWithContext(ctx context.Context, createPermittedNetworkOptions *dnssvcsv1.CreatePermittedNetworkOptions) (*dnssvcsv1.PermittedNetwork, *core.DetailedResponse, error) {
ret := _m.Called(ctx, createPermittedNetworkOptions)
var r0 *dnssvcsv1.PermittedNetwork
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.CreatePermittedNetworkOptions) *dnssvcsv1.PermittedNetwork); ok {
r0 = rf(ctx, createPermittedNetworkOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.PermittedNetwork)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.CreatePermittedNetworkOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, createPermittedNetworkOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.CreatePermittedNetworkOptions) error); ok {
r2 = rf(ctx, createPermittedNetworkOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// CreateResourceRecordWithContext provides a mock function with given fields: ctx, createResourceRecordOptions
func (_m *mockIbmcloudClientInterface) CreateResourceRecordWithContext(ctx context.Context, createResourceRecordOptions *dnssvcsv1.CreateResourceRecordOptions) (*dnssvcsv1.ResourceRecord, *core.DetailedResponse, error) {
ret := _m.Called(ctx, createResourceRecordOptions)
var r0 *dnssvcsv1.ResourceRecord
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.CreateResourceRecordOptions) *dnssvcsv1.ResourceRecord); ok {
r0 = rf(ctx, createResourceRecordOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecord)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.CreateResourceRecordOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, createResourceRecordOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.CreateResourceRecordOptions) error); ok {
r2 = rf(ctx, createResourceRecordOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// DeleteDNSRecordWithContext provides a mock function with given fields: ctx, deleteDnsRecordOptions
func (_m *mockIbmcloudClientInterface) DeleteDNSRecordWithContext(ctx context.Context, deleteDnsRecordOptions *dnsrecordsv1.DeleteDnsRecordOptions) (*dnsrecordsv1.DeleteDnsrecordResp, *core.DetailedResponse, error) {
ret := _m.Called(ctx, deleteDnsRecordOptions)
var r0 *dnsrecordsv1.DeleteDnsrecordResp
if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.DeleteDnsRecordOptions) *dnsrecordsv1.DeleteDnsrecordResp); ok {
r0 = rf(ctx, deleteDnsRecordOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnsrecordsv1.DeleteDnsrecordResp)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.DeleteDnsRecordOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, deleteDnsRecordOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.DeleteDnsRecordOptions) error); ok {
r2 = rf(ctx, deleteDnsRecordOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// DeleteResourceRecordWithContext provides a mock function with given fields: ctx, deleteResourceRecordOptions
func (_m *mockIbmcloudClientInterface) DeleteResourceRecordWithContext(ctx context.Context, deleteResourceRecordOptions *dnssvcsv1.DeleteResourceRecordOptions) (*core.DetailedResponse, error) {
ret := _m.Called(ctx, deleteResourceRecordOptions)
var r0 *core.DetailedResponse
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.DeleteResourceRecordOptions) *core.DetailedResponse); ok {
r0 = rf(ctx, deleteResourceRecordOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*core.DetailedResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.DeleteResourceRecordOptions) error); ok {
r1 = rf(ctx, deleteResourceRecordOptions)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetDnszoneWithContext provides a mock function with given fields: ctx, getDnszoneOptions
func (_m *mockIbmcloudClientInterface) GetDnszoneWithContext(ctx context.Context, getDnszoneOptions *dnssvcsv1.GetDnszoneOptions) (*dnssvcsv1.Dnszone, *core.DetailedResponse, error) {
ret := _m.Called(ctx, getDnszoneOptions)
var r0 *dnssvcsv1.Dnszone
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.GetDnszoneOptions) *dnssvcsv1.Dnszone); ok {
r0 = rf(ctx, getDnszoneOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.Dnszone)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.GetDnszoneOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, getDnszoneOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.GetDnszoneOptions) error); ok {
r2 = rf(ctx, getDnszoneOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// ListAllDDNSRecordsWithContext provides a mock function with given fields: ctx, listAllDnsRecordsOptions
func (_m *mockIbmcloudClientInterface) ListAllDDNSRecordsWithContext(ctx context.Context, listAllDnsRecordsOptions *dnsrecordsv1.ListAllDnsRecordsOptions) (*dnsrecordsv1.ListDnsrecordsResp, *core.DetailedResponse, error) {
ret := _m.Called(ctx, listAllDnsRecordsOptions)
var r0 *dnsrecordsv1.ListDnsrecordsResp
if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.ListAllDnsRecordsOptions) *dnsrecordsv1.ListDnsrecordsResp); ok {
r0 = rf(ctx, listAllDnsRecordsOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnsrecordsv1.ListDnsrecordsResp)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.ListAllDnsRecordsOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, listAllDnsRecordsOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.ListAllDnsRecordsOptions) error); ok {
r2 = rf(ctx, listAllDnsRecordsOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// ListDnszonesWithContext provides a mock function with given fields: ctx, listDnszonesOptions
func (_m *mockIbmcloudClientInterface) ListDnszonesWithContext(ctx context.Context, listDnszonesOptions *dnssvcsv1.ListDnszonesOptions) (*dnssvcsv1.ListDnszones, *core.DetailedResponse, error) {
ret := _m.Called(ctx, listDnszonesOptions)
var r0 *dnssvcsv1.ListDnszones
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.ListDnszonesOptions) *dnssvcsv1.ListDnszones); ok {
r0 = rf(ctx, listDnszonesOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ListDnszones)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.ListDnszonesOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, listDnszonesOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.ListDnszonesOptions) error); ok {
r2 = rf(ctx, listDnszonesOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// ListResourceRecordsWithContext provides a mock function with given fields: ctx, listResourceRecordsOptions
func (_m *mockIbmcloudClientInterface) ListResourceRecordsWithContext(ctx context.Context, listResourceRecordsOptions *dnssvcsv1.ListResourceRecordsOptions) (*dnssvcsv1.ListResourceRecords, *core.DetailedResponse, error) {
ret := _m.Called(ctx, listResourceRecordsOptions)
var r0 *dnssvcsv1.ListResourceRecords
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.ListResourceRecordsOptions) *dnssvcsv1.ListResourceRecords); ok {
r0 = rf(ctx, listResourceRecordsOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ListResourceRecords)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.ListResourceRecordsOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, listResourceRecordsOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.ListResourceRecordsOptions) error); ok {
r2 = rf(ctx, listResourceRecordsOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// NewResourceRecordInputRdataRdataARecord provides a mock function with given fields: ip
func (_m *mockIbmcloudClientInterface) NewResourceRecordInputRdataRdataARecord(ip string) (*dnssvcsv1.ResourceRecordInputRdataRdataARecord, error) {
ret := _m.Called(ip)
var r0 *dnssvcsv1.ResourceRecordInputRdataRdataARecord
if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordInputRdataRdataARecord); ok {
r0 = rf(ip)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordInputRdataRdataARecord)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(ip)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewResourceRecordInputRdataRdataCnameRecord provides a mock function with given fields: cname
func (_m *mockIbmcloudClientInterface) NewResourceRecordInputRdataRdataCnameRecord(cname string) (*dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord, error) {
ret := _m.Called(cname)
var r0 *dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord
if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord); ok {
r0 = rf(cname)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(cname)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewResourceRecordInputRdataRdataTxtRecord provides a mock function with given fields: text
func (_m *mockIbmcloudClientInterface) NewResourceRecordInputRdataRdataTxtRecord(text string) (*dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord, error) {
ret := _m.Called(text)
var r0 *dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord
if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord); ok {
r0 = rf(text)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(text)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewResourceRecordUpdateInputRdataRdataARecord provides a mock function with given fields: ip
func (_m *mockIbmcloudClientInterface) NewResourceRecordUpdateInputRdataRdataARecord(ip string) (*dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord, error) {
ret := _m.Called(ip)
var r0 *dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord
if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord); ok {
r0 = rf(ip)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(ip)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewResourceRecordUpdateInputRdataRdataCnameRecord provides a mock function with given fields: cname
func (_m *mockIbmcloudClientInterface) NewResourceRecordUpdateInputRdataRdataCnameRecord(cname string) (*dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord, error) {
ret := _m.Called(cname)
var r0 *dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord
if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord); ok {
r0 = rf(cname)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(cname)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewResourceRecordUpdateInputRdataRdataTxtRecord provides a mock function with given fields: text
func (_m *mockIbmcloudClientInterface) NewResourceRecordUpdateInputRdataRdataTxtRecord(text string) (*dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord, error) {
ret := _m.Called(text)
var r0 *dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord
if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord); ok {
r0 = rf(text)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(text)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateDNSRecordWithContext provides a mock function with given fields: ctx, updateDnsRecordOptions
func (_m *mockIbmcloudClientInterface) UpdateDNSRecordWithContext(ctx context.Context, updateDnsRecordOptions *dnsrecordsv1.UpdateDnsRecordOptions) (*dnsrecordsv1.DnsrecordResp, *core.DetailedResponse, error) {
ret := _m.Called(ctx, updateDnsRecordOptions)
var r0 *dnsrecordsv1.DnsrecordResp
if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.UpdateDnsRecordOptions) *dnsrecordsv1.DnsrecordResp); ok {
r0 = rf(ctx, updateDnsRecordOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnsrecordsv1.DnsrecordResp)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.UpdateDnsRecordOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, updateDnsRecordOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.UpdateDnsRecordOptions) error); ok {
r2 = rf(ctx, updateDnsRecordOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// UpdateResourceRecordWithContext provides a mock function with given fields: ctx, updateResourceRecordOptions
func (_m *mockIbmcloudClientInterface) UpdateResourceRecordWithContext(ctx context.Context, updateResourceRecordOptions *dnssvcsv1.UpdateResourceRecordOptions) (*dnssvcsv1.ResourceRecord, *core.DetailedResponse, error) {
ret := _m.Called(ctx, updateResourceRecordOptions)
var r0 *dnssvcsv1.ResourceRecord
if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.UpdateResourceRecordOptions) *dnssvcsv1.ResourceRecord); ok {
r0 = rf(ctx, updateResourceRecordOptions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*dnssvcsv1.ResourceRecord)
}
}
var r1 *core.DetailedResponse
if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.UpdateResourceRecordOptions) *core.DetailedResponse); ok {
r1 = rf(ctx, updateResourceRecordOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*core.DetailedResponse)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.UpdateResourceRecordOptions) error); ok {
r2 = rf(ctx, updateResourceRecordOptions)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
type mockSource struct {
mock.Mock
}
// Endpoints provides a mock function with given fields: ctx
func (_m *mockSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
ret := _m.Called(ctx)
var r0 []*endpoint.Endpoint
if rf, ok := ret.Get(0).(func(context.Context) []*endpoint.Endpoint); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*endpoint.Endpoint)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AddEventHandler provides a mock function with given fields: _a0, _a1
func (_m *mockSource) AddEventHandler(_a0 context.Context, _a1 func()) {
_m.Called(_a0, _a1)
}

View File

@ -1,60 +0,0 @@
/*
Copyright 2022 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 cloudapi
import (
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
)
type Action struct {
Service string `json:"service"`
Name string `json:"name"`
ReadOnly bool `json:"readOnly"`
}
var (
/* PrivateDNS */
CreatePrivateZoneRecord = Action{Service: "PrivateDns", Name: "CreatePrivateZoneRecord", ReadOnly: false}
DeletePrivateZoneRecord = Action{Service: "PrivateDns", Name: "DeletePrivateZoneRecord", ReadOnly: false}
ModifyPrivateZoneRecord = Action{Service: "PrivateDns", Name: "ModifyPrivateZoneRecord", ReadOnly: false}
DescribePrivateZoneList = Action{Service: "PrivateDns", Name: "DescribePrivateZoneList", ReadOnly: true}
DescribePrivateZoneRecordList = Action{Service: "PrivateDns", Name: "DescribePrivateZoneRecordList", ReadOnly: true}
/* DNSPod */
DescribeDomainList = Action{Service: "DnsPod", Name: "DescribeDomainList", ReadOnly: true}
DescribeRecordList = Action{Service: "DnsPod", Name: "DescribeRecordList", ReadOnly: true}
CreateRecord = Action{Service: "DnsPod", Name: "CreateRecord", ReadOnly: false}
DeleteRecord = Action{Service: "DnsPod", Name: "DeleteRecord", ReadOnly: false}
ModifyRecord = Action{Service: "DnsPod", Name: "ModifyRecord", ReadOnly: false}
)
type TencentAPIService interface {
// PrivateDNS
CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error)
DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error)
ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error)
DescribePrivateZoneList(request *privatedns.DescribePrivateZoneListRequest) (response *privatedns.DescribePrivateZoneListResponse, err error)
DescribePrivateZoneRecordList(request *privatedns.DescribePrivateZoneRecordListRequest) (response *privatedns.DescribePrivateZoneRecordListResponse, err error)
// DNSPod
DescribeDomainList(request *dnspod.DescribeDomainListRequest) (response *dnspod.DescribeDomainListResponse, err error)
DescribeRecordList(request *dnspod.DescribeRecordListRequest) (response *dnspod.DescribeRecordListResponse, err error)
CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error)
DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error)
ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error)
}

View File

@ -1,81 +0,0 @@
/*
Copyright 2022 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 cloudapi
import (
"fmt"
"sync"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
"go.uber.org/ratelimit"
)
type TencentClientSetService interface {
PrivateDnsCli(action string) *privatedns.Client
DnsPodCli(action string) *dnspod.Client
}
func NewTencentClientSetService(region string, rate int, secretId string, secretKey string, internetEndpoint bool) *defaultTencentClientSetService {
p := &defaultTencentClientSetService{
Region: region,
RateLimit: rate,
}
cred := common.NewCredential(secretId, secretKey)
privatednsProf := profile.NewClientProfile()
if !internetEndpoint {
privatednsProf.HttpProfile.Endpoint = "privatedns.internal.tencentcloudapi.com"
}
p.privateDnsClient, _ = privatedns.NewClient(cred, region, privatednsProf)
dnsPodProf := profile.NewClientProfile()
if !internetEndpoint {
dnsPodProf.HttpProfile.Endpoint = "dnspod.internal.tencentcloudapi.com"
}
p.dnsPodClient, _ = dnspod.NewClient(cred, region, dnsPodProf)
return p
}
type defaultTencentClientSetService struct {
Region string
RateLimit int
RateLimitSyncMap sync.Map
privateDnsClient *privatedns.Client
dnsPodClient *dnspod.Client
}
func (p *defaultTencentClientSetService) checkRateLimit(request, method string) {
action := fmt.Sprintf("%s_%s", request, method)
if rl, ok := p.RateLimitSyncMap.LoadOrStore(action, ratelimit.New(p.RateLimit, ratelimit.WithoutSlack)); ok {
rl.(ratelimit.Limiter).Take()
}
}
func (p *defaultTencentClientSetService) PrivateDnsCli(action string) *privatedns.Client {
p.checkRateLimit("privateDns", action)
return p.privateDnsClient
}
func (p *defaultTencentClientSetService) DnsPodCli(action string) *dnspod.Client {
p.checkRateLimit("dnsPod", action)
return p.dnsPodClient
}

View File

@ -1,234 +0,0 @@
/*
Copyright 2022 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 cloudapi
import (
"math/rand"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
)
type mockAPIService struct {
privateZones []*privatedns.PrivateZone
privateZoneRecords map[string][]*privatedns.PrivateZoneRecord
dnspodDomains []*dnspod.DomainListItem
dnspodRecords map[string][]*dnspod.RecordListItem
}
func NewMockService(privateZones []*privatedns.PrivateZone, privateZoneRecords map[string][]*privatedns.PrivateZoneRecord, dnspodDomains []*dnspod.DomainListItem, dnspodRecords map[string][]*dnspod.RecordListItem) *mockAPIService {
return &mockAPIService{
privateZones: privateZones,
privateZoneRecords: privateZoneRecords,
dnspodDomains: dnspodDomains,
dnspodRecords: dnspodRecords,
}
}
////////////////////////////////////////////////////////////////
// PrivateDns API
////////////////////////////////////////////////////////////////
func (api *mockAPIService) CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) {
randomRecordId := RandStringRunes(8)
if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist {
api.privateZoneRecords[*request.ZoneId] = make([]*privatedns.PrivateZoneRecord, 0)
}
if request.TTL == nil {
request.TTL = common.Int64Ptr(300)
}
api.privateZoneRecords[*request.ZoneId] = append(api.privateZoneRecords[*request.ZoneId], &privatedns.PrivateZoneRecord{
RecordId: common.StringPtr(randomRecordId),
ZoneId: request.ZoneId,
SubDomain: request.SubDomain,
RecordType: request.RecordType,
RecordValue: request.RecordValue,
TTL: request.TTL,
})
return response, nil
}
func (api *mockAPIService) DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) {
result := make([]*privatedns.PrivateZoneRecord, 0)
if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist {
return response, nil
}
for _, privateZoneRecord := range api.privateZoneRecords[*request.ZoneId] {
deleteflag := false
if len(request.RecordIdSet) != 0 {
for _, recordId := range request.RecordIdSet {
if *privateZoneRecord.RecordId == *recordId {
deleteflag = true
break
}
}
}
if request.RecordId != nil && *request.RecordId == *privateZoneRecord.RecordId {
deleteflag = true
}
if !deleteflag {
result = append(result, privateZoneRecord)
}
}
api.privateZoneRecords[*request.ZoneId] = result
return response, nil
}
func (api *mockAPIService) ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) {
if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist {
return response, nil
}
for _, privateZoneRecord := range api.privateZoneRecords[*request.ZoneId] {
if *privateZoneRecord.RecordId != *request.RecordId {
continue
}
privateZoneRecord.ZoneId = request.ZoneId
privateZoneRecord.SubDomain = request.SubDomain
privateZoneRecord.RecordType = request.RecordType
privateZoneRecord.RecordValue = request.RecordValue
privateZoneRecord.TTL = request.TTL
}
return response, nil
}
func (api *mockAPIService) DescribePrivateZoneList(request *privatedns.DescribePrivateZoneListRequest) (response *privatedns.DescribePrivateZoneListResponse, err error) {
response = privatedns.NewDescribePrivateZoneListResponse()
response.Response = &privatedns.DescribePrivateZoneListResponseParams{
TotalCount: common.Int64Ptr(int64(len(api.privateZones))),
PrivateZoneSet: api.privateZones,
}
return response, nil
}
func (api *mockAPIService) DescribePrivateZoneRecordList(request *privatedns.DescribePrivateZoneRecordListRequest) (response *privatedns.DescribePrivateZoneRecordListResponse, err error) {
response = privatedns.NewDescribePrivateZoneRecordListResponse()
response.Response = &privatedns.DescribePrivateZoneRecordListResponseParams{}
if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist {
response.Response.TotalCount = common.Int64Ptr(0)
response.Response.RecordSet = make([]*privatedns.PrivateZoneRecord, 0)
return response, nil
}
response.Response.TotalCount = common.Int64Ptr(int64(len(api.privateZoneRecords[*request.ZoneId])))
response.Response.RecordSet = api.privateZoneRecords[*request.ZoneId]
return response, nil
}
////////////////////////////////////////////////////////////////
// DnsPod API
////////////////////////////////////////////////////////////////
func (api *mockAPIService) DescribeDomainList(request *dnspod.DescribeDomainListRequest) (response *dnspod.DescribeDomainListResponse, err error) {
response = dnspod.NewDescribeDomainListResponse()
response.Response = &dnspod.DescribeDomainListResponseParams{
DomainCountInfo: &dnspod.DomainCountInfo{
AllTotal: common.Uint64Ptr(uint64(len(api.dnspodDomains))),
},
DomainList: api.dnspodDomains,
}
response.Response.DomainList = api.dnspodDomains
response.Response.DomainCountInfo = &dnspod.DomainCountInfo{
AllTotal: common.Uint64Ptr(uint64(len(api.dnspodDomains))),
}
return response, nil
}
func (api *mockAPIService) DescribeRecordList(request *dnspod.DescribeRecordListRequest) (response *dnspod.DescribeRecordListResponse, err error) {
response = dnspod.NewDescribeRecordListResponse()
response.Response = &dnspod.DescribeRecordListResponseParams{}
if _, exist := api.dnspodRecords[*request.Domain]; !exist {
response.Response.RecordList = make([]*dnspod.RecordListItem, 0)
response.Response.RecordCountInfo = &dnspod.RecordCountInfo{
TotalCount: common.Uint64Ptr(uint64(0)),
}
return response, nil
}
response.Response.RecordList = api.dnspodRecords[*request.Domain]
response.Response.RecordCountInfo = &dnspod.RecordCountInfo{
TotalCount: common.Uint64Ptr(uint64(len(api.dnspodRecords[*request.Domain]))),
}
return response, nil
}
func (api *mockAPIService) CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) {
randomRecordId := RandUint64()
if _, exist := api.dnspodRecords[*request.Domain]; !exist {
api.dnspodRecords[*request.Domain] = make([]*dnspod.RecordListItem, 0)
}
if request.TTL == nil {
request.TTL = common.Uint64Ptr(300)
}
api.dnspodRecords[*request.Domain] = append(api.dnspodRecords[*request.Domain], &dnspod.RecordListItem{
RecordId: common.Uint64Ptr(randomRecordId),
Value: request.Value,
TTL: request.TTL,
Name: request.SubDomain,
Line: request.RecordLine,
LineId: request.RecordLineId,
Type: request.RecordType,
})
return response, nil
}
func (api *mockAPIService) DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) {
result := make([]*dnspod.RecordListItem, 0)
if _, exist := api.dnspodRecords[*request.Domain]; !exist {
return response, nil
}
for _, zoneRecord := range api.dnspodRecords[*request.Domain] {
deleteflag := false
if request.RecordId != nil && *request.RecordId == *zoneRecord.RecordId {
deleteflag = true
}
if !deleteflag {
result = append(result, zoneRecord)
}
}
api.dnspodRecords[*request.Domain] = result
return response, nil
}
func (api *mockAPIService) ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) {
if _, exist := api.dnspodRecords[*request.Domain]; !exist {
return response, nil
}
for _, zoneRecord := range api.dnspodRecords[*request.Domain] {
if *zoneRecord.RecordId != *request.RecordId {
continue
}
zoneRecord.Type = request.RecordType
zoneRecord.Name = request.SubDomain
zoneRecord.Value = request.Value
zoneRecord.TTL = request.TTL
}
return response, nil
}
var letterRunes = []byte("abcdefghijklmnopqrstuvwxyz")
func RandStringRunes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func RandUint64() uint64 {
return rand.Uint64()
}

View File

@ -1,78 +0,0 @@
/*
Copyright 2022 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 cloudapi
import (
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
)
type readonlyAPIService struct {
defaultTencentAPIService
}
func NewReadOnlyAPIService(region string, rate int, secretId string, secretKey string, internetEndpoint bool) *readonlyAPIService {
apiService := NewTencentAPIService(region, rate, secretId, secretKey, internetEndpoint)
tencentAPIService := &readonlyAPIService{
*apiService,
}
return tencentAPIService
}
////////////////////////////////////////////////////////////////
// PrivateDns API
////////////////////////////////////////////////////////////////
func (api *readonlyAPIService) CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) {
apiAction := CreatePrivateZoneRecord
APIRecord(apiAction, JsonWrapper(request), "dryRun")
return response, nil
}
func (api *readonlyAPIService) DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) {
apiAction := DeletePrivateZoneRecord
APIRecord(apiAction, JsonWrapper(request), "dryRun")
return response, nil
}
func (api *readonlyAPIService) ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) {
apiAction := ModifyPrivateZoneRecord
APIRecord(apiAction, JsonWrapper(request), "dryRun")
return response, nil
}
////////////////////////////////////////////////////////////////
// DnsPod API
////////////////////////////////////////////////////////////////
func (api *readonlyAPIService) CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) {
apiAction := CreateRecord
APIRecord(apiAction, JsonWrapper(request), "dryRun")
return response, nil
}
func (api *readonlyAPIService) DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) {
apiAction := DeleteRecord
APIRecord(apiAction, JsonWrapper(request), "dryRun")
return response, nil
}
func (api *readonlyAPIService) ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) {
apiAction := ModifyRecord
APIRecord(apiAction, JsonWrapper(request), "dryRun")
return response, nil
}

View File

@ -1,278 +0,0 @@
/*
Copyright 2022 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 cloudapi
import (
"encoding/json"
"errors"
"fmt"
"net"
"time"
log "github.com/sirupsen/logrus"
tclouderrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
)
type defaultTencentAPIService struct {
RetryDefault int
TaskCheckInterval time.Duration
ClientSetService TencentClientSetService
}
func NewTencentAPIService(region string, rate int, secretId string, secretKey string, internetEndpoint bool) *defaultTencentAPIService {
tencentAPIService := &defaultTencentAPIService{
RetryDefault: 3,
TaskCheckInterval: 3 * time.Second,
ClientSetService: NewTencentClientSetService(region, rate, secretId, secretKey, internetEndpoint),
}
return tencentAPIService
}
// //////////////////////////////////////////////////////////////
// PrivateDns API
// //////////////////////////////////////////////////////////////
func (api *defaultTencentAPIService) CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) {
apiAction := CreatePrivateZoneRecord
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.PrivateDnsCli(apiAction.Name)
if response, err = client.CreatePrivateZoneRecord(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) {
apiAction := DeletePrivateZoneRecord
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.PrivateDnsCli(apiAction.Name)
if response, err = client.DeletePrivateZoneRecord(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) {
apiAction := ModifyPrivateZoneRecord
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.PrivateDnsCli(apiAction.Name)
if response, err = client.ModifyPrivateZoneRecord(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) DescribePrivateZoneList(request *privatedns.DescribePrivateZoneListRequest) (response *privatedns.DescribePrivateZoneListResponse, err error) {
apiAction := DescribePrivateZoneList
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.PrivateDnsCli(apiAction.Name)
if response, err = client.DescribePrivateZoneList(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) DescribePrivateZoneRecordList(request *privatedns.DescribePrivateZoneRecordListRequest) (response *privatedns.DescribePrivateZoneRecordListResponse, err error) {
apiAction := DescribePrivateZoneRecordList
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.PrivateDnsCli(apiAction.Name)
if response, err = client.DescribePrivateZoneRecordList(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
// //////////////////////////////////////////////////////////////
// DnsPod API
// //////////////////////////////////////////////////////////////
func (api *defaultTencentAPIService) DescribeDomainList(request *dnspod.DescribeDomainListRequest) (response *dnspod.DescribeDomainListResponse, err error) {
apiAction := DescribeDomainList
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.DnsPodCli(apiAction.Name)
if response, err = client.DescribeDomainList(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) DescribeRecordList(request *dnspod.DescribeRecordListRequest) (response *dnspod.DescribeRecordListResponse, err error) {
apiAction := DescribeRecordList
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.DnsPodCli(apiAction.Name)
if response, err = client.DescribeRecordList(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) {
apiAction := CreateRecord
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.DnsPodCli(apiAction.Name)
if response, err = client.CreateRecord(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) {
apiAction := DeleteRecord
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.DnsPodCli(apiAction.Name)
if response, err = client.DeleteRecord(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
func (api *defaultTencentAPIService) ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) {
apiAction := ModifyRecord
for times := 1; times <= api.RetryDefault; times++ {
client := api.ClientSetService.DnsPodCli(apiAction.Name)
if response, err = client.ModifyRecord(request); err != nil {
requestJson := JsonWrapper(request)
if retry := dealWithError(apiAction, requestJson, err); retry || times == api.RetryDefault {
APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err)
return nil, err
}
continue
}
break
}
APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response))
return response, nil
}
// //////////////////////////////////////////////////////////////
// API Error Report
// //////////////////////////////////////////////////////////////
func dealWithError(action Action, request string, err error) bool {
log.Errorf("dealWithError %s/%s request: %s, error: %s.", action.Service, action.Name, request, err.Error())
sdkError := &tclouderrors.TencentCloudSDKError{}
if errors.As(err, &sdkError) {
switch sdkError.Code {
case "RequestLimitExceeded":
return true
case "InternalError", "ClientError.HttpStatusCodeError":
return false
case "ClientError.NetworkError":
return false
case "AuthFailure.UnauthorizedOperation", "UnauthorizedOperation.CamNoAuth":
return false
}
return false
}
return errors.As(err, new(net.Error))
}
func APIErrorRecord(apiAction Action, request string, response string, err error) {
log.Infof("APIError API: %s/%s Request: %s, Response: %s, Error: %s", apiAction.Service, apiAction.Name, request, response, err.Error())
}
func APIRecord(apiAction Action, request string, response string) {
message := fmt.Sprintf("APIRecord API: %s/%s Request: %s, Response: %s", apiAction.Service, apiAction.Name, request, response)
if apiAction.ReadOnly {
// log.Info(message)
} else {
log.Info(message)
}
}
func JsonWrapper(obj interface{}) string {
if jsonStr, jsonErr := json.Marshal(obj); jsonErr == nil {
return string(jsonStr)
}
return "json_format_error"
}

View File

@ -1,281 +0,0 @@
/*
Copyright 2022 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 tencentcloud
import (
"fmt"
"strconv"
"strings"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)
// DnsPod For Public Dns
func (p *TencentCloudProvider) dnsRecords() ([]*endpoint.Endpoint, error) {
recordsList, err := p.recordsForDNS()
if err != nil {
return nil, err
}
endpoints := make([]*endpoint.Endpoint, 0)
recordMap := groupDomainRecordList(recordsList)
for _, recordList := range recordMap {
name := getDnsDomain(*recordList.RecordList[0].Name, *recordList.Domain.Name)
recordType := *recordList.RecordList[0].Type
ttl := *recordList.RecordList[0].TTL
var targets []string
for _, record := range recordList.RecordList {
targets = append(targets, *record.Value)
}
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...))
}
return endpoints, nil
}
func (p *TencentCloudProvider) recordsForDNS() (map[uint64]*RecordListGroup, error) {
domainList, err := p.getDomainList()
if err != nil {
return nil, err
}
recordListGroup := make(map[uint64]*RecordListGroup, 0)
for _, domain := range domainList {
records, err := p.getDomainRecordList(*domain.Name)
if err != nil {
return nil, err
}
for _, record := range records {
if *record.Type == "TXT" && strings.HasPrefix(*record.Value, "heritage=") {
record.Value = common.StringPtr(fmt.Sprintf(`"%s"`, *record.Value))
}
}
recordListGroup[*domain.DomainId] = &RecordListGroup{
Domain: domain,
RecordList: records,
}
}
return recordListGroup, nil
}
func (p *TencentCloudProvider) getDomainList() ([]*dnspod.DomainListItem, error) {
request := dnspod.NewDescribeDomainListRequest()
request.Offset = common.Int64Ptr(0)
request.Limit = common.Int64Ptr(3000)
domainList := make([]*dnspod.DomainListItem, 0)
totalCount := int64(100)
for *request.Offset < totalCount {
response, err := p.apiService.DescribeDomainList(request)
if err != nil {
return nil, err
}
if len(response.Response.DomainList) > 0 {
if !p.domainFilter.IsConfigured() {
domainList = append(domainList, response.Response.DomainList...)
} else {
for _, domain := range response.Response.DomainList {
if p.domainFilter.Match(*domain.Name) {
domainList = append(domainList, domain)
}
}
}
}
totalCount = int64(*response.Response.DomainCountInfo.AllTotal)
request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.DomainList)))
}
return domainList, nil
}
func (p *TencentCloudProvider) getDomainRecordList(domain string) ([]*dnspod.RecordListItem, error) {
request := dnspod.NewDescribeRecordListRequest()
request.Domain = common.StringPtr(domain)
request.Offset = common.Uint64Ptr(0)
request.Limit = common.Uint64Ptr(3000)
domainList := make([]*dnspod.RecordListItem, 0)
totalCount := uint64(100)
for *request.Offset < totalCount {
response, err := p.apiService.DescribeRecordList(request)
if err != nil {
return nil, err
}
if len(response.Response.RecordList) > 0 {
for _, record := range response.Response.RecordList {
if *record.Name == "@" && *record.Type == "NS" { // Special Record, Skip it.
continue
}
domainList = append(domainList, record)
}
}
totalCount = *response.Response.RecordCountInfo.TotalCount
request.Offset = common.Uint64Ptr(*request.Offset + uint64(len(response.Response.RecordList)))
}
return domainList, nil
}
type RecordListGroup struct {
Domain *dnspod.DomainListItem
RecordList []*dnspod.RecordListItem
}
func (p *TencentCloudProvider) applyChangesForDNS(changes *plan.Changes) error {
recordsGroupMap, err := p.recordsForDNS()
if err != nil {
return err
}
zoneNameIDMapper := provider.ZoneIDName{}
for _, recordsGroup := range recordsGroupMap {
if recordsGroup.Domain.DomainId != nil {
zoneNameIDMapper.Add(strconv.FormatUint(*recordsGroup.Domain.DomainId, 10), *recordsGroup.Domain.Name)
}
}
// Apply Change Delete
deleteEndpoints := make(map[string][]uint64)
for _, change := range [][]*endpoint.Endpoint{changes.Delete, changes.UpdateOld} {
for _, deleteChange := range change {
if zoneId, _ := zoneNameIDMapper.FindZone(deleteChange.DNSName); zoneId != "" {
zoneIdString, _ := strconv.ParseUint(zoneId, 10, 64)
recordListGroup := recordsGroupMap[zoneIdString]
for _, domainRecord := range recordListGroup.RecordList {
subDomain := getSubDomain(*recordListGroup.Domain.Name, deleteChange)
if *domainRecord.Name == subDomain && *domainRecord.Type == deleteChange.RecordType {
for _, target := range deleteChange.Targets {
if *domainRecord.Value == target {
if _, exist := deleteEndpoints[*recordListGroup.Domain.Name]; !exist {
deleteEndpoints[*recordListGroup.Domain.Name] = make([]uint64, 0)
}
deleteEndpoints[*recordListGroup.Domain.Name] = append(deleteEndpoints[*recordListGroup.Domain.Name], *domainRecord.RecordId)
}
}
}
}
}
}
}
if err := p.deleteRecords(deleteEndpoints); err != nil {
return err
}
// Apply Change Create
createEndpoints := make(map[string][]*endpoint.Endpoint)
for zoneId := range zoneNameIDMapper {
createEndpoints[zoneId] = make([]*endpoint.Endpoint, 0)
}
for _, change := range [][]*endpoint.Endpoint{changes.Create, changes.UpdateNew} {
for _, createChange := range change {
if zoneId, _ := zoneNameIDMapper.FindZone(createChange.DNSName); zoneId != "" {
createEndpoints[zoneId] = append(createEndpoints[zoneId], createChange)
}
}
}
if err := p.createRecord(recordsGroupMap, createEndpoints); err != nil {
return err
}
return nil
}
func (p *TencentCloudProvider) createRecord(zoneMap map[uint64]*RecordListGroup, endpointsMap map[string][]*endpoint.Endpoint) error {
for zoneId, endpoints := range endpointsMap {
zoneIdString, _ := strconv.ParseUint(zoneId, 10, 64)
domain := zoneMap[zoneIdString]
for _, endpoint := range endpoints {
for _, target := range endpoint.Targets {
if endpoint.RecordType == "TXT" && strings.HasPrefix(target, `"heritage=`) {
target = strings.Trim(target, `"`)
}
if err := p.createRecords(domain.Domain, endpoint, target); err != nil {
return err
}
}
}
}
return nil
}
func (p *TencentCloudProvider) createRecords(domain *dnspod.DomainListItem, endpoint *endpoint.Endpoint, target string) error {
request := dnspod.NewCreateRecordRequest()
request.Domain = common.StringPtr(*domain.Name)
request.RecordType = common.StringPtr(endpoint.RecordType)
request.Value = common.StringPtr(target)
request.SubDomain = common.StringPtr(getSubDomain(*domain.Name, endpoint))
if endpoint.RecordTTL.IsConfigured() {
request.TTL = common.Uint64Ptr(uint64(endpoint.RecordTTL))
}
request.RecordLine = common.StringPtr("默认")
if _, err := p.apiService.CreateRecord(request); err != nil {
return err
}
return nil
}
func (p *TencentCloudProvider) deleteRecords(RecordIdsMap map[string][]uint64) error {
for domain, recordIds := range RecordIdsMap {
if len(recordIds) == 0 {
continue
}
if err := p.deleteRecord(domain, recordIds); err != nil {
return err
}
}
return nil
}
func (p *TencentCloudProvider) deleteRecord(domain string, recordIds []uint64) error {
request := dnspod.NewDeleteRecordRequest()
request.Domain = common.StringPtr(domain)
for _, recordId := range recordIds {
request.RecordId = common.Uint64Ptr(recordId)
if _, err := p.apiService.DeleteRecord(request); err != nil {
return err
}
}
return nil
}
func groupDomainRecordList(recordListGroup map[uint64]*RecordListGroup) (endpointMap map[string]*RecordListGroup) {
endpointMap = make(map[string]*RecordListGroup)
for _, recordGroup := range recordListGroup {
for _, record := range recordGroup.RecordList {
key := fmt.Sprintf("%s:%s.%s", *record.Type, *record.Name, *recordGroup.Domain.Name)
if *record.Name == TencentCloudEmptyPrefix {
key = fmt.Sprintf("%s:%s", *record.Type, *recordGroup.Domain.Name)
}
if _, exist := endpointMap[key]; !exist {
endpointMap[key] = &RecordListGroup{
Domain: recordGroup.Domain,
RecordList: make([]*dnspod.RecordListItem, 0),
}
}
endpointMap[key].RecordList = append(endpointMap[key].RecordList, record)
}
}
return endpointMap
}

View File

@ -1,319 +0,0 @@
/*
Copyright 2022 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 tencentcloud
import (
"fmt"
"strings"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)
// PrivateZone For Internal Dns
func (p *TencentCloudProvider) privateZoneRecords() ([]*endpoint.Endpoint, error) {
privateZones, err := p.recordForPrivateZone()
if err != nil {
return nil, err
}
endpoints := make([]*endpoint.Endpoint, 0)
recordMap := groupPrivateZoneRecords(privateZones)
for _, recordList := range recordMap {
name := getDnsDomain(*recordList.RecordList[0].SubDomain, *recordList.Zone.Domain)
recordType := *recordList.RecordList[0].RecordType
ttl := *recordList.RecordList[0].TTL
var targets []string
for _, record := range recordList.RecordList {
targets = append(targets, *record.RecordValue)
}
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...))
}
return endpoints, nil
}
func (p *TencentCloudProvider) recordForPrivateZone() (map[string]*PrivateZoneRecordListGroup, error) {
privateZones, err := p.getPrivateZones()
if err != nil {
return nil, err
}
recordListGroup := make(map[string]*PrivateZoneRecordListGroup, 0)
for _, zone := range privateZones {
records, err := p.getPrivateZoneRecords(*zone.ZoneId)
if err != nil {
return nil, err
}
for _, record := range records {
if *record.RecordType == "TXT" && strings.HasPrefix(*record.RecordValue, "heritage=") {
record.RecordValue = common.StringPtr(fmt.Sprintf("\"%s\"", *record.RecordValue))
}
}
recordListGroup[*zone.ZoneId] = &PrivateZoneRecordListGroup{
Zone: zone,
RecordList: records,
}
}
return recordListGroup, nil
}
func (p *TencentCloudProvider) getPrivateZones() ([]*privatedns.PrivateZone, error) {
filters := make([]*privatedns.Filter, 1)
filters[0] = &privatedns.Filter{
Name: common.StringPtr("Vpc"),
Values: []*string{
common.StringPtr(p.vpcID),
},
}
if p.zoneIDFilter.IsConfigured() {
zoneIDs := make([]*string, len(p.zoneIDFilter.ZoneIDs))
for index, zoneId := range p.zoneIDFilter.ZoneIDs {
zoneIDs[index] = common.StringPtr(zoneId)
}
filters = append(filters, &privatedns.Filter{
Name: common.StringPtr("ZoneId"),
Values: zoneIDs,
})
}
request := privatedns.NewDescribePrivateZoneListRequest()
request.Filters = filters
request.Offset = common.Int64Ptr(0)
request.Limit = common.Int64Ptr(100)
privateZones := make([]*privatedns.PrivateZone, 0)
totalCount := int64(100)
for *request.Offset < totalCount {
response, err := p.apiService.DescribePrivateZoneList(request)
if err != nil {
return nil, err
}
if len(response.Response.PrivateZoneSet) > 0 {
privateZones = append(privateZones, response.Response.PrivateZoneSet...)
}
totalCount = *response.Response.TotalCount
request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.PrivateZoneSet)))
}
privateZonesFilter := make([]*privatedns.PrivateZone, 0)
for _, privateZone := range privateZones {
if !p.domainFilter.Match(*privateZone.Domain) {
continue
}
privateZonesFilter = append(privateZonesFilter, privateZone)
}
return privateZonesFilter, nil
}
func (p *TencentCloudProvider) getPrivateZoneRecords(zoneId string) ([]*privatedns.PrivateZoneRecord, error) {
request := privatedns.NewDescribePrivateZoneRecordListRequest()
request.ZoneId = common.StringPtr(zoneId)
request.Offset = common.Int64Ptr(0)
request.Limit = common.Int64Ptr(100)
privateZoneRecords := make([]*privatedns.PrivateZoneRecord, 0)
totalCount := int64(100)
for *request.Offset < totalCount {
response, err := p.apiService.DescribePrivateZoneRecordList(request)
if err != nil {
return nil, err
}
if len(response.Response.RecordSet) > 0 {
privateZoneRecords = append(privateZoneRecords, response.Response.RecordSet...)
}
totalCount = *response.Response.TotalCount
request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.RecordSet)))
}
return privateZoneRecords, nil
}
type PrivateZoneRecordListGroup struct {
Zone *privatedns.PrivateZone
RecordList []*privatedns.PrivateZoneRecord
}
// Returns nil if the operation was successful or an error if the operation failed.
func (p *TencentCloudProvider) applyChangesForPrivateZone(changes *plan.Changes) error {
zoneGroups, err := p.recordForPrivateZone()
if err != nil {
return err
}
// In PrivateDns Service. A Zone has at least one record. The last rule cannot be deleted.
for _, zoneGroup := range zoneGroups {
if !containsBaseRecord(zoneGroup.RecordList) {
err := p.createPrivateZoneRecord(zoneGroup.Zone, &endpoint.Endpoint{
DNSName: *zoneGroup.Zone.Domain,
RecordType: "TXT",
}, "tencent_provider_record")
if err != nil {
return err
}
}
}
zoneNameIDMapper := provider.ZoneIDName{}
for _, zoneGroup := range zoneGroups {
if zoneGroup.Zone.ZoneId != nil {
zoneNameIDMapper.Add(*zoneGroup.Zone.ZoneId, *zoneGroup.Zone.Domain)
}
}
// Apply Change Delete
deleteEndpoints := make(map[string][]string)
for _, change := range [][]*endpoint.Endpoint{changes.Delete, changes.UpdateOld} {
for _, deleteChange := range change {
if zoneId, _ := zoneNameIDMapper.FindZone(deleteChange.DNSName); zoneId != "" {
zoneGroup := zoneGroups[zoneId]
for _, zoneRecord := range zoneGroup.RecordList {
subDomain := getSubDomain(*zoneGroup.Zone.Domain, deleteChange)
if *zoneRecord.SubDomain == subDomain && *zoneRecord.RecordType == deleteChange.RecordType {
for _, target := range deleteChange.Targets {
if *zoneRecord.RecordValue == target {
if _, exist := deleteEndpoints[zoneId]; !exist {
deleteEndpoints[zoneId] = make([]string, 0)
}
deleteEndpoints[zoneId] = append(deleteEndpoints[zoneId], *zoneRecord.RecordId)
}
}
}
}
}
}
}
if err := p.deletePrivateZoneRecords(deleteEndpoints); err != nil {
return err
}
// Apply Change Create
createEndpoints := make(map[string][]*endpoint.Endpoint)
for _, change := range [][]*endpoint.Endpoint{changes.Create, changes.UpdateNew} {
for _, createChange := range change {
if zoneId, _ := zoneNameIDMapper.FindZone(createChange.DNSName); zoneId != "" {
if _, exist := createEndpoints[zoneId]; !exist {
createEndpoints[zoneId] = make([]*endpoint.Endpoint, 0)
}
createEndpoints[zoneId] = append(createEndpoints[zoneId], createChange)
}
}
}
if err := p.createPrivateZoneRecords(zoneGroups, createEndpoints); err != nil {
return err
}
return nil
}
func containsBaseRecord(records []*privatedns.PrivateZoneRecord) bool {
for _, record := range records {
if *record.SubDomain == TencentCloudEmptyPrefix && *record.RecordType == "TXT" && *record.RecordValue == "tencent_provider_record" {
return true
}
}
return false
}
func (p *TencentCloudProvider) createPrivateZoneRecords(zoneGroups map[string]*PrivateZoneRecordListGroup, endpointsMap map[string][]*endpoint.Endpoint) error {
for zoneId, endpoints := range endpointsMap {
zoneGroup := zoneGroups[zoneId]
for _, endpoint := range endpoints {
for _, target := range endpoint.Targets {
if endpoint.RecordType == "TXT" && strings.HasPrefix(target, "\"heritage=") {
target = strings.Trim(target, "\"")
}
if err := p.createPrivateZoneRecord(zoneGroup.Zone, endpoint, target); err != nil {
return err
}
}
}
}
return nil
}
func (p *TencentCloudProvider) deletePrivateZoneRecords(zoneRecordIdsMap map[string][]string) error {
for zoneId, zoneRecordIds := range zoneRecordIdsMap {
if len(zoneRecordIds) == 0 {
continue
}
if err := p.deletePrivateZoneRecord(zoneId, zoneRecordIds); err != nil {
return err
}
}
return nil
}
func (p *TencentCloudProvider) createPrivateZoneRecord(zone *privatedns.PrivateZone, endpoint *endpoint.Endpoint, target string) error {
request := privatedns.NewCreatePrivateZoneRecordRequest()
request.ZoneId = common.StringPtr(*zone.ZoneId)
request.RecordType = common.StringPtr(endpoint.RecordType)
request.RecordValue = common.StringPtr(target)
request.SubDomain = common.StringPtr(getSubDomain(*zone.Domain, endpoint))
if endpoint.RecordTTL.IsConfigured() {
request.TTL = common.Int64Ptr(int64(endpoint.RecordTTL))
}
if _, err := p.apiService.CreatePrivateZoneRecord(request); err != nil {
return err
}
return nil
}
func (p *TencentCloudProvider) deletePrivateZoneRecord(zoneId string, zoneRecordIds []string) error {
recordIds := make([]*string, len(zoneRecordIds))
for index, recordId := range zoneRecordIds {
recordIds[index] = common.StringPtr(recordId)
}
request := privatedns.NewDeletePrivateZoneRecordRequest()
request.ZoneId = common.StringPtr(zoneId)
request.RecordIdSet = recordIds
if _, err := p.apiService.DeletePrivateZoneRecord(request); err != nil {
return err
}
return nil
}
func groupPrivateZoneRecords(zoneRecords map[string]*PrivateZoneRecordListGroup) (endpointMap map[string]*PrivateZoneRecordListGroup) {
endpointMap = make(map[string]*PrivateZoneRecordListGroup)
for _, recordGroup := range zoneRecords {
for _, record := range recordGroup.RecordList {
key := fmt.Sprintf("%s:%s.%s", *record.RecordType, *record.SubDomain, *recordGroup.Zone.Domain)
if *record.SubDomain == TencentCloudEmptyPrefix {
key = fmt.Sprintf("%s:%s", *record.RecordType, *recordGroup.Zone.Domain)
}
if _, exist := endpointMap[key]; !exist {
endpointMap[key] = &PrivateZoneRecordListGroup{
Zone: recordGroup.Zone,
RecordList: make([]*privatedns.PrivateZoneRecord, 0),
}
}
endpointMap[key].RecordList = append(endpointMap[key].RecordList, record)
}
}
return endpointMap
}

View File

@ -1,121 +0,0 @@
/*
Copyright 2022 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 tencentcloud
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
"sigs.k8s.io/external-dns/provider/tencentcloud/cloudapi"
)
const (
TencentCloudEmptyPrefix = "@"
DefaultAPIRate = 9
)
func NewTencentCloudProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, configFile string, zoneType string, dryRun bool) (*TencentCloudProvider, error) {
cfg := tencentCloudConfig{}
if configFile != "" {
contents, err := os.ReadFile(configFile)
if err != nil {
return nil, fmt.Errorf("failed to read Tencent Cloud config file '%s': %w", configFile, err)
}
err = json.Unmarshal(contents, &cfg)
if err != nil {
return nil, fmt.Errorf("failed to parse Tencent Cloud config file '%s': %w", configFile, err)
}
}
var apiService cloudapi.TencentAPIService = cloudapi.NewTencentAPIService(cfg.RegionId, DefaultAPIRate, cfg.SecretId, cfg.SecretKey, cfg.InternetEndpoint)
if dryRun {
apiService = cloudapi.NewReadOnlyAPIService(cfg.RegionId, DefaultAPIRate, cfg.SecretId, cfg.SecretKey, cfg.InternetEndpoint)
}
tencentCloudProvider := &TencentCloudProvider{
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
apiService: apiService,
vpcID: cfg.VPCId,
privateZone: zoneType == "private",
}
return tencentCloudProvider, nil
}
type TencentCloudProvider struct {
provider.BaseProvider
apiService cloudapi.TencentAPIService
domainFilter endpoint.DomainFilter
zoneIDFilter provider.ZoneIDFilter // Private Zone only
vpcID string // Private Zone only
privateZone bool
}
type tencentCloudConfig struct {
RegionId string `json:"regionId" yaml:"regionId"`
SecretId string `json:"secretId" yaml:"secretId"`
SecretKey string `json:"secretKey" yaml:"secretKey"`
VPCId string `json:"vpcId" yaml:"vpcId"`
InternetEndpoint bool `json:"internetEndpoint" yaml:"internetEndpoint"`
}
func (p *TencentCloudProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
if p.privateZone {
return p.privateZoneRecords()
}
return p.dnsRecords()
}
func (p *TencentCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
if !changes.HasChanges() {
return nil
}
log.Infof("apply changes. %s", cloudapi.JsonWrapper(changes))
if p.privateZone {
return p.applyChangesForPrivateZone(changes)
}
return p.applyChangesForDNS(changes)
}
func getSubDomain(domain string, endpoint *endpoint.Endpoint) string {
name := endpoint.DNSName
name = name[:len(name)-len(domain)]
name = strings.TrimSuffix(name, ".")
if name == "" {
return TencentCloudEmptyPrefix
}
return name
}
func getDnsDomain(subDomain string, domain string) string {
if subDomain == TencentCloudEmptyPrefix {
return domain
}
return subDomain + "." + domain
}

View File

@ -1,403 +0,0 @@
/*
Copyright 2022 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 tencentcloud
import (
"context"
"testing"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
"sigs.k8s.io/external-dns/provider/tencentcloud/cloudapi"
privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028"
)
func NewMockTencentCloudProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneType string) *TencentCloudProvider {
cfg := tencentCloudConfig{
RegionId: "ap-shanghai",
VPCId: "vpc-abcdefg",
}
zoneId1 := common.StringPtr(cloudapi.RandStringRunes(8))
privateZones := []*privatedns.PrivateZone{
{
ZoneId: zoneId1,
Domain: common.StringPtr("external-dns-test.com"),
VpcSet: []*privatedns.VpcInfo{
{
UniqVpcId: common.StringPtr("vpc-abcdefg"),
Region: common.StringPtr("ap-shanghai"),
},
},
},
}
zoneRecordId1 := common.StringPtr(cloudapi.RandStringRunes(8))
zoneRecordId2 := common.StringPtr(cloudapi.RandStringRunes(8))
privateZoneRecords := map[string][]*privatedns.PrivateZoneRecord{
*zoneId1: {
{
ZoneId: zoneId1,
RecordId: zoneRecordId1,
SubDomain: common.StringPtr("nginx"),
RecordType: common.StringPtr("TXT"),
RecordValue: common.StringPtr("heritage=external-dns,external-dns/owner=default"),
TTL: common.Int64Ptr(300),
},
{
ZoneId: zoneId1,
RecordId: zoneRecordId2,
SubDomain: common.StringPtr("nginx"),
RecordType: common.StringPtr("A"),
RecordValue: common.StringPtr("10.10.10.10"),
TTL: common.Int64Ptr(300),
},
},
}
dnsDomainId1 := common.Uint64Ptr(cloudapi.RandUint64())
dnsPodDomains := []*dnspod.DomainListItem{
{
DomainId: dnsDomainId1,
Name: common.StringPtr("external-dns-test.com"),
},
}
dnsDomainRecordId1 := common.Uint64Ptr(cloudapi.RandUint64())
dnsDomainRecordId2 := common.Uint64Ptr(cloudapi.RandUint64())
dnspodRecords := map[string][]*dnspod.RecordListItem{
"external-dns-test.com": {
{
RecordId: dnsDomainRecordId1,
Value: common.StringPtr("heritage=external-dns,external-dns/owner=default"),
Name: common.StringPtr("nginx"),
Type: common.StringPtr("TXT"),
TTL: common.Uint64Ptr(300),
},
{
RecordId: dnsDomainRecordId2,
Name: common.StringPtr("nginx"),
Type: common.StringPtr("A"),
Value: common.StringPtr("10.10.10.10"),
TTL: common.Uint64Ptr(300),
},
},
}
var apiService cloudapi.TencentAPIService = cloudapi.NewMockService(privateZones, privateZoneRecords, dnsPodDomains, dnspodRecords)
tencentCloudProvider := &TencentCloudProvider{
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
apiService: apiService,
vpcID: cfg.VPCId,
privateZone: zoneType == "private",
}
return tencentCloudProvider
}
func TestTencentPrivateProvider_Records(t *testing.T) {
p := NewMockTencentCloudProvider(endpoint.NewDomainFilter([]string{"external-dns-test.com"}), provider.NewZoneIDFilter([]string{}), "private")
endpoints, err := p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Create、UpdateOld、UpdateNew、Delete
// The base record will be created.
changes := &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "redis.external-dns-test.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("4.3.2.1"),
},
},
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "nginx.external-dns-test.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("10.10.10.10"),
},
},
UpdateNew: []*endpoint.Endpoint{
{
DNSName: "tencent.external-dns-test.com",
RecordType: "A",
RecordTTL: 600,
Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"),
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "nginx.external-dns-test.com",
RecordType: "TXT",
RecordTTL: 300,
Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=default\""),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 3 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Delete one target
changes = &plan.Changes{
Delete: []*endpoint.Endpoint{
{
DNSName: "tencent.external-dns-test.com",
RecordType: "A",
RecordTTL: 600,
Targets: endpoint.NewTargets("5.6.7.8"),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 3 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Delete another target
changes = &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "redis.external-dns-test.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("5.6.7.8"),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 3 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Delete another target
changes = &plan.Changes{
Delete: []*endpoint.Endpoint{
{
DNSName: "tencent.external-dns-test.com",
RecordType: "A",
RecordTTL: 600,
Targets: endpoint.NewTargets("1.2.3.4"),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
}
func TestTencentPublicProvider_Records(t *testing.T) {
p := NewMockTencentCloudProvider(endpoint.NewDomainFilter([]string{"external-dns-test.com"}), provider.NewZoneIDFilter([]string{}), "public")
endpoints, err := p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Create、UpdateOld、UpdateNew、Delete
changes := &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "redis.external-dns-test.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("4.3.2.1"),
},
},
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "nginx.external-dns-test.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("10.10.10.10"),
},
},
UpdateNew: []*endpoint.Endpoint{
{
DNSName: "tencent.external-dns-test.com",
RecordType: "A",
RecordTTL: 600,
Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"),
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "nginx.external-dns-test.com",
RecordType: "TXT",
RecordTTL: 300,
Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=default\""),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Delete one target
changes = &plan.Changes{
Delete: []*endpoint.Endpoint{
{
DNSName: "tencent.external-dns-test.com",
RecordType: "A",
RecordTTL: 600,
Targets: endpoint.NewTargets("5.6.7.8"),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Delete another target
changes = &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "redis.external-dns-test.com",
RecordType: "A",
RecordTTL: 300,
Targets: endpoint.NewTargets("5.6.7.8"),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
// Test for Delete another target
changes = &plan.Changes{
Delete: []*endpoint.Endpoint{
{
DNSName: "tencent.external-dns-test.com",
RecordType: "A",
RecordTTL: 600,
Targets: endpoint.NewTargets("1.2.3.4"),
},
},
}
if err := p.ApplyChanges(context.Background(), changes); err != nil {
t.Errorf("Failed to get records: %v", err)
}
endpoints, err = p.Records(context.Background())
if err != nil {
t.Errorf("Failed to get records: %v", err)
} else {
if len(endpoints) != 1 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %+v", *endpoint)
}
}
}

View File

@ -1,498 +0,0 @@
/*
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 ultradns
import (
"context"
"encoding/base64"
"fmt"
"os"
"strconv"
"strings"
"time"
log "github.com/sirupsen/logrus"
udnssdk "github.com/ultradns/ultradns-sdk-go"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)
const (
ultradnsCreate = "CREATE"
ultradnsDelete = "DELETE"
ultradnsUpdate = "UPDATE"
sbPoolPriority = 1
sbPoolOrder = "ROUND_ROBIN"
rdPoolOrder = "ROUND_ROBIN"
)
var (
sbPoolActOnProbes = true
ultradnsPoolType = "rdpool"
accountName string
sbPoolRunProbes = true
// Setting custom headers for ultradns api calls
customHeader = []udnssdk.CustomHeader{
{
Key: "UltraClient",
Value: "kube-client",
},
}
)
// UltraDNSProvider struct
type UltraDNSProvider struct {
provider.BaseProvider
client udnssdk.Client
domainFilter endpoint.DomainFilter
dryRun bool
}
// UltraDNSChanges struct
type UltraDNSChanges struct {
Action string
ResourceRecordSetUltraDNS udnssdk.RRSet
}
// NewUltraDNSProvider initializes a new UltraDNS DNS based provider
func NewUltraDNSProvider(domainFilter endpoint.DomainFilter, dryRun bool) (*UltraDNSProvider, error) {
username, ok := os.LookupEnv("ULTRADNS_USERNAME")
udnssdk.SetCustomHeader = customHeader
if !ok {
return nil, fmt.Errorf("no username found")
}
base64password, ok := os.LookupEnv("ULTRADNS_PASSWORD")
if !ok {
return nil, fmt.Errorf("no password found")
}
// Base64 Standard Decoding
password, err := base64.StdEncoding.DecodeString(base64password)
if err != nil {
fmt.Printf("Error decoding string: %s ", err.Error())
return nil, err
}
baseURL, ok := os.LookupEnv("ULTRADNS_BASEURL")
if !ok {
return nil, fmt.Errorf("no baseurl found")
}
accountName, ok = os.LookupEnv("ULTRADNS_ACCOUNTNAME")
if !ok {
accountName = ""
}
probeValue, ok := os.LookupEnv("ULTRADNS_ENABLE_PROBING")
if ok {
if (probeValue != "true") && (probeValue != "false") {
return nil, fmt.Errorf("please set proper probe value, the values can be either true or false")
}
sbPoolRunProbes, _ = strconv.ParseBool(probeValue)
}
actOnProbeValue, ok := os.LookupEnv("ULTRADNS_ENABLE_ACTONPROBE")
if ok {
if (actOnProbeValue != "true") && (actOnProbeValue != "false") {
return nil, fmt.Errorf("please set proper act on probe value, the values can be either true or false")
}
sbPoolActOnProbes, _ = strconv.ParseBool(actOnProbeValue)
}
poolValue, ok := os.LookupEnv("ULTRADNS_POOL_TYPE")
if ok {
if (poolValue != "sbpool") && (poolValue != "rdpool") {
return nil, fmt.Errorf(" please set proper ULTRADNS_POOL_TYPE, supported types are sbpool or rdpool")
}
ultradnsPoolType = poolValue
}
client, err := udnssdk.NewClient(username, string(password), baseURL)
if err != nil {
return nil, fmt.Errorf("connection cannot be established")
}
return &UltraDNSProvider{
client: *client,
domainFilter: domainFilter,
dryRun: dryRun,
}, nil
}
// Zones returns list of hosted zones
func (p *UltraDNSProvider) Zones(ctx context.Context) ([]udnssdk.Zone, error) {
zoneKey := &udnssdk.ZoneKey{}
var err error
if p.domainFilter.IsConfigured() {
zonesAppender := []udnssdk.Zone{}
for _, zone := range p.domainFilter.Filters {
zoneKey.Zone = zone
zoneKey.AccountName = accountName
zones, err := p.fetchZones(ctx, zoneKey)
if err != nil {
return nil, err
}
zonesAppender = append(zonesAppender, zones...)
}
return zonesAppender, nil
}
zoneKey.AccountName = accountName
zones, err := p.fetchZones(ctx, zoneKey)
if err != nil {
return nil, err
}
return zones, nil
}
func (p *UltraDNSProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
zones, err := p.Zones(ctx)
if err != nil {
return nil, err
}
for _, zone := range zones {
log.Infof("zones : %v", zone)
var rrsetType string
var ownerName string
rrsetKey := udnssdk.RRSetKey{
Zone: zone.Properties.Name,
Type: rrsetType,
Name: ownerName,
}
if zone.Properties.ResourceRecordCount != 0 {
records, err := p.fetchRecords(ctx, rrsetKey)
if err != nil {
return nil, err
}
for _, r := range records {
recordTypeArray := strings.Fields(r.RRType)
if provider.SupportedRecordType(recordTypeArray[0]) {
log.Infof("owner name %s", r.OwnerName)
name := r.OwnerName
// root name is identified by the empty string and should be
// translated to zone name for the endpoint entry.
if r.OwnerName == "" {
name = zone.Properties.Name
}
endPointTTL := endpoint.NewEndpointWithTTL(name, recordTypeArray[0], endpoint.TTL(r.TTL), r.RData...)
endpoints = append(endpoints, endPointTTL)
}
}
}
}
log.Infof("endpoints %v", endpoints)
return endpoints, nil
}
func (p *UltraDNSProvider) fetchRecords(ctx context.Context, k udnssdk.RRSetKey) ([]udnssdk.RRSet, error) {
// Logic to paginate through all available results
maxerrs := 5
waittime := 5 * time.Second
var rrsets []udnssdk.RRSet
errcnt := 0
offset := 0
limit := 1000
for {
reqRrsets, ri, res, err := p.client.RRSets.SelectWithOffsetWithLimit(k, offset, limit)
if err != nil {
if res != nil && res.StatusCode >= 500 {
errcnt = errcnt + 1
if errcnt < maxerrs {
time.Sleep(waittime)
continue
}
}
return rrsets, err
}
rrsets = append(rrsets, reqRrsets...)
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
return rrsets, nil
}
offset = ri.ReturnedCount + ri.Offset
continue
}
}
func (p *UltraDNSProvider) fetchZones(ctx context.Context, zoneKey *udnssdk.ZoneKey) ([]udnssdk.Zone, error) {
// Logic to paginate through all available results
offset := 0
limit := 1000
maxerrs := 5
waittime := 5 * time.Second
zones := []udnssdk.Zone{}
errcnt := 0
for {
reqZones, ri, res, err := p.client.Zone.SelectWithOffsetWithLimit(zoneKey, offset, limit)
if err != nil {
if res != nil && res.StatusCode >= 500 {
errcnt = errcnt + 1
if errcnt < maxerrs {
time.Sleep(waittime)
continue
}
}
return zones, err
}
zones = append(zones, reqZones...)
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
return zones, nil
}
offset = ri.ReturnedCount + ri.Offset
continue
}
}
func (p *UltraDNSProvider) submitChanges(ctx context.Context, changes []*UltraDNSChanges) error {
cnameownerName := "cname"
txtownerName := "txt"
if len(changes) == 0 {
log.Infof("All records are already up to date")
return nil
}
zones, err := p.Zones(ctx)
if err != nil {
return err
}
zoneChanges := seperateChangeByZone(zones, changes)
for zoneName, changes := range zoneChanges {
for _, change := range changes {
switch change.ResourceRecordSetUltraDNS.RRType {
case "CNAME":
cnameownerName = change.ResourceRecordSetUltraDNS.OwnerName
case "TXT":
txtownerName = change.ResourceRecordSetUltraDNS.OwnerName
}
if cnameownerName == txtownerName {
rrsetKey := udnssdk.RRSetKey{
Zone: zoneName,
Type: endpoint.RecordTypeCNAME,
Name: change.ResourceRecordSetUltraDNS.OwnerName,
}
err := p.getSpecificRecord(ctx, rrsetKey)
if err != nil {
return err
}
if !p.dryRun {
_, err = p.client.RRSets.Delete(rrsetKey)
if err != nil {
return err
}
}
return fmt.Errorf("the 'cname' and 'txt' record name cannot be same please recreate external-dns with - --txt-prefix=")
}
rrsetKey := udnssdk.RRSetKey{
Zone: zoneName,
Type: change.ResourceRecordSetUltraDNS.RRType,
Name: change.ResourceRecordSetUltraDNS.OwnerName,
}
record := udnssdk.RRSet{}
if (change.ResourceRecordSetUltraDNS.RRType == "A" || change.ResourceRecordSetUltraDNS.RRType == "AAAA") && (len(change.ResourceRecordSetUltraDNS.RData) >= 2) {
if ultradnsPoolType == "sbpool" && change.ResourceRecordSetUltraDNS.RRType == "A" {
sbPoolObject, _ := p.newSBPoolObjectCreation(ctx, change)
record = udnssdk.RRSet{
RRType: change.ResourceRecordSetUltraDNS.RRType,
OwnerName: change.ResourceRecordSetUltraDNS.OwnerName,
RData: change.ResourceRecordSetUltraDNS.RData,
TTL: change.ResourceRecordSetUltraDNS.TTL,
Profile: sbPoolObject.RawProfile(),
}
} else if ultradnsPoolType == "rdpool" {
rdPoolObject, _ := p.newRDPoolObjectCreation(ctx, change)
record = udnssdk.RRSet{
RRType: change.ResourceRecordSetUltraDNS.RRType,
OwnerName: change.ResourceRecordSetUltraDNS.OwnerName,
RData: change.ResourceRecordSetUltraDNS.RData,
TTL: change.ResourceRecordSetUltraDNS.TTL,
Profile: rdPoolObject.RawProfile(),
}
} else {
return fmt.Errorf("we do not support Multiple target 'aaaa' records in sb pool please contact to neustar for further details")
}
} else {
record = udnssdk.RRSet{
RRType: change.ResourceRecordSetUltraDNS.RRType,
OwnerName: change.ResourceRecordSetUltraDNS.OwnerName,
RData: change.ResourceRecordSetUltraDNS.RData,
TTL: change.ResourceRecordSetUltraDNS.TTL,
}
}
log.WithFields(log.Fields{
"record": record.OwnerName,
"type": record.RRType,
"ttl": record.TTL,
"action": change.Action,
"zone": zoneName,
"profile": record.Profile,
}).Info("Changing record.")
switch change.Action {
case ultradnsCreate:
if !p.dryRun {
res, err := p.client.RRSets.Create(rrsetKey, record)
_ = res
if err != nil {
return err
}
}
case ultradnsDelete:
err := p.getSpecificRecord(ctx, rrsetKey)
if err != nil {
return err
}
if !p.dryRun {
_, err = p.client.RRSets.Delete(rrsetKey)
if err != nil {
return err
}
}
case ultradnsUpdate:
err := p.getSpecificRecord(ctx, rrsetKey)
if err != nil {
return err
}
if !p.dryRun {
_, err = p.client.RRSets.Update(rrsetKey, record)
if err != nil {
return err
}
}
}
}
}
return nil
}
func (p *UltraDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
combinedChanges := make([]*UltraDNSChanges, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete))
log.Infof("value of changes %v,%v,%v", changes.Create, changes.UpdateNew, changes.Delete)
combinedChanges = append(combinedChanges, newUltraDNSChanges(ultradnsCreate, changes.Create)...)
combinedChanges = append(combinedChanges, newUltraDNSChanges(ultradnsUpdate, changes.UpdateNew)...)
combinedChanges = append(combinedChanges, newUltraDNSChanges(ultradnsDelete, changes.Delete)...)
return p.submitChanges(ctx, combinedChanges)
}
func newUltraDNSChanges(action string, endpoints []*endpoint.Endpoint) []*UltraDNSChanges {
changes := make([]*UltraDNSChanges, 0, len(endpoints))
var ttl int
for _, e := range endpoints {
if e.RecordTTL.IsConfigured() {
ttl = int(e.RecordTTL)
}
// Adding suffix dot to the record name
recordName := fmt.Sprintf("%s.", e.DNSName)
change := &UltraDNSChanges{
Action: action,
ResourceRecordSetUltraDNS: udnssdk.RRSet{
RRType: e.RecordType,
OwnerName: recordName,
RData: e.Targets,
TTL: ttl,
},
}
changes = append(changes, change)
}
return changes
}
func seperateChangeByZone(zones []udnssdk.Zone, changes []*UltraDNSChanges) map[string][]*UltraDNSChanges {
change := make(map[string][]*UltraDNSChanges)
zoneNameID := provider.ZoneIDName{}
for _, z := range zones {
zoneNameID.Add(z.Properties.Name, z.Properties.Name)
change[z.Properties.Name] = []*UltraDNSChanges{}
}
for _, c := range changes {
zone, _ := zoneNameID.FindZone(c.ResourceRecordSetUltraDNS.OwnerName)
if zone == "" {
log.Infof("Skipping record %s because no hosted zone matching record DNS Name was detected", c.ResourceRecordSetUltraDNS.OwnerName)
continue
}
change[zone] = append(change[zone], c)
}
return change
}
func (p *UltraDNSProvider) getSpecificRecord(ctx context.Context, rrsetKey udnssdk.RRSetKey) (err error) {
_, err = p.client.RRSets.Select(rrsetKey)
if err != nil {
return fmt.Errorf("no record was found for %v", rrsetKey)
}
return nil
}
// Creation of SBPoolObject
func (p *UltraDNSProvider) newSBPoolObjectCreation(ctx context.Context, change *UltraDNSChanges) (sbPool udnssdk.SBPoolProfile, err error) {
sbpoolRDataList := []udnssdk.SBRDataInfo{}
for range change.ResourceRecordSetUltraDNS.RData {
rrdataInfo := udnssdk.SBRDataInfo{
RunProbes: sbPoolRunProbes,
Priority: sbPoolPriority,
State: "NORMAL",
Threshold: 1,
Weight: nil,
}
sbpoolRDataList = append(sbpoolRDataList, rrdataInfo)
}
sbPoolObject := udnssdk.SBPoolProfile{
Context: udnssdk.SBPoolSchema,
Order: sbPoolOrder,
Description: change.ResourceRecordSetUltraDNS.OwnerName,
MaxActive: len(change.ResourceRecordSetUltraDNS.RData),
MaxServed: len(change.ResourceRecordSetUltraDNS.RData),
RDataInfo: sbpoolRDataList,
RunProbes: sbPoolRunProbes,
ActOnProbes: sbPoolActOnProbes,
}
return sbPoolObject, nil
}
// Creation of RDPoolObject
func (p *UltraDNSProvider) newRDPoolObjectCreation(ctx context.Context, change *UltraDNSChanges) (rdPool udnssdk.RDPoolProfile, err error) {
rdPoolObject := udnssdk.RDPoolProfile{
Context: udnssdk.RDPoolSchema,
Order: rdPoolOrder,
Description: change.ResourceRecordSetUltraDNS.OwnerName,
}
return rdPoolObject, nil
}

View File

@ -1,756 +0,0 @@
/*
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 ultradns
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"reflect"
_ "strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
udnssdk "github.com/ultradns/ultradns-sdk-go"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
type mockUltraDNSZone struct {
client *udnssdk.Client
}
func (m *mockUltraDNSZone) SelectWithOffsetWithLimit(k *udnssdk.ZoneKey, offset int, limit int) (zones []udnssdk.Zone, ResultInfo udnssdk.ResultInfo, resp *http.Response, err error) {
zones = []udnssdk.Zone{}
zone := udnssdk.Zone{}
zoneJson := `
{
"properties": {
"name":"test-ultradns-provider.com.",
"accountName":"teamrest",
"type":"PRIMARY",
"dnssecStatus":"UNSIGNED",
"status":"ACTIVE",
"owner":"teamrest",
"resourceRecordCount":7,
"lastModifiedDateTime":""
}
}`
if err := json.Unmarshal([]byte(zoneJson), &zone); err != nil {
log.Fatal(err)
}
zones = append(zones, zone)
return zones, udnssdk.ResultInfo{}, nil, nil
}
type mockUltraDNSRecord struct {
client *udnssdk.Client
}
func (m *mockUltraDNSRecord) Create(_ udnssdk.RRSetKey, _ udnssdk.RRSet) (*http.Response, error) {
return &http.Response{}, nil
}
func (m *mockUltraDNSRecord) Select(_ udnssdk.RRSetKey) ([]udnssdk.RRSet, error) {
return []udnssdk.RRSet{{
OwnerName: "test-ultradns-provider.com.",
RRType: endpoint.RecordTypeA,
RData: []string{"1.1.1.1"},
TTL: 86400,
}}, nil
}
func (m *mockUltraDNSRecord) SelectWithOffset(k udnssdk.RRSetKey, offset int) ([]udnssdk.RRSet, udnssdk.ResultInfo, *http.Response, error) {
return nil, udnssdk.ResultInfo{}, nil, nil
}
func (m *mockUltraDNSRecord) Update(udnssdk.RRSetKey, udnssdk.RRSet) (*http.Response, error) {
return &http.Response{}, nil
}
func (m *mockUltraDNSRecord) Delete(k udnssdk.RRSetKey) (*http.Response, error) {
return &http.Response{}, nil
}
func (m *mockUltraDNSRecord) SelectWithOffsetWithLimit(k udnssdk.RRSetKey, offset int, limit int) (rrsets []udnssdk.RRSet, ResultInfo udnssdk.ResultInfo, resp *http.Response, err error) {
return []udnssdk.RRSet{{
OwnerName: "test-ultradns-provider.com.",
RRType: endpoint.RecordTypeA,
RData: []string{"1.1.1.1"},
TTL: 86400,
}}, udnssdk.ResultInfo{}, nil, nil
}
// NewUltraDNSProvider Test scenario
func TestNewUltraDNSProvider(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NoError(t, err)
_ = os.Unsetenv("ULTRADNS_PASSWORD")
_ = os.Unsetenv("ULTRADNS_USERNAME")
_ = os.Unsetenv("ULTRADNS_BASEURL")
_ = os.Unsetenv("ULTRADNS_ACCOUNTNAME")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "Expected to fail %s", "formatted")
}
// zones function test scenario
func TestUltraDNSProvider_Zones(t *testing.T) {
mocked := mockUltraDNSZone{}
provider := &UltraDNSProvider{
client: udnssdk.Client{
Zone: &mocked,
},
}
zoneKey := &udnssdk.ZoneKey{
Zone: "",
AccountName: "teamrest",
}
expected, _, _, err := provider.client.Zone.SelectWithOffsetWithLimit(zoneKey, 0, 1000)
require.NoError(t, err)
zones, err := provider.Zones(context.Background())
require.NoError(t, err)
assert.True(t, reflect.DeepEqual(expected, zones))
}
// Records function test case
func TestUltraDNSProvider_Records(t *testing.T) {
mocked := mockUltraDNSRecord{}
mockedDomain := mockUltraDNSZone{}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
rrsetKey := udnssdk.RRSetKey{}
expected, _, _, err := provider.client.RRSets.SelectWithOffsetWithLimit(rrsetKey, 0, 1000)
records, err := provider.Records(context.Background())
require.NoError(t, err)
for _, v := range records {
assert.Equal(t, fmt.Sprintf("%s.", v.DNSName), expected[0].OwnerName)
assert.Equal(t, v.RecordType, expected[0].RRType)
assert.Equal(t, int(v.RecordTTL), expected[0].TTL)
}
}
// ApplyChanges function testcase
func TestUltraDNSProvider_ApplyChanges(t *testing.T) {
changes := &plan.Changes{}
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
changes.Create = []*endpoint.Endpoint{
{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A"},
{DNSName: "ttl.test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A", RecordTTL: 100},
}
changes.Create = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.2"}, RecordType: "A"}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.2.2", "1.1.2.3", "1.1.2.4"}, RecordType: "A", RecordTTL: 100}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.2.2", "1.1.2.3", "1.1.2.4"}, RecordType: "A", RecordTTL: 100}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A", RecordTTL: 100}}
err := provider.ApplyChanges(context.Background(), changes)
assert.NoErrorf(t, err, "Should not fail %s", "formatted")
}
// Testing function getSpecificRecord
func TestUltraDNSProvider_getSpecificRecord(t *testing.T) {
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
recordSetKey := udnssdk.RRSetKey{
Zone: "test-ultradns-provider.com.",
Type: "A",
Name: "teamrest",
}
err := provider.getSpecificRecord(context.Background(), recordSetKey)
assert.NoError(t, err)
}
// Fail case scenario testing where CNAME and TXT Record name are same
func TestUltraDNSProvider_ApplyChangesCNAME(t *testing.T) {
changes := &plan.Changes{}
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
changes.Create = []*endpoint.Endpoint{
{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "CNAME"},
{DNSName: "test-ultradns-provider.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "TXT"},
}
err := provider.ApplyChanges(context.Background(), changes)
assert.Error(t, err)
}
// This will work if you would set the environment variables such as "ULTRADNS_INTEGRATION" and zone should be available "kubernetes-ultradns-provider-test.com"
func TestUltraDNSProvider_ApplyChanges_Integration(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
providerUltradns, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1"}, RecordType: "A"},
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, RecordType: "AAAA", RecordTTL: 100},
}
err = providerUltradns.ApplyChanges(context.Background(), changes)
require.NoError(t, err)
rrsetKey := udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ := providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, "1.1.1.1", rrsets[0].RData[0])
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "ttl.kubernetes-ultradns-provider-test.com.",
Type: "AAAA",
}
rrsets, _ = providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, "2001:db8:85a3:0:0:8a2e:370:7334", rrsets[0].RData[0])
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100},
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
}
err = providerUltradns.ApplyChanges(context.Background(), changes)
require.NoError(t, err)
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ = providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, "1.1.2.2", rrsets[0].RData[0])
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "ttl.kubernetes-ultradns-provider-test.com.",
Type: "AAAA",
}
rrsets, _ = providerUltradns.client.RRSets.Select(rrsetKey)
assert.Equal(t, "2001:db8:85a3:0:0:8a2e:370:7335", rrsets[0].RData[0])
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100},
}
err = providerUltradns.ApplyChanges(context.Background(), changes)
require.NoError(t, err)
resp, _ := providerUltradns.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/AAAA/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
resp, _ = providerUltradns.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
}
}
// This will work if you would set the environment variables such as "ULTRADNS_INTEGRATION" and zone should be available "kubernetes-ultradns-provider-test.com" for multiple target
func TestUltraDNSProvider_ApplyChanges_MultipleTarget_integeration(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
provider, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.1.2.2"}, RecordType: "A"},
}
err = provider.ApplyChanges(context.Background(), changes)
assert.NoError(t, err)
rrsetKey := udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ := provider.client.RRSets.Select(rrsetKey)
assert.Equal(t, []string{"1.1.1.1", "1.1.2.2"}, rrsets[0].RData)
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24", "1.2.3.4"}, RecordType: "A", RecordTTL: 100}}
err = provider.ApplyChanges(context.Background(), changes)
require.NoError(t, err)
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ = provider.client.RRSets.Select(rrsetKey)
assert.Equal(t, []string{"1.1.2.2", "192.168.0.24", "1.2.3.4"}, rrsets[0].RData)
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}}
err = provider.ApplyChanges(context.Background(), changes)
assert.NoError(t, err)
rrsetKey = udnssdk.RRSetKey{
Zone: "kubernetes-ultradns-provider-test.com.",
Name: "kubernetes-ultradns-provider-test.com.",
Type: "A",
}
rrsets, _ = provider.client.RRSets.Select(rrsetKey)
assert.Equal(t, []string{"1.1.2.2"}, rrsets[0].RData)
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.NoError(t, err)
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
}
}
// Test case to check sbpool creation
func TestUltraDNSProvider_newSBPoolObjectCreation(t *testing.T) {
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
sbpoolRDataList := []udnssdk.SBRDataInfo{}
changes := &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com.", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24"}, RecordType: "A", RecordTTL: 100}}
changesList := &UltraDNSChanges{
Action: "UPDATE",
ResourceRecordSetUltraDNS: udnssdk.RRSet{
RRType: "A",
OwnerName: "kubernetes-ultradns-provider-test.com.",
RData: []string{"1.1.2.2", "192.168.0.24"},
TTL: 100,
},
}
for range changesList.ResourceRecordSetUltraDNS.RData {
rrdataInfo := udnssdk.SBRDataInfo{
RunProbes: true,
Priority: 1,
State: "NORMAL",
Threshold: 1,
Weight: nil,
}
sbpoolRDataList = append(sbpoolRDataList, rrdataInfo)
}
sbPoolObject := udnssdk.SBPoolProfile{
Context: udnssdk.SBPoolSchema,
Order: "ROUND_ROBIN",
Description: "kubernetes-ultradns-provider-test.com.",
MaxActive: 2,
MaxServed: 2,
RDataInfo: sbpoolRDataList,
RunProbes: true,
ActOnProbes: true,
}
actualSBPoolObject, _ := provider.newSBPoolObjectCreation(context.Background(), changesList)
assert.Equal(t, sbPoolObject, actualSBPoolObject)
}
// Testcase to check fail scenario for multiple AAAA targets
func TestUltraDNSProvider_MultipleTargetAAAA(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
_ = os.Setenv("ULTRADNS_POOL_TYPE", "sbpool")
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
}
err := provider.ApplyChanges(context.Background(), changes)
assert.Errorf(t, err, "We wanted it to fail since multiple AAAA targets are not allowed %s", "formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/AAAA/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
_ = os.Unsetenv("ULTRADNS_POOL_TYPE")
}
}
// Testcase to check fail scenario for multiple AAAA targets
func TestUltraDNSProvider_MultipleTargetAAAARDPool(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
_ = os.Setenv("ULTRADNS_POOL_TYPE", "rdpool")
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100},
}
err := provider.ApplyChanges(context.Background(), changes)
require.NoErrorf(t, err, " multiple AAAA targets are allowed when pool is RDPool %s", "formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/AAAA/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "200 OK", resp.Status)
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA"}}
err = provider.ApplyChanges(context.Background(), changes)
require.NoError(t, err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
}
}
// Test case to check multiple CNAME targets.
func TestUltraDNSProvider_MultipleTargetCNAME(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
provider, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{
{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"nginx.loadbalancer.com.", "nginx1.loadbalancer.com."}, RecordType: "CNAME", RecordTTL: 100},
}
err = provider.ApplyChanges(context.Background(), changes)
assert.Errorf(t, err, "We wanted it to fail since multiple CNAME targets are not allowed %s", "formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/CNAME/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
}
}
// Testing creation of RD Pool
func TestUltraDNSProvider_newRDPoolObjectCreation(t *testing.T) {
mocked := mockUltraDNSRecord{nil}
mockedDomain := mockUltraDNSZone{nil}
provider := &UltraDNSProvider{
client: udnssdk.Client{
RRSets: &mocked,
Zone: &mockedDomain,
},
}
changes := &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "kubernetes-ultradns-provider-test.com.", Targets: endpoint.Targets{"1.1.2.2", "192.168.0.24"}, RecordType: "A", RecordTTL: 100}}
changesList := &UltraDNSChanges{
Action: "UPDATE",
ResourceRecordSetUltraDNS: udnssdk.RRSet{
RRType: "A",
OwnerName: "kubernetes-ultradns-provider-test.com.",
RData: []string{"1.1.2.2", "192.168.0.24"},
TTL: 100,
},
}
rdPoolObject := udnssdk.RDPoolProfile{
Context: udnssdk.RDPoolSchema,
Order: "ROUND_ROBIN",
Description: "kubernetes-ultradns-provider-test.com.",
}
actualRDPoolObject, _ := provider.newRDPoolObjectCreation(context.Background(), changesList)
assert.Equal(t, rdPoolObject, actualRDPoolObject)
}
// Testing Failure scenarios over NewUltraDNS Provider
func TestNewUltraDNSProvider_FailCases(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_POOL_TYPE", "xyz")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "Pool Type other than given type not working %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_PROBING", "adefg")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "Probe value other than given values not working %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "adefg")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "ActOnProbe value other than given values not working %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Unsetenv("ULTRADNS_PASSWORD")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "Expected to give error if password is not set %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Unsetenv("ULTRADNS_BASEURL")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_, err = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "Expected to give error if baseurl is not set %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Unsetenv("ULTRADNS_ACCOUNTNAME")
_ = os.Unsetenv("ULTRADNS_ENABLE_ACTONPROBE")
_ = os.Unsetenv("ULTRADNS_ENABLE_PROBING")
_ = os.Unsetenv("ULTRADNS_POOL_TYPE")
_, accounterr := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NoError(t, accounterr)
}
// Testing success scenarios for newly introduced environment variables
func TestNewUltraDNSProvider_NewEnvVariableSuccessCases(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_POOL_TYPE", "rdpool")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NoErrorf(t, err, "Pool Type not working in proper scenario %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_PROBING", "false")
_, err1 := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NoErrorf(t, err1, "Probe given value is not working %s", "formatted")
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "true")
_, err2 := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.NoErrorf(t, err2, "ActOnProbe given value is not working %s", "formatted")
}
// Base64 Bad string decoding scenario
func TestNewUltraDNSProvider_Base64DecodeFailcase(t *testing.T) {
_ = os.Setenv("ULTRADNS_USERNAME", "")
_ = os.Setenv("ULTRADNS_PASSWORD", "12345")
_ = os.Setenv("ULTRADNS_BASEURL", "")
_ = os.Setenv("ULTRADNS_ACCOUNTNAME", "")
_ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "true")
_, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true)
assert.Errorf(t, err, "Base64 decode should fail in this case %s", "formatted")
}
func TestUltraDNSProvider_PoolConversionCase(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
// Creating SBPool Record
_ = os.Setenv("ULTRADNS_POOL_TYPE", "sbpool")
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.2.3.4"}, RecordType: "A", RecordTTL: 100}}
err := provider.ApplyChanges(context.Background(), changes)
assert.NoErrorf(t, err, " multiple A record creation with SBPool %s", "formatted")
resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "200 OK", resp.Status)
// Converting to RD Pool
_ = os.Setenv("ULTRADNS_POOL_TYPE", "rdpool")
provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.2.3.5"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.NoError(t, err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "200 OK", resp.Status)
// Converting back to SB Pool
_ = os.Setenv("ULTRADNS_POOL_TYPE", "sbpool")
provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false)
changes = &plan.Changes{}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.2.3.4"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.NoError(t, err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "200 OK", resp.Status)
// Deleting Record
changes = &plan.Changes{}
changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.2.3.4"}, RecordType: "A"}}
err = provider.ApplyChanges(context.Background(), changes)
assert.NoError(t, err)
resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{})
assert.Equal(t, "404 Not Found", resp.Status)
}
}
func TestUltraDNSProvider_DomainFilter(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com", "kubernetes-ultradns-provider-test.com"}), true)
zones, err := provider.Zones(context.Background())
assert.Equal(t, "kubernetes-ultradns-provider-test.com.", zones[0].Properties.Name)
assert.Equal(t, "kubernetes-ultradns-provider-test.com.", zones[1].Properties.Name)
assert.NoErrorf(t, err, " Multiple domain filter failed %s", "formatted")
provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{}), true)
zones, err = provider.Zones(context.Background())
assert.NoErrorf(t, err, " Multiple domain filter failed %s", "formatted")
}
}
func TestUltraDNSProvider_DomainFiltersZonesFailCase(t *testing.T) {
_, ok := os.LookupEnv("ULTRADNS_INTEGRATION")
if !ok {
log.Printf("Skipping test")
} else {
provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com", "kubernetes-uldsvdsvadvvdsvadvstradns-provider-test.com"}), true)
_, err := provider.Zones(context.Background())
assert.Errorf(t, err, " Multiple domain filter failed %s", "formatted")
}
}
// zones function with domain filter test scenario
func TestUltraDNSProvider_DomainFilterZonesMocked(t *testing.T) {
mocked := mockUltraDNSZone{}
provider := &UltraDNSProvider{
client: udnssdk.Client{
Zone: &mocked,
},
domainFilter: endpoint.NewDomainFilter([]string{"test-ultradns-provider.com."}),
}
zoneKey := &udnssdk.ZoneKey{
Zone: "test-ultradns-provider.com.",
AccountName: "",
}
// When AccountName not given
expected, _, _, err := provider.client.Zone.SelectWithOffsetWithLimit(zoneKey, 0, 1000)
assert.NoError(t, err)
zones, err := provider.Zones(context.Background())
assert.NoError(t, err)
assert.True(t, reflect.DeepEqual(expected, zones))
accountName = "teamrest"
// When AccountName is set
provider = &UltraDNSProvider{
client: udnssdk.Client{
Zone: &mocked,
},
domainFilter: endpoint.NewDomainFilter([]string{"test-ultradns-provider.com."}),
}
zoneKey = &udnssdk.ZoneKey{
Zone: "test-ultradns-provider.com.",
AccountName: "teamrest",
}
expected, _, _, err = provider.client.Zone.SelectWithOffsetWithLimit(zoneKey, 0, 1000)
assert.NoError(t, err)
zones, err = provider.Zones(context.Background())
assert.NoError(t, err)
assert.True(t, reflect.DeepEqual(expected, zones))
// When zone is not given but account is provided
provider = &UltraDNSProvider{
client: udnssdk.Client{
Zone: &mocked,
},
}
zoneKey = &udnssdk.ZoneKey{
AccountName: "teamrest",
}
expected, _, _, err = provider.client.Zone.SelectWithOffsetWithLimit(zoneKey, 0, 1000)
assert.NoError(t, err)
zones, err = provider.Zones(context.Background())
assert.NoError(t, err)
assert.True(t, reflect.DeepEqual(expected, zones))
}

View File

@ -26,7 +26,6 @@ const (
AWSPrefix = "external-dns.alpha.kubernetes.io/aws-" AWSPrefix = "external-dns.alpha.kubernetes.io/aws-"
SCWPrefix = "external-dns.alpha.kubernetes.io/scw-" SCWPrefix = "external-dns.alpha.kubernetes.io/scw-"
IBMCloudPrefix = "external-dns.alpha.kubernetes.io/ibmcloud-"
WebhookPrefix = "external-dns.alpha.kubernetes.io/webhook-" WebhookPrefix = "external-dns.alpha.kubernetes.io/webhook-"
CloudflarePrefix = "external-dns.alpha.kubernetes.io/cloudflare-" CloudflarePrefix = "external-dns.alpha.kubernetes.io/cloudflare-"

View File

@ -45,12 +45,6 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid
Name: fmt.Sprintf("scw/%s", attr), Name: fmt.Sprintf("scw/%s", attr),
Value: v, Value: v,
}) })
} else if strings.HasPrefix(k, IBMCloudPrefix) {
attr := strings.TrimPrefix(k, IBMCloudPrefix)
providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{
Name: fmt.Sprintf("ibmcloud-%s", attr),
Value: v,
})
} else if strings.HasPrefix(k, WebhookPrefix) { } else if strings.HasPrefix(k, WebhookPrefix) {
// Support for wildcard annotations for webhook providers // Support for wildcard annotations for webhook providers
attr := strings.TrimPrefix(k, WebhookPrefix) attr := strings.TrimPrefix(k, WebhookPrefix)

View File

@ -300,19 +300,6 @@ func TestGetProviderSpecificIdentifierAnnotations(t *testing.T) {
}, },
expectedIdentifier: "id1", expectedIdentifier: "id1",
}, },
{
title: "ibmcloud- provider specific annotations are set correctly",
annotations: map[string]string{
"external-dns.alpha.kubernetes.io/ibmcloud-annotation-1": "value 1",
SetIdentifierKey: "id1",
"external-dns.alpha.kubernetes.io/ibmcloud-annotation-2": "value 2",
},
expectedResult: map[string]string{
"ibmcloud-annotation-1": "value 1",
"ibmcloud-annotation-2": "value 2",
},
expectedIdentifier: "id1",
},
{ {
title: "webhook- provider specific annotations are set correctly", title: "webhook- provider specific annotations are set correctly",
annotations: map[string]string{ annotations: map[string]string{