Merge branch 'kubernetes-sigs:master' into aws-sd-tags

This commit is contained in:
Vincent Miszczak 2024-10-07 10:20:22 +02:00 committed by GitHub
commit ad744bda83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 836 additions and 1411 deletions

View File

@ -21,7 +21,7 @@ jobs:
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set up Go 1.x
uses: actions/setup-go@v5

View File

@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Install go version
uses: actions/setup-go@v5
with:

View File

@ -15,7 +15,7 @@ jobs:
name: Release Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 0

View File

@ -14,7 +14,7 @@ jobs:
json-yaml-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: json-yaml-validate
uses: GrantBirki/json-yaml-validate@v3.2.1

View File

@ -14,7 +14,7 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 0

View File

@ -22,7 +22,7 @@ jobs:
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set up Go 1.x
uses: actions/setup-go@v5

View File

@ -20,7 +20,7 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 0

View File

@ -21,7 +21,7 @@ jobs:
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set up Go 1.x
uses: actions/setup-go@v5

View File

@ -74,22 +74,23 @@ See PR #3063 for all the discussions about it.
Known providers using webhooks:
| Provider | Repo |
| -------- | ----------- |
| Adguard Home Provider | https://github.com/muhlba91/external-dns-provider-adguard |
| Anexia | https://github.com/ProbstenHias/external-dns-anexia-webhook |
| Bizfly Cloud | https://github.com/bizflycloud/external-dns-bizflycloud-webhook |
| Efficient IP | https://github.com/EfficientIP-Labs/external-dns-efficientip-webhook |
| Gcore | https://github.com/G-Core/external-dns-gcore-webhook |
| GleSYS | https://github.com/glesys/external-dns-glesys |
| Hetzner | https://github.com/mconfalonieri/external-dns-hetzner-webhook |
| IONOS | https://github.com/ionos-cloud/external-dns-ionos-webhook |
| Infoblox | https://github.com/AbsaOSS/external-dns-infoblox-webhook |
| Netcup | https://github.com/mrueg/external-dns-netcup-webhook |
| Netic | https://github.com/neticdk/external-dns-tidydns-webhook |
| RouterOS | https://github.com/benfiola/external-dns-routeros-provider |
| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |
| Unifi | https://github.com/kashalls/external-dns-unifi-webhook |
| Provider | Repo |
|-----------------------|----------------------------------------------------------------------|
| Adguard Home Provider | https://github.com/muhlba91/external-dns-provider-adguard |
| Anexia | https://github.com/ProbstenHias/external-dns-anexia-webhook |
| Bizfly Cloud | https://github.com/bizflycloud/external-dns-bizflycloud-webhook |
| Efficient IP | https://github.com/EfficientIP-Labs/external-dns-efficientip-webhook |
| Gcore | https://github.com/G-Core/external-dns-gcore-webhook |
| GleSYS | https://github.com/glesys/external-dns-glesys |
| Hetzner | https://github.com/mconfalonieri/external-dns-hetzner-webhook |
| IONOS | https://github.com/ionos-cloud/external-dns-ionos-webhook |
| Infoblox | https://github.com/AbsaOSS/external-dns-infoblox-webhook |
| Netcup | https://github.com/mrueg/external-dns-netcup-webhook |
| Netic | https://github.com/neticdk/external-dns-tidydns-webhook |
| RouterOS | https://github.com/benfiola/external-dns-routeros-provider |
| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |
| Unifi | https://github.com/kashalls/external-dns-unifi-webhook |
| Vultr | https://github.com/vultr/external-dns-vultr-webhook |
## Status of in-tree providers
@ -125,7 +126,6 @@ The following table clarifies the current status of the providers according to t
| RFC2136 | Alpha | |
| NS1 | Alpha | |
| TransIP | Alpha | |
| RancherDNS | Alpha | |
| OVH | Alpha | |
| Scaleway DNS | Alpha | @Sh4d1 |
| UltraDNS | Alpha | |
@ -183,10 +183,10 @@ The following tutorials are provided:
* [NS1](docs/tutorials/ns1.md)
* [NS Record Creation with CRD Source](docs/sources/ns-record.md)
* [MX Record Creation with CRD Source](docs/sources/mx-record.md)
* [TXT Record Creation with CRD Source](docs/sources/txt-record.md)
* [OpenStack Designate](docs/tutorials/designate.md)
* [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md)
* [PowerDNS](docs/tutorials/pdns.md)
* [RancherDNS (RDNS)](docs/tutorials/rdns.md)
* [RFC2136](docs/tutorials/rfc2136.md)
* [TransIP](docs/tutorials/transip.md)
* [OVH](docs/tutorials/ovh.md)

View File

@ -1,12 +1,12 @@
# MX record with CRD source
You can create and manage MX records with the help of [CRD source](../contributing/crd-source.md)
and `DNSEndpoint` CRD. Currently, this feature is only supported by `aws`, `azure`, and `google` providers.
and `DNSEndpoint` CRD. Currently, this feature is only supported by `aws`, `azure`, `google` and `digitalocean` providers.
In order to start managing MX records you need to set the `--managed-record-types MX` flag.
```console
external-dns --source crd --provider {aws|azure|google} --managed-record-types A --managed-record-types CNAME --managed-record-types MX
external-dns --source crd --provider {aws|azure|google|digitalocean} --managed-record-types A --managed-record-types CNAME --managed-record-types MX
```
Targets within the CRD need to be specified according to the RFC 1034 (section 3.6.1). Below is an example of

View File

@ -0,0 +1,30 @@
# Creating TXT record with CRD source
You can create and manage TXT records with the help of [CRD source](../contributing/crd-source.md)
and `DNSEndpoint` CRD. Currently, this feature is only supported by `digitalocean` providers.
In order to start managing TXT records you need to set the `--managed-record-types TXT` flag.
```console
external-dns --source crd --provider {digitalocean} --managed-record-types A --managed-record-types CNAME --managed-record-types TXT
```
Targets within the CRD need to be specified according to the RFC 1035 (section 3.3.14). Below is an example of
`example.com` DNS TXT two records creation.
**NOTE** Current implementation do not support RFC 6763 (section 6).
```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplemxrecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 180
recordType: TXT
targets:
- SOMETXT
- ANOTHERTXT
```

View File

@ -47,6 +47,7 @@ spec:
- --source=service # or ingress or both
- --provider=pdns
- --pdns-server={{ pdns-api-url }}
- --pdns-server-id={{ pdns-server-id }}
- --pdns-api-key={{ pdns-http-api-key }}
- --txt-owner-id={{ owner-id-for-this-external-dns }}
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the zones matching provided domain; omit to process all available zones in PowerDNS
@ -172,3 +173,102 @@ Once the API shows the record correctly, you can double check your record using:
```bash
$ dig @${PDNS_FQDN} echo.example.com.
```
## Using CRD source to manage DNS records in PowerDNS
[CRD source](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/contributing/crd-source.md) provides a generic mechanism and declarative way to manage DNS records in PowerDNS using external-dns.
```bash
external-dns --source=crd --provider=pdns \
--pdns-server={{ pdns-api-url }} \
--pdns-api-key={{ pdns-api-key }} \
--domain-filter=example.com \
--managed-record-types=A \
--managed-record-types=CNAME \
--managed-record-types=TXT \
--managed-record-types=MX \
--managed-record-types=SRV
```
Not all the record types are enabled by default so we can enable the required record types using `--managed-record-types`.
* Example for record type `A`
```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplearecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 60
recordType: A
targets:
- 10.0.0.1
```
* Example for record type `CNAME`
```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplecnamerecord
spec:
endpoints:
- dnsName: test-a.example.com
recordTTL: 300
recordType: CNAME
targets:
- example.com
```
* Example for record type `TXT`
```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: exampletxtrecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 3600
recordType: TXT
targets:
- '"v=spf1 include:spf.protection.example.com include:example.org -all"'
- '"apple-domain-verification=XXXXXXXXXXXXX"'
```
* Example for record type `MX`
```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplemxrecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 3600
recordType: MX
targets:
- "10 mailhost1.example.com"
```
* Example for record type `SRV`
```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplesrvrecord
spec:
endpoints:
- dnsName: _service._tls.example.com
recordTTL: 180
recordType: SRV
targets:
- "100 1 443 service.example.com"
```

View File

@ -1,173 +0,0 @@
# RancherDNS
This tutorial describes how to setup ExternalDNS for usage within a kubernetes cluster that makes use of [RDNS](https://github.com/rancher/rdns-server) and [nginx ingress controller](https://github.com/kubernetes/ingress-nginx).
You need to:
* install RDNS with [etcd](https://github.com/etcd-io/etcd) enabled
* install external-dns with rdns as a provider
## Installing RDNS with etcdv3 backend
### Clone RDNS
```
git clone https://github.com/rancher/rdns-server.git
```
### Installing ETCD
```
cd rdns-server
docker-compose -f deploy/etcdv3/etcd-compose.yaml up -d
```
> ETCD was successfully deployed on `http://172.31.35.77:2379`
### Installing RDNS
```
export ETCD_ENDPOINTS="http://172.31.35.77:2379"
export DOMAIN="lb.rancher.cloud"
./scripts/start etcdv3
```
> RDNS was successfully deployed on `172.31.35.77`
## Installing ExternalDNS
### Install external ExternalDNS
ETCD_URLS is configured to etcd client service address.
RDNS_ROOT_DOMAIN is configured to the same with RDNS DOMAIN environment. e.g. lb.rancher.cloud.
#### Manifest (for clusters without RBAC enabled)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: kube-system
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.15.0
args:
- --source=ingress
- --provider=rdns
- --log-level=debug # debug only
env:
- name: ETCD_URLS
value: http://172.31.35.77:2379
- name: RDNS_ROOT_DOMAIN
value: lb.rancher.cloud
```
#### Manifest (for clusters with RBAC enabled)
```yaml
---
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: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: kube-system
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.15.0
args:
- --source=ingress
- --provider=rdns
- --log-level=debug # debug only
env:
- name: ETCD_URLS
value: http://172.31.35.77:2379
- name: RDNS_ROOT_DOMAIN
value: lb.rancher.cloud
```
## Testing ingress example
```
$ cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
spec:
ingressClassName: nginx
rules:
- host: nginx.lb.rancher.cloud
http:
paths:
- backend:
serviceName: nginx
servicePort: 80
$ kubectl apply -f ingress.yaml
ingress.extensions "nginx" created
```
Wait a moment until DNS has the ingress IP. The RDNS IP in this example is "172.31.35.77".
```
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
nginx nginx.lb.rancher.cloud 172.31.42.211 80 2m
$ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
If you don't see a command prompt, try pressing enter.
dnstools# dig @172.31.35.77 nginx.lb.rancher.cloud +short
172.31.42.211
dnstools#
```

134
go.mod
View File

@ -3,35 +3,35 @@ module sigs.k8s.io/external-dns
go 1.23
require (
cloud.google.com/go/compute/metadata v0.5.0
cloud.google.com/go/compute/metadata v0.5.2
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.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.2.0
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.17.1
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.0
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.5.0
github.com/IBM/go-sdk-core/v5 v5.17.4
github.com/IBM/go-sdk-core/v5 v5.17.5
github.com/IBM/networking-go-sdk v0.49.0
github.com/Yamashou/gqlgenc v0.24.0
github.com/Yamashou/gqlgenc v0.25.2
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/aliyun/alibaba-cloud-sdk-go v1.63.0
github.com/aws/aws-sdk-go-v2 v1.30.3
github.com/aws/aws-sdk-go-v2/config v1.27.27
github.com/aws/aws-sdk-go-v2/credentials v1.17.27
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.14.10
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4
github.com/aws/aws-sdk-go-v2/service/route53 v1.42.3
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.31.3
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3
github.com/aliyun/alibaba-cloud-sdk-go v1.63.18
github.com/aws/aws-sdk-go-v2 v1.31.0
github.com/aws/aws-sdk-go-v2/config v1.27.38
github.com/aws/aws-sdk-go-v2/credentials v1.17.36
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.7
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.2
github.com/aws/aws-sdk-go-v2/service/route53 v1.44.2
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.32.2
github.com/aws/aws-sdk-go-v2/service/sts v1.31.2
github.com/bodgit/tsig v1.2.2
github.com/cenkalti/backoff/v4 v4.3.0
github.com/civo/civogo v0.3.73
github.com/cloudflare/cloudflare-go v0.102.0
github.com/civo/civogo v0.3.79
github.com/cloudflare/cloudflare-go v0.105.0
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
github.com/datawire/ambassador v1.12.4
github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace
github.com/digitalocean/godo v1.120.0
github.com/digitalocean/godo v1.126.0
github.com/dnsimple/dnsimple-go v1.7.0
github.com/exoscale/egoscale v0.102.3
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
@ -39,70 +39,70 @@ require (
github.com/go-logr/logr v1.4.2
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/gophercloud/gophercloud v1.14.0
github.com/gophercloud/gophercloud v1.14.1
github.com/linki/instrumented_http v0.3.0
github.com/linode/linodego v1.39.0
github.com/linode/linodego v1.41.0
github.com/maxatome/go-testdeep v1.14.0
github.com/miekg/dns v1.1.62
github.com/onsi/ginkgo v1.16.5
github.com/openshift/api v0.0.0-20230607130528-611114dca681
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
github.com/oracle/oci-go-sdk/v65 v65.71.1
github.com/oracle/oci-go-sdk/v65 v65.75.0
github.com/ovh/go-ovh v1.6.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pluralsh/gqlclient v1.12.2
github.com/projectcontour/contour v1.30.0
github.com/prometheus/client_golang v1.20.0
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29
github.com/prometheus/client_golang v1.20.4
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.984
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.984
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.984
github.com/transip/gotransip/v6 v6.25.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1011
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1011
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1011
github.com/transip/gotransip/v6 v6.26.0
github.com/ultradns/ultradns-sdk-go v1.3.7
go.etcd.io/etcd/api/v3 v3.5.15
go.etcd.io/etcd/client/v3 v3.5.15
go.etcd.io/etcd/api/v3 v3.5.16
go.etcd.io/etcd/client/v3 v3.5.16
go.uber.org/ratelimit v0.3.1
golang.org/x/net v0.28.0
golang.org/x/oauth2 v0.22.0
golang.org/x/net v0.29.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/time v0.6.0
google.golang.org/api v0.192.0
gopkg.in/ns1/ns1-go.v2 v2.12.0
google.golang.org/api v0.199.0
gopkg.in/ns1/ns1-go.v2 v2.12.1
gopkg.in/yaml.v2 v2.4.0
istio.io/api v1.23.0
istio.io/client-go v1.23.0
k8s.io/api v0.31.0
k8s.io/apimachinery v0.31.0
k8s.io/client-go v0.31.0
istio.io/api v1.23.2
istio.io/client-go v1.23.2
k8s.io/api v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/client-go v0.31.1
k8s.io/klog/v2 v2.130.1
sigs.k8s.io/gateway-api v1.1.0
)
require (
cloud.google.com/go/auth v0.8.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
cloud.google.com/go/auth v0.9.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
github.com/99designs/gqlgen v0.17.44 // indirect
github.com/99designs/gqlgen v0.17.54 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // 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.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.22.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.16 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.23.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 // indirect
github.com/aws/smithy-go v1.21.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@ -137,7 +137,7 @@ require (
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@ -181,32 +181,32 @@ require (
github.com/schollz/progressbar/v3 v3.8.6 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/sosodev/duration v1.2.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // 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.14 // indirect
github.com/vektah/gqlparser/v2 v2.5.16 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.22.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect
google.golang.org/grpc v1.65.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.67.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect

268
go.sum
View File

@ -2,18 +2,18 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxo
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo=
cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc=
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
cloud.google.com/go/auth v0.9.5 h1:4CTn43Eynw40aFVr3GpPqsQponx2jv0BQpjvajsbbzw=
cloud.google.com/go/auth v0.9.5/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM=
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk=
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM=
github.com/99designs/gqlgen v0.17.44 h1:OS2wLk/67Y+vXM75XHbwRnNYJcbuJd4OBL76RX3NQQA=
github.com/99designs/gqlgen v0.17.44/go.mod h1:UTCu3xpK2mLI5qcMNw+HKDiEL77it/1XtAjisC4sLwM=
github.com/99designs/gqlgen v0.17.54 h1:AsF49k/7RJlwA00RQYsYN0T8cQuaosnV/7G1dHC3Uh8=
github.com/99designs/gqlgen v0.17.54/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
@ -44,13 +44,13 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.17.1 h1:DsX6HIG2kxYg2bRM7jIVgyBb6PcJXBt9iCsTfLvFwWE=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.17.1/go.mod h1:mMF9pk71U8aIzMBS+CWq8OL3gLcFCRCiy+wNpE4gDIE=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.0 h1:soCQ+QIHHVk/WFQPOfS2moxtFqLHIQuFJlf1U9773aM=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.0/go.mod h1:m2bDK0Bb/KdhazvWWsQeQFw7R4QpHxX4U/cefZFBtHs=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.5.0 h1:+a994rHmNFwlSA609Z6SYhn9xt+lhGFF+dsgjMF75hY=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.5.0/go.mod h1:XxWyb5MQDU4GnRBSDZpGgIFwfbcn+GAUbPKS8CR8Bxc=
github.com/IBM/go-sdk-core/v5 v5.17.4 h1:VGb9+mRrnS2HpHZFM5hy4J6ppIWnwNrw0G+tLSgcJLc=
github.com/IBM/go-sdk-core/v5 v5.17.4/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns=
github.com/IBM/go-sdk-core/v5 v5.17.5 h1:AjGC7xNee5tgDIjndekBDW5AbypdERHSgib3EZ1KNsA=
github.com/IBM/go-sdk-core/v5 v5.17.5/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns=
github.com/IBM/networking-go-sdk v0.49.0 h1:lPS34u3C0JVrbxH+Ulua76Nwl6Frv8BEfq6LRkyvOv0=
github.com/IBM/networking-go-sdk v0.49.0/go.mod h1:G9CKbmPE8gSLjN+ABh4hIZ1bMx076enl5Eekvj6zQnA=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@ -77,8 +77,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Yamashou/gqlgenc v0.24.0 h1:Aeufjb2zF0XxkeSTAVQ+DfiHL+ney/M2ovShZozBmHw=
github.com/Yamashou/gqlgenc v0.24.0/go.mod h1:3QQD8ZoeEyVXuzqcMDsl8OfCCCTk+ulaxkvFFQDupIA=
github.com/Yamashou/gqlgenc v0.25.2 h1:4VoeXuI/8M7njUYiOsTSkkEit13rXWOjoK39AG31jPo=
github.com/Yamashou/gqlgenc v0.25.2/go.mod h1:G0g1N81xpIklVdnyboW1zwOHcj/n4hNfhTwfN29Rjig=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
@ -94,8 +94,8 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.0 h1:GIwkDPfeF/IBh5lZ5Mig50r1LXomNXR7t/oKGSMJWns=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.0/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.18 h1:Q36nROtbIQhLN6VZ4KJzZkISj4SWlq8pEwtqYBNz0is=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.18/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
@ -117,44 +117,44 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
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-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.14.10 h1:orAIBscNu5aIjDOnKIrjO+IUFPMLKj3Lp0bPf4chiPc=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.14.10/go.mod h1:GNjJ8daGhv10hmQYCnmkV8HuY6xXOXV4vzBssSjEIlU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4 h1:utG3S4T+X7nONPIpRoi1tVcQdAdJxntiVS2yolPJyXc=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4/go.mod h1:q9vzW3Xr1KEXa8n4waHiFt1PrppNDlMymlYP+xpsFbY=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.22.3 h1:r27/FnxLPixKBRIlslsvhqscBuMK8uysCYG9Kfgm098=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.22.3/go.mod h1:jqOFyN+QSWSoQC+ppyc4weiO8iNQXbzRbxDjQ1ayYd4=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.16 h1:lhAX5f7KpgwyieXjbDnRTjPEUI0l3emSRyxXj1PXP8w=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.16/go.mod h1:AblAlCwvi7Q/SFowvckgN+8M3uFPlopSYeLlbNDArhA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
github.com/aws/aws-sdk-go-v2/service/route53 v1.42.3 h1:MmLCRqP4U4Cw9gJ4bNrCG0mWqEtBlmAVleyelcHARMU=
github.com/aws/aws-sdk-go-v2/service/route53 v1.42.3/go.mod h1:AMPjK2YnRh0YgOID3PqhJA1BRNfXDfGOnSsKHtAe8yA=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.31.3 h1:EthA93BNgTnk36FoI9DCKtv4S0m63WzdGDYlBp/CvHQ=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.31.3/go.mod h1:4xh/h0pevPhBkA4b2iYosZaqrThccxFREQxiGuZpJlc=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
github.com/aws/aws-sdk-go-v2/config v1.27.38 h1:mMVyJJuSUdbD4zKXoxDgWrgM60QwlFEg+JhihCq6wCw=
github.com/aws/aws-sdk-go-v2/config v1.27.38/go.mod h1:6xOiNEn58bj/64MPKx89r6G/el9JZn8pvVbquSqTKK4=
github.com/aws/aws-sdk-go-v2/credentials v1.17.36 h1:zwI5WrT+oWWfzSKoTNmSyeBKQhsFRJRv+PGW/UZW+Yk=
github.com/aws/aws-sdk-go-v2/credentials v1.17.36/go.mod h1:3AG/sY1rc9NJrNWcN/3KPU4SIDPGTrd/qegKB0TnFdE=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.7 h1:ZzyrqQfMX4lagelhV90h7QKiKyoVfV7eXTPS3dOX5GY=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.7/go.mod h1:YYffpxyQJqvscSWs4Sh3h0rALEiCePKbaJlw6N+pPy0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.2 h1:EGvR8KwbxUXEUCS4HAgSRcxeFT1/0bqvS5tRR0WZSbM=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.2/go.mod h1:k5XW8MoMxsNZ20RJmsokakvENUwQyjv69R9GqrI4xdQ=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.23.2 h1:h4sDZaE8OcfPdR5C2m8MEkmQ0PXKYj9BQcYZH6Kc0GQ=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.23.2/go.mod h1:NZQWaOwOszI7jnQ7s1i5kN/FUAglaaJIm2htZG7BJKw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 h1:dOxqOlOEa2e2heC/74+ZzcJOa27+F1aXFZpYgY/4QfA=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19/go.mod h1:aV6U1beLFvk3qAgognjS3wnGGoDId8hlPEiBsLHXVZE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg=
github.com/aws/aws-sdk-go-v2/service/route53 v1.44.2 h1:ssvjp8LJrv7x/sPr15E5igCARw00MoIWl54SXZ1FIr0=
github.com/aws/aws-sdk-go-v2/service/route53 v1.44.2/go.mod h1:l2ABSKg3AibEJeR/l60cfeGU54UqF3VTgd51pq+vYhU=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.32.2 h1:29YTjasLjpAjb9RMacMkwWJ2PgDipZqzDS3TOkqUsl4=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.32.2/go.mod h1:hbMVfSdZneCht4UmPOsejDt93QnetQPFuLOOqbuybqs=
github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 h1:yzi/y/vKlLyzOfG7pSu5ONNGRxHIgLeDrV4w2AMRCo0=
github.com/aws/aws-sdk-go-v2/service/sso v1.23.2/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 h1:3gb6pYhYLjo8rB1h2Tqs61wpjRd3rQymYcVq/pp0yxI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E=
github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 h1:O6tyji8mXmBGsHvTCB0VIhrDw19lGTUSbKIyjnw79s8=
github.com/aws/aws-sdk-go-v2/service/sts v1.31.2/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -183,12 +183,12 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/civo/civogo v0.3.73 h1:thkNnkziU+xh+MEOChIUwRZI1forN20+SSAPe/VFDME=
github.com/civo/civogo v0.3.73/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
github.com/civo/civogo v0.3.79 h1:Z1MbEG9CsGqSZV7UFBA0xsjk7TBGUPHjW9sM7cS5yZM=
github.com/civo/civogo v0.3.79/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.102.0 h1:+0MGbkirM/yzVLOYpWMgW7CDdKzesSbdwA2Y+rABrWI=
github.com/cloudflare/cloudflare-go v0.102.0/go.mod h1:BOB41tXf31ti/qtBO9paYhyapotQbGRDbQoLOAF7pSg=
github.com/cloudflare/cloudflare-go v0.105.0 h1:yu2IatITLZ4dw7/byzRrlE5DfUvtub0k9CHZ5zBlj90=
github.com/cloudflare/cloudflare-go v0.105.0/go.mod h1:pfUQ4PIG4ISI0/Mmc21Bp86UnFU0ktmPf3iTgbSL+cM=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -251,8 +251,8 @@ github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace/go.mod h1:TK05
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/godo v1.120.0 h1:t2DpzIitSnCDNQM7svSW4+cZd8E4Lv6+r8y33Kym0Xw=
github.com/digitalocean/godo v1.120.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY=
github.com/digitalocean/godo v1.126.0 h1:+Znh7VMQj/E8ArbjWnc7OKGjWfzC+I8OCSRp7r1MdD8=
github.com/digitalocean/godo v1.126.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
@ -523,8 +523,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
@ -533,8 +533,8 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gookit/color v1.2.3/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v1.14.0 h1:Bt9zQDhPrbd4qX7EILGmy+i7GP35cc+AAL2+wIJpUE8=
github.com/gophercloud/gophercloud v1.14.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
@ -704,8 +704,8 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/linki/instrumented_http v0.3.0 h1:dsN92+mXpfZtjJraartcQ99jnuw7fqsnPDjr85ma2dA=
github.com/linki/instrumented_http v0.3.0/go.mod h1:pjYbItoegfuVi2GUOMhEqzvm/SJKuEL3H0tc8QRLRFk=
github.com/linode/linodego v1.39.0 h1:gRsj2PXX+HTO3eYQaXEuQGsLeeLFDSBDontC5JL3Nn8=
github.com/linode/linodego v1.39.0/go.mod h1:da8KzAQKSm5obwa06yXk5CZSDFMP9Wb08GA/O+aR9W0=
github.com/linode/linodego v1.41.0 h1:GcP7JIBr9iLRJ9FwAtb9/WCT1DuPJS/xUApapfdjtiY=
github.com/linode/linodego v1.41.0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/lyft/protoc-gen-star v0.4.10/go.mod h1:mE8fbna26u7aEA2QCVvvfBU/ZrPgocG1206xAFPcs94=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@ -849,8 +849,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/oracle/oci-go-sdk/v65 v65.71.1 h1:t1GpyLYaD/x2OrUoSyxNwBQaDaQP4F084FX8LQMXA/s=
github.com/oracle/oci-go-sdk/v65 v65.71.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/oracle/oci-go-sdk/v65 v65.75.0 h1:tifYRSqCjxANJb0xnMSZ6N2bF2xGyqcCIMg7xihgk+s=
github.com/oracle/oci-go-sdk/v65 v65.75.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
@ -894,8 +894,8 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -944,8 +944,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8=
github.com/schollz/progressbar/v3 v3.8.6 h1:QruMUdzZ1TbEP++S1m73OqRJk20ON11m6Wqv4EoGg8c=
github.com/schollz/progressbar/v3 v3.8.6/go.mod h1:W5IEwbJecncFGBvuEh4A7HT1nZZ6WNIL2i3qbnI0WKY=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@ -974,8 +974,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us=
github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@ -1017,19 +1017,19 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.984 h1:QLSx+ibsV68NXKgzofPuo1gxFwYSWk2++rvxZxNjbVo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.984/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.984 h1:ABZeSsOOkkBn+gToVp8KkMt4E69hQkBMEFegCD4w15Q=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.984/go.mod h1:r++X8dKvTZWltr4J83TIwqGlyvG5fKaVh7RGC2+BryI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.984 h1:dD0pLtMCJyRNMTystzaZ9WAK+UBb2ymGcdbkhtAub+8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.984/go.mod h1:hWQvbAt8kqN3JLfVpgxsn2YNxDBLaiUaXZFtF4ymRjU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1011 h1:cjqeXTwKKtGVqrf6luwunUnA77buzmzbk+G42US1Sns=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1011/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1011 h1:O3QK5t7HIWhzo9ZtQcWjq+voOL2Anko8ON6bikKTcl0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1011/go.mod h1:HbdUWIghvwUPfIhgFkKZqwaoyALK6GxBzayhflN9wY8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1011 h1:Ak4EA3TtzcFtqY+2E9z+mYBC8gjlw4X8WXlVPoRVtQU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1011/go.mod h1:FYFEkG/OCQ4vDPiuV7LijHSNq+kr14ABC1IswVtHNmM=
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/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/transip/gotransip/v6 v6.25.0 h1:/H+SjMq/9HNZ0/maE1OLhJpxLaCGHsxq0PWaMPJHxK4=
github.com/transip/gotransip/v6 v6.25.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s=
github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550=
github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
@ -1049,8 +1049,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vektah/gqlparser/v2 v2.5.14 h1:dzLq75BJe03jjQm6n56PdH1oweB8ana42wj7E4jRy70=
github.com/vektah/gqlparser/v2 v2.5.14/go.mod h1:WQQjFc+I1YIzoPvZBhUQX7waZgg3pMLi0r8KymvAE2w=
github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8=
github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -1075,12 +1075,12 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE=
go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
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.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@ -1093,14 +1093,14 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -1147,8 +1147,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1174,8 +1174,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1218,14 +1218,14 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1295,8 +1295,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1304,8 +1304,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1317,8 +1317,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1362,8 +1362,8 @@ golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1376,8 +1376,8 @@ gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZ
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.192.0 h1:PljqpNAfZaaSpS+TnANfnNAXKdzHM/B9bKhwRlo7JP0=
google.golang.org/api v0.192.0/go.mod h1:9VcphjvAxPKLmSxVSzPlSRXy/5ARMEw5bf58WoVXafQ=
google.golang.org/api v0.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs=
google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1392,10 +1392,10 @@ google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dT
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk=
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -1410,8 +1410,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -1452,8 +1452,8 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/ns1/ns1-go.v2 v2.12.0 h1:cqdqQoTx17JmTusfxh5m3e2b36jfUzFAZedv89pFX18=
gopkg.in/ns1/ns1-go.v2 v2.12.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
gopkg.in/ns1/ns1-go.v2 v2.12.1 h1:GiiZPB8JusUF/ruyUDzddd70b3HZGa5A3njtKUe84jA=
gopkg.in/ns1/ns1-go.v2 v2.12.1/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@ -1481,23 +1481,23 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
istio.io/api v1.23.0 h1:yqv3lNW6XSYS5XkbEkxsmFROXIQznp4lFWqj7xKEqCA=
istio.io/api v1.23.0/go.mod h1:QPSTGXuIQdnZFEm3myf9NZ5uBMwCdJWUvfj9ZZ+2oBM=
istio.io/client-go v1.23.0 h1://xojbifr84q29WE3eMx74p36hD4lvcejX1KxE3iJvY=
istio.io/client-go v1.23.0/go.mod h1:3qX/KBS5aR47QV4JhphcZl5ysnZ53x78TBjNQLM2TC4=
istio.io/api v1.23.2 h1:FvWi7GC+rWD60/ZFPuulX/h3k+f2Q9qot3dP8CIL8Ss=
istio.io/api v1.23.2/go.mod h1:QPSTGXuIQdnZFEm3myf9NZ5uBMwCdJWUvfj9ZZ+2oBM=
istio.io/client-go v1.23.2 h1:BIt6A+KaUOFin3SzXiDq2Fr/TMBev1+c836R0BfUfhU=
istio.io/client-go v1.23.2/go.mod h1:E08wpMtUulJk2tlWOCUVakjy1bKFxUNm22tM1R1QY0Y=
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4=
k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
@ -1506,8 +1506,8 @@ k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g
k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=

View File

@ -65,7 +65,6 @@ import (
"sigs.k8s.io/external-dns/provider/pdns"
"sigs.k8s.io/external-dns/provider/pihole"
"sigs.k8s.io/external-dns/provider/plural"
"sigs.k8s.io/external-dns/provider/rdns"
"sigs.k8s.io/external-dns/provider/rfc2136"
"sigs.k8s.io/external-dns/provider/scaleway"
"sigs.k8s.io/external-dns/provider/tencentcloud"
@ -258,13 +257,6 @@ func main() {
p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
case "coredns", "skydns":
p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.DryRun)
case "rdns":
p, err = rdns.NewRDNSProvider(
rdns.RDNSConfig{
DomainFilter: domainFilter,
DryRun: cfg.DryRun,
},
)
case "exoscale":
p, err = exoscale.NewExoscaleProvider(
cfg.ExoscaleAPIEnvironment,
@ -286,6 +278,7 @@ func main() {
DomainFilter: domainFilter,
DryRun: cfg.DryRun,
Server: cfg.PDNSServer,
ServerID: cfg.PDNSServerID,
APIKey: cfg.PDNSAPIKey,
TLSConfig: pdns.TLSConfig{
SkipTLSVerify: cfg.PDNSSkipTLSVerify,

View File

@ -123,6 +123,7 @@ type Config struct {
OVHEndpoint string
OVHApiRateLimit int
PDNSServer string
PDNSServerID string
PDNSAPIKey string `secure:"yes"`
PDNSSkipTLSVerify bool
TLSCA string
@ -278,6 +279,7 @@ var defaultConfig = &Config{
OVHEndpoint: "ovh-eu",
OVHApiRateLimit: 20,
PDNSServer: "http://localhost:8081",
PDNSServerID: "localhost",
PDNSAPIKey: "",
PDNSSkipTLSVerify: false,
TLSCA: "",
@ -447,7 +449,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("nat64-networks", "Adding an A record for each AAAA record in NAT64-enabled networks; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.NAT64Networks)
// Flags related to providers
providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rdns", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"}
providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "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-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)
@ -503,6 +505,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("ovh-endpoint", "When using the OVH provider, specify the endpoint (default: ovh-eu)").Default(defaultConfig.OVHEndpoint).StringVar(&cfg.OVHEndpoint)
app.Flag("ovh-api-rate-limit", "When using the OVH provider, specify the API request rate limit, X operations by seconds (default: 20)").Default(strconv.Itoa(defaultConfig.OVHApiRateLimit)).IntVar(&cfg.OVHApiRateLimit)
app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer)
app.Flag("pdns-server-id", "When using the PowerDNS/PDNS provider, specify the id of the server to retrieve. Should be `localhost` except when the server is behind a proxy (optional when --provider=pdns) (default: localhost)").Default(defaultConfig.PDNSServerID).StringVar(&cfg.PDNSServerID)
app.Flag("pdns-api-key", "When using the PowerDNS/PDNS provider, specify the API key to use to authorize requests (required when --provider=pdns)").Default(defaultConfig.PDNSAPIKey).StringVar(&cfg.PDNSAPIKey)
app.Flag("pdns-skip-tls-verify", "When using the PowerDNS/PDNS provider, disable verification of any TLS certificates (optional when --provider=pdns) (default: false)").Default(strconv.FormatBool(defaultConfig.PDNSSkipTLSVerify)).BoolVar(&cfg.PDNSSkipTLSVerify)
app.Flag("ns1-endpoint", "When using the NS1 provider, specify the URL of the API endpoint to target (default: https://api.nsone.net/v1/)").Default(defaultConfig.NS1Endpoint).StringVar(&cfg.NS1Endpoint)

View File

@ -89,6 +89,7 @@ var (
OVHEndpoint: "ovh-eu",
OVHApiRateLimit: 20,
PDNSServer: "http://localhost:8081",
PDNSServerID: "localhost",
PDNSAPIKey: "",
Policy: "sync",
Registry: "txt",
@ -188,6 +189,7 @@ var (
OVHEndpoint: "ovh-ca",
OVHApiRateLimit: 42,
PDNSServer: "http://ns.example.com:8081",
PDNSServerID: "localhost",
PDNSAPIKey: "some-secret-key",
PDNSSkipTLSVerify: true,
TLSCA: "/path/to/ca.crt",
@ -287,6 +289,7 @@ func TestParseFlags(t *testing.T) {
"--ovh-endpoint=ovh-ca",
"--ovh-api-rate-limit=42",
"--pdns-server=http://ns.example.com:8081",
"--pdns-server-id=localhost",
"--pdns-api-key=some-secret-key",
"--pdns-skip-tls-verify",
"--oci-config-file=oci.yaml",
@ -413,6 +416,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_TARGET_NET_FILTER": "10.0.0.0/9\n10.1.0.0/9",
"EXTERNAL_DNS_EXCLUDE_TARGET_NET": "1.0.0.0/9\n1.1.0.0/9",
"EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081",
"EXTERNAL_DNS_PDNS_ID": "localhost",
"EXTERNAL_DNS_PDNS_API_KEY": "some-secret-key",
"EXTERNAL_DNS_PDNS_SKIP_TLS_VERIFY": "1",
"EXTERNAL_DNS_RDNS_ROOT_DOMAIN": "lb.rancher.cloud",

View File

@ -212,16 +212,16 @@ func (m *MockAlibabaCloudPrivateZoneAPI) DescribeZoneInfo(request *pvtz.Describe
response = pvtz.CreateDescribeZoneInfoResponse()
response.ZoneId = m.zone.ZoneId
response.ZoneName = m.zone.ZoneName
response.BindVpcs = pvtz.BindVpcsInDescribeZoneInfo{Vpc: m.zone.Vpcs.Vpc}
response.BindVpcs = pvtz.BindVpcsInDescribeZoneInfo{Vpc: make([]pvtz.VpcInDescribeZoneInfo, len(m.zone.Vpcs.Vpc))}
for idx, vpc := range m.zone.Vpcs.Vpc {
response.BindVpcs.Vpc[idx] = pvtz.VpcInDescribeZoneInfo{VpcName: vpc.VpcName, VpcId: vpc.VpcId, VpcType: vpc.VpcType, RegionName: vpc.RegionName, RegionId: vpc.RegionId}
}
return response, nil
}
func newTestAlibabaCloudProvider(private bool) *AlibabaCloudProvider {
cfg := alibabaCloudConfig{
RegionID: "cn-beijing",
AccessKeyID: "xxxxxx",
AccessKeySecret: "xxxxxx",
VPCID: "vpc-xxxxxx",
VPCID: "vpc-xxxxxx",
}
//
//dnsClient, _ := alidns.NewClientWithAccessKey(

View File

@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
@ -398,6 +399,28 @@ func wildcardUnescape(s string) string {
return strings.Replace(s, "\\052", "*", 1)
}
// See https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html
// convertOctalToAscii decodes inputs that contain octal escape sequences into their original ASCII characters.
// The function returns converted string where any octal escape sequences have been replaced with their corresponding ASCII characters.
func convertOctalToAscii(input string) string {
if !containsOctalSequence(input) {
return input
}
result, err := strconv.Unquote("\"" + input + "\"")
if err != nil {
return input
}
return result
}
// validateDomainName checks if the domain name contains valid octal escape sequences.
func containsOctalSequence(domain string) bool {
// Pattern to match valid octal escape sequences
octalEscapePattern := `\\[0-3][0-7]{2}`
octalEscapeRegex := regexp.MustCompile(octalEscapePattern)
return octalEscapeRegex.MatchString(domain)
}
// Records returns the list of records in a given hosted zone.
func (p *AWSProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, _ error) {
zones, err := p.zones(ctx)
@ -432,6 +455,8 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZon
continue
}
name := convertOctalToAscii(wildcardUnescape(*r.Name))
var ttl endpoint.TTL
if r.TTL != nil {
ttl = endpoint.TTL(*r.TTL)
@ -443,7 +468,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZon
targets[idx] = *rr.Value
}
ep := endpoint.NewEndpointWithTTL(wildcardUnescape(*r.Name), string(r.Type), ttl, targets...)
ep := endpoint.NewEndpointWithTTL(name, string(r.Type), ttl, targets...)
if r.Type == endpoint.RecordTypeCNAME {
ep = ep.WithProviderSpecific(providerSpecificAlias, "false")
}
@ -456,7 +481,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZon
ttl = recordTTL
}
ep := endpoint.
NewEndpointWithTTL(wildcardUnescape(*r.Name), endpoint.RecordTypeA, ttl, *r.AliasTarget.DNSName).
NewEndpointWithTTL(name, endpoint.RecordTypeA, ttl, *r.AliasTarget.DNSName).
WithProviderSpecific(providerSpecificEvaluateTargetHealth, fmt.Sprintf("%t", r.AliasTarget.EvaluateTargetHealth)).
WithProviderSpecific(providerSpecificAlias, "true")
newEndpoints = append(newEndpoints, ep)

View File

@ -141,6 +141,20 @@ func wildcardEscape(s string) string {
return s
}
// Route53 octal escapes https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html
func specialCharactersEscape(s string) string {
var result strings.Builder
for _, char := range s {
if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == '-' || char == '.' {
result.WriteRune(char)
} else {
octalCode := fmt.Sprintf("\\%03o", char)
result.WriteString(octalCode)
}
}
return result.String()
}
func (r *Route53APIStub) ListTagsForResource(ctx context.Context, input *route53.ListTagsForResourceInput, optFns ...func(options *route53.Options)) (*route53.ListTagsForResourceOutput, error) {
if input.ResourceType == route53types.TagResourceTypeHostedzone {
tags := r.zoneTags[*input.ResourceId]
@ -352,11 +366,33 @@ func TestAWSRecords(t *testing.T) {
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
},
{
Name: aws.String("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Name: aws.String(wildcardEscape("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
},
{
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("example")}},
},
{
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
},
{
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
AliasTarget: &route53types.AliasTarget{
DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."),
EvaluateTargetHealth: false,
HostedZoneId: aws.String("Z215JYRZR1TBD5"),
},
},
{
Name: aws.String("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
@ -499,6 +535,9 @@ func TestAWSRecords(t *testing.T) {
endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"),
endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"),
endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "example").WithProviderSpecific(providerSpecificAlias, "false"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "escape-codes.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"),
@ -687,6 +726,14 @@ func TestAWSApplyChanges(t *testing.T) {
TTL: aws.Int64(recordTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("30 mailhost1.foo.elb.amazonaws.com")}},
},
{
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"),
Weight: aws.Int64(10),
},
})
createRecords := []*endpoint.Endpoint{
@ -712,6 +759,7 @@ func TestAWSApplyChanges(t *testing.T) {
endpoint.NewEndpoint("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("before").WithProviderSpecific(providerSpecificWeight, "10"),
endpoint.NewEndpoint("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "10"),
endpoint.NewEndpoint("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost2.bar.elb.amazonaws.com"),
endpoint.NewEndpoint("escape-%!s(<nil>)-codes.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("policy-change").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "10"),
}
updatedRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"),
@ -853,6 +901,14 @@ func TestAWSApplyChanges(t *testing.T) {
},
})
validateRecords(t, listAWSRecords(t, provider.clients[defaultAWSProfile], "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), []route53types.ResourceRecordSet{
{
Name: aws.String("escape-\\045\\041s\\050\\074nil\\076\\051-codes.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"),
Weight: aws.Int64(10),
},
{
Name: aws.String("create-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
@ -2024,3 +2080,34 @@ func TestRequiresDeleteCreate(t *testing.T) {
assert.False(t, provider.requiresDeleteCreate(oldSetIdentifier, oldSetIdentifier), "actual and expected endpoints don't match. %+v:%+v", oldSetIdentifier, oldSetIdentifier)
assert.True(t, provider.requiresDeleteCreate(oldSetIdentifier, newSetIdentifier), "actual and expected endpoints don't match. %+v:%+v", oldSetIdentifier, newSetIdentifier)
}
func TestConvertOctalToAscii(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "Characters escaped !\"#$%&'()*+,-/:;",
input: "txt-\\041\\042\\043\\044\\045\\046\\047\\050\\051\\052\\053\\054-\\057\\072\\073-test.example.com",
expected: "txt-!\"#$%&'()*+,-/:;-test.example.com",
},
{
name: "Characters escaped <=>?@[\\]^_`{|}~",
input: "txt-\\074\\075\\076\\077\\100\\133\\134\\135\\136_\\140\\173\\174\\175\\176-test2.example.com",
expected: "txt-<=>?@[\\]^_`{|}~-test2.example.com",
},
{
name: "No escaped characters in domain",
input: "txt-awesome-test3.example.com",
expected: "txt-awesome-test3.example.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := convertOctalToAscii(tt.input)
assert.Equal(t, tt.expected, actual)
})
}
}

View File

@ -54,7 +54,6 @@ type mockCloudFlareClient struct {
var ExampleDomain = []cloudflare.DNSRecord{
{
ID: "1234567890",
ZoneID: "001",
Name: "foobar.bar.com",
Type: endpoint.RecordTypeA,
TTL: 120,
@ -63,7 +62,6 @@ var ExampleDomain = []cloudflare.DNSRecord{
},
{
ID: "2345678901",
ZoneID: "001",
Name: "foobar.bar.com",
Type: endpoint.RecordTypeA,
TTL: 120,
@ -72,7 +70,6 @@ var ExampleDomain = []cloudflare.DNSRecord{
},
{
ID: "1231231233",
ZoneID: "002",
Name: "bar.foo.com",
Type: endpoint.RecordTypeA,
TTL: 1,
@ -328,7 +325,6 @@ func AssertActions(t *testing.T, provider *CloudFlareProvider, endpoints []*endp
}
err = provider.ApplyChanges(context.Background(), changes)
if err != nil {
t.Fatalf("cannot apply changes, %s", err)
}
@ -1163,7 +1159,6 @@ func TestProviderPropertiesIdempotency(t *testing.T) {
"001": {
{
ID: "1234567890",
ZoneID: "001",
Name: "foobar.bar.com",
Type: endpoint.RecordTypeA,
TTL: 120,
@ -1268,7 +1263,6 @@ func TestCloudflareComplexUpdate(t *testing.T) {
planned := plan.Calculate()
err = provider.ApplyChanges(context.Background(), planned.Changes)
if err != nil {
t.Errorf("should not fail, %s", err)
}
@ -1310,7 +1304,6 @@ func TestCustomTTLWithEnabledProxyNotChanged(t *testing.T) {
"001": {
{
ID: "1234567890",
ZoneID: "001",
Name: "foobar.bar.com",
Type: endpoint.RecordTypeA,
TTL: 1,

View File

@ -20,6 +20,7 @@ import (
"context"
"fmt"
"os"
"strconv"
"strings"
"github.com/digitalocean/godo"
@ -158,13 +159,15 @@ func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoin
endpoints := []*endpoint.Endpoint{}
for _, zone := range zones {
records, err := p.fetchRecords(ctx, zone.Name)
if err != nil {
return nil, err
}
for _, r := range records {
if provider.SupportedRecordType(r.Type) {
if p.SupportedRecordType(r.Type) {
name := r.Name + "." + zone.Name
data := r.Data
// root name is identified by @ and should be
// translated to zone name for the endpoint entry.
@ -172,7 +175,11 @@ func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoin
name = zone.Name
}
ep := endpoint.NewEndpointWithTTL(name, r.Type, endpoint.TTL(r.TTL), r.Data)
if r.Type == endpoint.RecordTypeMX {
data = fmt.Sprintf("%d %s", r.Priority, r.Data)
}
ep := endpoint.NewEndpointWithTTL(name, r.Type, endpoint.TTL(r.TTL), data)
endpoints = append(endpoints, ep)
}
@ -283,16 +290,33 @@ func makeDomainEditRequest(domain, name, recordType, data string, ttl int) *godo
// For some reason the DO API requires the '.' at the end of "data" in case of CNAME request.
// Example: {"type":"CNAME","name":"hello","data":"www.example.com."}
if recordType == endpoint.RecordTypeCNAME && !strings.HasSuffix(data, ".") {
if (recordType == endpoint.RecordTypeCNAME || recordType == endpoint.RecordTypeMX) && !strings.HasSuffix(data, ".") {
data += "."
}
return &godo.DomainRecordEditRequest{
request := &godo.DomainRecordEditRequest{
Name: adjustedName,
Type: recordType,
Data: data,
TTL: ttl,
}
if recordType == endpoint.RecordTypeMX {
priority, domain, err := parseMxTarget(data)
if err == nil {
request.Priority = int(priority)
request.Data = provider.EnsureTrailingDot(domain)
} else {
log.WithFields(log.Fields{
"domain": domain,
"dnsName": name,
"recordType": recordType,
"data": data,
}).Warn("Unable to parse MX target")
}
}
return request
}
// submitChanges applies an instance of `digitalOceanChanges` to the DigitalOcean API.
@ -303,13 +327,19 @@ func (p *DigitalOceanProvider) submitChanges(ctx context.Context, changes *digit
}
for _, c := range changes.Creates {
log.WithFields(log.Fields{
logFields := log.Fields{
"domain": c.Domain,
"dnsName": c.Options.Name,
"recordType": c.Options.Type,
"data": c.Options.Data,
"ttl": c.Options.TTL,
}).Debug("Creating domain record")
}
if c.Options.Type == endpoint.RecordTypeMX {
logFields["priority"] = c.Options.Priority
}
log.WithFields(logFields).Debug("Creating domain record")
if p.DryRun {
continue
@ -322,13 +352,17 @@ func (p *DigitalOceanProvider) submitChanges(ctx context.Context, changes *digit
}
for _, u := range changes.Updates {
log.WithFields(log.Fields{
logFields := log.Fields{
"domain": u.Domain,
"dnsName": u.Options.Name,
"recordType": u.Options.Type,
"data": u.Options.Data,
"ttl": u.Options.TTL,
}).Debug("Updating domain record")
}
if u.Options.Type == endpoint.RecordTypeMX {
logFields["priority"] = u.Options.Priority
}
log.WithFields(logFields).Debug("Updating domain record")
if p.DryRun {
continue
@ -589,6 +623,16 @@ func processDeleteActions(
return nil
}
// SupportedRecordType returns true if the record type is supported by the provider
func (p *DigitalOceanProvider) SupportedRecordType(recordType string) bool {
switch recordType {
case "MX":
return true
default:
return provider.SupportedRecordType(recordType)
}
}
// ApplyChanges applies the given set of generic changes to the provider.
func (p *DigitalOceanProvider) ApplyChanges(ctx context.Context, planChanges *plan.Changes) error {
// TODO: This should only retrieve zones affected by the given `planChanges`.
@ -617,3 +661,18 @@ func (p *DigitalOceanProvider) ApplyChanges(ctx context.Context, planChanges *pl
return p.submitChanges(ctx, &changes)
}
func parseMxTarget(mxTarget string) (priority int64, exchange string, err error) {
targetParts := strings.SplitN(mxTarget, " ", 2)
if len(targetParts) != 2 {
return priority, exchange, fmt.Errorf("mx target needs to be of form '10 example.com'")
}
priorityRaw, exchange := targetParts[0], targetParts[1]
priority, err = strconv.ParseInt(priorityRaw, 10, 32)
if err != nil {
return priority, exchange, fmt.Errorf("invalid priority specified")
}
return priority, exchange, nil
}

View File

@ -100,6 +100,9 @@ func (m *mockDigitalOceanClient) Records(ctx context.Context, domain string, opt
{ID: 1, Name: "foo.ext-dns-test", Type: "CNAME"},
{ID: 2, Name: "bar.ext-dns-test", Type: "CNAME"},
{ID: 3, Name: "@", Type: endpoint.RecordTypeCNAME},
{ID: 4, Name: "@", Type: endpoint.RecordTypeMX, Priority: 10, Data: "mx1.foo.com."},
{ID: 5, Name: "@", Type: endpoint.RecordTypeMX, Priority: 10, Data: "mx2.foo.com."},
{ID: 6, Name: "@", Type: endpoint.RecordTypeTXT, Data: "SOME-TXT-TEXT"},
}, &godo.Response{
Links: &godo.Links{
Pages: &godo.Pages{
@ -344,6 +347,28 @@ func TestDigitalOceanMakeDomainEditRequest(t *testing.T) {
Data: "bar.example.com.",
TTL: customTTL,
}, r4)
// Ensure that MX records have `.` appended.
r5 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
"10 mx.example.com", digitalOceanRecordTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeMX,
Name: "foo",
Data: "mx.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
}, r5)
// Ensure that MX records do not have an extra `.` appended if they already have a `.`
r6 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
"10 mx.example.com.", digitalOceanRecordTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeMX,
Name: "foo",
Data: "mx.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
}, r6)
}
func TestDigitalOceanApplyChanges(t *testing.T) {
@ -375,6 +400,8 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeMX, "10 mx.example.com"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "SOME-TXT-TEXT"),
},
}
@ -382,7 +409,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
err := processCreateActions(recordsByDomain, createsByDomain, &changes)
require.NoError(t, err)
assert.Equal(t, 2, len(changes.Creates))
assert.Equal(t, 4, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
assert.Equal(t, 0, len(changes.Deletes))
@ -405,6 +432,25 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
TTL: digitalOceanRecordTTL,
},
},
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "@",
Type: endpoint.RecordTypeMX,
Priority: 10,
Data: "mx.example.com.",
TTL: digitalOceanRecordTTL,
},
},
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "SOME-TXT-TEXT",
TTL: digitalOceanRecordTTL,
},
},
}
if !elementsMatch(t, expectedCreates, changes.Creates) {
@ -436,6 +482,29 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
},
{
ID: 4,
Name: "@",
Type: endpoint.RecordTypeMX,
Data: "mx1.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
},
{
ID: 5,
Name: "@",
Type: endpoint.RecordTypeMX,
Data: "mx2.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
},
{
ID: 6,
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "SOME_TXTX_TEXT",
TTL: digitalOceanRecordTTL,
},
},
}
@ -443,6 +512,8 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "10.11.12.13"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "bar.example.com"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeMX, "10 mx3.example.com"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "ANOTHER-TXT"),
},
}
@ -450,9 +521,9 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
err := processUpdateActions(recordsByDomain, updatesByDomain, &changes)
require.NoError(t, err)
assert.Equal(t, 2, len(changes.Creates))
assert.Equal(t, 4, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
assert.Equal(t, 3, len(changes.Deletes))
assert.Equal(t, 6, len(changes.Deletes))
expectedCreates := []*digitalOceanChangeCreate{
{
@ -473,6 +544,25 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
TTL: digitalOceanRecordTTL,
},
},
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "@",
Type: endpoint.RecordTypeMX,
Data: "mx3.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
},
},
{
Domain: "example.com",
Options: &godo.DomainRecordEditRequest{
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "ANOTHER-TXT",
TTL: digitalOceanRecordTTL,
},
},
}
if !elementsMatch(t, expectedCreates, changes.Creates) {
@ -492,6 +582,18 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Domain: "example.com",
RecordID: 3,
},
{
Domain: "example.com",
RecordID: 4,
},
{
Domain: "example.com",
RecordID: 5,
},
{
Domain: "example.com",
RecordID: 6,
},
}
if !elementsMatch(t, expectedDeletes, changes.Deletes) {
@ -597,6 +699,26 @@ func TestDigitalOceanGetMatchingDomainRecords(t *testing.T) {
Type: endpoint.RecordTypeA,
Data: "9.10.11.12",
},
{
ID: 5,
Name: "@",
Type: endpoint.RecordTypeMX,
Priority: 10,
Data: "mx1.foo.com.",
},
{
ID: 6,
Name: "@",
Type: endpoint.RecordTypeMX,
Priority: 10,
Data: "mx2.foo.com.",
},
{
ID: 7,
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "MYTXT",
},
}
ep1 := endpoint.NewEndpoint("foo.com", endpoint.RecordTypeCNAME)
@ -627,6 +749,17 @@ func TestDigitalOceanGetMatchingDomainRecords(t *testing.T) {
r2 := getMatchingDomainRecords(records, "example.com", ep4)
assert.Equal(t, 1, len(r2))
assert.Equal(t, "9.10.11.12", r2[0].Data)
ep5 := endpoint.NewEndpoint("example.com", endpoint.RecordTypeMX)
r3 := getMatchingDomainRecords(records, "example.com", ep5)
assert.Equal(t, 2, len(r3))
assert.Equal(t, "mx1.foo.com.", r3[0].Data)
assert.Equal(t, "mx2.foo.com.", r3[1].Data)
ep6 := endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT)
r4 := getMatchingDomainRecords(records, "example.com", ep6)
assert.Equal(t, 1, len(r4))
assert.Equal(t, "MYTXT", r4[0].Data)
}
func validateDigitalOceanZones(t *testing.T, zones []godo.Domain, expected []godo.Domain) {
@ -663,7 +796,7 @@ func TestDigitalOceanAllRecords(t *testing.T) {
if err != nil {
t.Errorf("should not fail, %s", err)
}
require.Equal(t, 5, len(records))
require.Equal(t, 7, len(records))
provider.Client = &mockDigitalOceanRecordsFail{}
_, err = provider.Records(ctx)
@ -678,11 +811,15 @@ func TestDigitalOceanMergeRecordsByNameType(t *testing.T) {
endpoint.NewEndpoint("bar.example.com", "A", "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", "A", "5.6.7.8"),
endpoint.NewEndpoint("foo.example.com", "CNAME", "somewhere.out.there.com"),
endpoint.NewEndpoint("bar.example.com", "MX", "10 bar.mx1.com"),
endpoint.NewEndpoint("bar.example.com", "MX", "10 bar.mx2.com"),
endpoint.NewEndpoint("foo.example.com", "TXT", "txtone"),
endpoint.NewEndpoint("foo.example.com", "TXT", "txttwo"),
}
merged := mergeEndpointsByNameType(xs)
assert.Equal(t, 3, len(merged))
assert.Equal(t, 5, len(merged))
sort.SliceStable(merged, func(i, j int) bool {
if merged[i].DNSName != merged[j].DNSName {
return merged[i].DNSName < merged[j].DNSName
@ -693,14 +830,22 @@ func TestDigitalOceanMergeRecordsByNameType(t *testing.T) {
assert.Equal(t, "A", merged[0].RecordType)
assert.Equal(t, 1, len(merged[0].Targets))
assert.Equal(t, "1.2.3.4", merged[0].Targets[0])
assert.Equal(t, "foo.example.com", merged[1].DNSName)
assert.Equal(t, "A", merged[1].RecordType)
assert.Equal(t, "MX", merged[1].RecordType)
assert.Equal(t, 2, len(merged[1].Targets))
assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[1].Targets)
assert.ElementsMatch(t, []string{"10 bar.mx1.com", "10 bar.mx2.com"}, merged[1].Targets)
assert.Equal(t, "foo.example.com", merged[2].DNSName)
assert.Equal(t, "CNAME", merged[2].RecordType)
assert.Equal(t, 1, len(merged[2].Targets))
assert.Equal(t, "somewhere.out.there.com", merged[2].Targets[0])
assert.Equal(t, "A", merged[2].RecordType)
assert.Equal(t, 2, len(merged[2].Targets))
assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[2].Targets)
assert.Equal(t, "foo.example.com", merged[3].DNSName)
assert.Equal(t, "CNAME", merged[3].RecordType)
assert.Equal(t, 1, len(merged[3].Targets))
assert.Equal(t, "somewhere.out.there.com", merged[3].Targets[0])
assert.Equal(t, "foo.example.com", merged[4].DNSName)
assert.Equal(t, "TXT", merged[4].RecordType)
assert.Equal(t, 2, len(merged[4].Targets))
assert.ElementsMatch(t, []string{"txtone", "txttwo"}, merged[4].Targets)
}

View File

@ -194,7 +194,7 @@ func (p *GoogleProvider) Zones(ctx context.Context) (map[string]*dns.ManagedZone
log.Debugf("Matching zones against domain filters: %v", p.domainFilter)
if err := p.managedZonesClient.List(p.project).Pages(ctx, f); err != nil {
return nil, err
return nil, provider.NewSoftError(fmt.Errorf("failed to list zones: %w", err))
}
if len(zones) == 0 {
@ -228,7 +228,7 @@ func (p *GoogleProvider) Records(ctx context.Context) (endpoints []*endpoint.End
for _, z := range zones {
if err := p.resourceRecordSetsClient.List(p.project, z.Name).Pages(ctx, f); err != nil {
return nil, err
return nil, provider.NewSoftError(fmt.Errorf("failed to list records in zone %s: %w", z.Name, err))
}
}
@ -302,7 +302,7 @@ func (p *GoogleProvider) submitChange(ctx context.Context, change *dns.Change) e
}
if _, err := p.changesClient.Create(p.project, zone, c).Do(); err != nil {
return err
return provider.NewSoftError(fmt.Errorf("failed to create changes: %w", err))
}
time.Sleep(p.batchChangeInterval)

View File

@ -59,7 +59,8 @@ func (m *mockManagedZonesCreateCall) Do(opts ...googleapi.CallOption) (*dns.Mana
}
type mockManagedZonesListCall struct {
project string
project string
zonesListSoftErr error
}
func (m *mockManagedZonesListCall) Pages(ctx context.Context, f func(*dns.ManagedZonesListResponse) error) error {
@ -71,22 +72,29 @@ func (m *mockManagedZonesListCall) Pages(ctx context.Context, f func(*dns.Manage
}
}
if m.zonesListSoftErr != nil {
return m.zonesListSoftErr
}
return f(&dns.ManagedZonesListResponse{ManagedZones: zones})
}
type mockManagedZonesClient struct{}
type mockManagedZonesClient struct {
zonesErr error
}
func (m *mockManagedZonesClient) Create(project string, managedZone *dns.ManagedZone) managedZonesCreateCallInterface {
return &mockManagedZonesCreateCall{project: project, managedZone: managedZone}
}
func (m *mockManagedZonesClient) List(project string) managedZonesListCallInterface {
return &mockManagedZonesListCall{project: project}
return &mockManagedZonesListCall{project: project, zonesListSoftErr: m.zonesErr}
}
type mockResourceRecordSetsListCall struct {
project string
managedZone string
project string
managedZone string
recordsListSoftErr error
}
func (m *mockResourceRecordSetsListCall) Pages(ctx context.Context, f func(*dns.ResourceRecordSetsListResponse) error) error {
@ -102,13 +110,19 @@ func (m *mockResourceRecordSetsListCall) Pages(ctx context.Context, f func(*dns.
resp = append(resp, v)
}
if m.recordsListSoftErr != nil {
return m.recordsListSoftErr
}
return f(&dns.ResourceRecordSetsListResponse{Rrsets: resp})
}
type mockResourceRecordSetsClient struct{}
type mockResourceRecordSetsClient struct {
recordsErr error
}
func (m *mockResourceRecordSetsClient) List(project string, managedZone string) resourceRecordSetsListCallInterface {
return &mockResourceRecordSetsListCall{project: project, managedZone: managedZone}
return &mockResourceRecordSetsListCall{project: project, managedZone: managedZone, recordsListSoftErr: m.recordsErr}
}
type mockChangesCreateCall struct {
@ -242,25 +256,12 @@ func TestGoogleZonesVisibilityFilterPrivatePeering(t *testing.T) {
zones, err := provider.Zones(context.Background())
require.NoError(t, err)
validateZones(t, zones, map[string]*dns.ManagedZone{
"svc-local": {Name: "svc-local", DnsName: "svc.local.", Id: 1005, Visibility: "private"},
})
}
func TestGoogleZones(t *testing.T) {
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
zones, err := provider.Zones(context.Background())
require.NoError(t, err)
validateZones(t, zones, map[string]*dns.ManagedZone{
"zone-1-ext-dns-test-2-gcp-zalan-do": {Name: "zone-1-ext-dns-test-2-gcp-zalan-do", DnsName: "zone-1.ext-dns-test-2.gcp.zalan.do."},
"zone-2-ext-dns-test-2-gcp-zalan-do": {Name: "zone-2-ext-dns-test-2-gcp-zalan-do", DnsName: "zone-2.ext-dns-test-2.gcp.zalan.do."},
"zone-3-ext-dns-test-2-gcp-zalan-do": {Name: "zone-3-ext-dns-test-2-gcp-zalan-do", DnsName: "zone-3.ext-dns-test-2.gcp.zalan.do."},
})
}
func TestGoogleRecords(t *testing.T) {
originalEndpoints := []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(1), "1.2.3.4"),
@ -268,7 +269,7 @@ func TestGoogleRecords(t *testing.T) {
endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(3), "foo.elb.amazonaws.com"),
}
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, originalEndpoints)
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, originalEndpoints, nil, nil)
records, err := provider.Records(context.Background())
require.NoError(t, err)
@ -299,6 +300,8 @@ func TestGoogleRecordsFilter(t *testing.T) {
provider.NewZoneIDFilter([]string{""}),
false,
originalEndpoints,
nil,
nil,
)
// these records should be filtered out since they don't match a hosted zone or domain filter.
@ -343,6 +346,8 @@ func TestGoogleApplyChanges(t *testing.T) {
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"),
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
},
nil,
nil,
)
createRecords := []*endpoint.Endpoint{
@ -407,7 +412,7 @@ func TestGoogleApplyChangesDryRun(t *testing.T) {
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
}
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), true, originalEndpoints)
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), true, originalEndpoints, nil, nil)
createRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
@ -449,12 +454,12 @@ func TestGoogleApplyChangesDryRun(t *testing.T) {
}
func TestGoogleApplyChangesEmpty(t *testing.T) {
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{}, nil, nil)
assert.NoError(t, provider.ApplyChanges(context.Background(), &plan.Changes{}))
}
func TestNewFilteredRecords(t *testing.T) {
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{})
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{}, nil, nil)
records := provider.newFilteredRecords([]*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 1, "8.8.4.4"),
@ -603,6 +608,26 @@ func TestGoogleBatchChangeSetExceedingNameChange(t *testing.T) {
require.Equal(t, 0, len(batchCs))
}
func TestSoftErrListZonesConflict(t *testing.T) {
p := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{}), false, []*endpoint.Endpoint{}, provider.NewSoftError(fmt.Errorf("failed to list zones")), nil)
zones, err := p.Zones(context.Background())
require.Error(t, err)
require.ErrorIs(t, err, provider.SoftError)
require.Empty(t, zones)
}
func TestSoftErrListRecordsConflict(t *testing.T) {
p := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{}), false, []*endpoint.Endpoint{}, nil, provider.NewSoftError(fmt.Errorf("failed to list records in zone")))
records, err := p.Records(context.Background())
require.Error(t, err)
require.ErrorIs(t, err, provider.SoftError)
require.Empty(t, records)
}
func sortChangesByName(cs *dns.Change) {
sort.SliceStable(cs.Additions, func(i, j int) bool {
return cs.Additions[i].Name < cs.Additions[j].Name
@ -647,7 +672,7 @@ func validateChangeRecord(t *testing.T, record *dns.ResourceRecordSet, expected
assert.Equal(t, expected.Type, record.Type)
}
func newGoogleProviderZoneOverlap(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider {
func newGoogleProviderZoneOverlap(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, dryRun bool, _ []*endpoint.Endpoint) *GoogleProvider {
provider := &GoogleProvider{
project: "zalando-external-dns-test",
dryRun: false,
@ -694,7 +719,6 @@ func newGoogleProviderZoneOverlap(t *testing.T, domainFilter endpoint.DomainFilt
Visibility: "private",
})
createZone(t, provider, &dns.ManagedZone{
Name: "svc-local",
DnsName: "svc.local.",
@ -703,27 +727,31 @@ func newGoogleProviderZoneOverlap(t *testing.T, domainFilter endpoint.DomainFilt
})
createZone(t, provider, &dns.ManagedZone{
Name: "svc-local-peer",
DnsName: "svc.local.",
Id: 10006,
Visibility: "private",
Name: "svc-local-peer",
DnsName: "svc.local.",
Id: 10006,
Visibility: "private",
PeeringConfig: &dns.ManagedZonePeeringConfig{TargetNetwork: nil},
})
provider.dryRun = dryRun
return provider
}
func newGoogleProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider {
func newGoogleProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint, zonesErr, recordsErr error) *GoogleProvider {
provider := &GoogleProvider{
project: "zalando-external-dns-test",
dryRun: false,
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
resourceRecordSetsClient: &mockResourceRecordSetsClient{},
managedZonesClient: &mockManagedZonesClient{},
changesClient: &mockChangesClient{},
project: "zalando-external-dns-test",
dryRun: false,
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
resourceRecordSetsClient: &mockResourceRecordSetsClient{
recordsErr: recordsErr,
},
managedZonesClient: &mockManagedZonesClient{
zonesErr: zonesErr,
},
changesClient: &mockChangesClient{},
}
createZone(t, provider, &dns.ManagedZone{
@ -754,10 +782,10 @@ func newGoogleProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDF
return provider
}
func createZone(t *testing.T, provider *GoogleProvider, zone *dns.ManagedZone) {
func createZone(t *testing.T, p *GoogleProvider, zone *dns.ManagedZone) {
zone.Description = "Testing zone for kubernetes.io/external-dns"
if _, err := provider.managedZonesClient.Create("zalando-external-dns-test", zone).Do(); err != nil {
if _, err := p.managedZonesClient.Create("zalando-external-dns-test", zone).Do(); err != nil {
if err, ok := err.(*googleapi.Error); !ok || err.Code != http.StatusConflict {
require.NoError(t, err)
}
@ -770,8 +798,7 @@ func setupGoogleRecords(t *testing.T, provider *GoogleProvider, endpoints []*end
clearGoogleRecords(t, provider, "zone-3-ext-dns-test-2-gcp-zalan-do")
ctx := context.Background()
records, err := provider.Records(ctx)
require.NoError(t, err)
records, _ := provider.Records(ctx)
validateEndpoints(t, records, []*endpoint.Endpoint{})
@ -779,15 +806,15 @@ func setupGoogleRecords(t *testing.T, provider *GoogleProvider, endpoints []*end
Create: endpoints,
}))
records, err = provider.Records(ctx)
require.NoError(t, err)
records, _ = provider.Records(ctx)
validateEndpoints(t, records, endpoints)
}
func clearGoogleRecords(t *testing.T, provider *GoogleProvider, zone string) {
recordSets := []*dns.ResourceRecordSet{}
require.NoError(t, provider.resourceRecordSetsClient.List(provider.project, zone).Pages(context.Background(), func(resp *dns.ResourceRecordSetsListResponse) error {
provider.resourceRecordSetsClient.List(provider.project, zone).Pages(context.Background(), func(resp *dns.ResourceRecordSetsListResponse) error {
for _, r := range resp.Rrsets {
switch r.Type {
case endpoint.RecordTypeA, endpoint.RecordTypeCNAME:
@ -795,7 +822,7 @@ func clearGoogleRecords(t *testing.T, provider *GoogleProvider, zone string) {
}
}
return nil
}))
})
if len(recordSets) != 0 {
_, err := provider.changesClient.Create(provider.project, zone, &dns.Change{

View File

@ -43,9 +43,7 @@ type pdnsChangeType string
const (
apiBase = "/api/v1"
// Unless we use something like pdnsproxy (discontinued upstream), this value will _always_ be localhost
defaultServerID = "localhost"
defaultTTL = 300
defaultTTL = 300
// PdnsDelete and PdnsReplace are effectively an enum for "pgo.RrSet.changetype"
// TODO: Can we somehow get this from the pgo swagger client library itself?
@ -66,6 +64,7 @@ type PDNSConfig struct {
DomainFilter endpoint.DomainFilter
DryRun bool
Server string
ServerID string
APIKey string
TLSConfig TLSConfig
}
@ -137,6 +136,7 @@ type PDNSAPIProvider interface {
// PDNSAPIClient : Struct that encapsulates all the PowerDNS specific implementation details
type PDNSAPIClient struct {
dryRun bool
serverID string
authCtx context.Context
client *pgo.APIClient
domainFilter endpoint.DomainFilter
@ -146,7 +146,7 @@ type PDNSAPIClient struct {
// ref: https://doc.powerdns.com/authoritative/http-api/zone.html#get--servers-server_id-zones
func (c *PDNSAPIClient) ListZones() (zones []pgo.Zone, resp *http.Response, err error) {
for i := 0; i < retryLimit; i++ {
zones, resp, err = c.client.ZonesApi.ListZones(c.authCtx, defaultServerID)
zones, resp, err = c.client.ZonesApi.ListZones(c.authCtx, c.serverID)
if err != nil {
log.Debugf("Unable to fetch zones %v", err)
log.Debugf("Retrying ListZones() ... %d", i)
@ -180,7 +180,7 @@ func (c *PDNSAPIClient) PartitionZones(zones []pgo.Zone) (filteredZones []pgo.Zo
// ref: https://doc.powerdns.com/authoritative/http-api/zone.html#get--servers-server_id-zones-zone_id
func (c *PDNSAPIClient) ListZone(zoneID string) (zone pgo.Zone, resp *http.Response, err error) {
for i := 0; i < retryLimit; i++ {
zone, resp, err = c.client.ZonesApi.ListZone(c.authCtx, defaultServerID, zoneID)
zone, resp, err = c.client.ZonesApi.ListZone(c.authCtx, c.serverID, zoneID)
if err != nil {
log.Debugf("Unable to fetch zone %v", err)
log.Debugf("Retrying ListZone() ... %d", i)
@ -198,7 +198,7 @@ func (c *PDNSAPIClient) ListZone(zoneID string) (zone pgo.Zone, resp *http.Respo
// ref: https://doc.powerdns.com/authoritative/http-api/zone.html#patch--servers-server_id-zones-zone_id
func (c *PDNSAPIClient) PatchZone(zoneID string, zoneStruct pgo.Zone) (resp *http.Response, err error) {
for i := 0; i < retryLimit; i++ {
resp, err = c.client.ZonesApi.PatchZone(c.authCtx, defaultServerID, zoneID, zoneStruct)
resp, err = c.client.ZonesApi.PatchZone(c.authCtx, c.serverID, zoneID, zoneStruct)
if err != nil {
log.Debugf("Unable to patch zone %v", err)
log.Debugf("Retrying PatchZone() ... %d", i)
@ -245,6 +245,7 @@ func NewPDNSProvider(ctx context.Context, config PDNSConfig) (*PDNSProvider, err
provider := &PDNSProvider{
client: &PDNSAPIClient{
dryRun: config.DryRun,
serverID: config.ServerID,
authCtx: context.WithValue(ctx, pgo.ContextAPIKey, pgo.APIKey{Key: config.APIKey}),
client: pgo.NewAPIClient(pdnsClientConfig),
domainFilter: config.DomainFilter,
@ -314,7 +315,7 @@ func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changet
records := []pgo.Record{}
RecordType_ := ep.RecordType
for _, t := range ep.Targets {
if ep.RecordType == "CNAME" || ep.RecordType == "ALIAS" {
if ep.RecordType == "CNAME" || ep.RecordType == "ALIAS" || ep.RecordType == "MX" || ep.RecordType == "SRV" {
t = provider.EnsureTrailingDot(t)
}
records = append(records, pgo.Record{Content: t})

View File

@ -117,6 +117,27 @@ var (
},
}
// RRSet with MX record
RRSetMXRecord = pgo.RrSet{
Name: "example.com.",
Type_: "MX",
Ttl: 300,
Records: []pgo.Record{
{Content: "10 mailhost1.example.com", Disabled: false, SetPtr: false},
{Content: "10 mailhost2.example.com", Disabled: false, SetPtr: false},
},
}
// RRSet with SRV record
RRSetSRVRecord = pgo.RrSet{
Name: "_service._tls.example.com.",
Type_: "SRV",
Ttl: 300,
Records: []pgo.Record{
{Content: "100 1 443 service.example.com", Disabled: false, SetPtr: false},
},
}
endpointsDisabledRecord = []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(300), "8.8.8.8"),
}
@ -144,6 +165,8 @@ var (
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(300), "'would smell as sweet'"),
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(300), "8.8.8.8", "8.8.4.4", "4.4.4.4"),
endpoint.NewEndpointWithTTL("alias.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(300), "example.by.any.other.name.com"),
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeMX, endpoint.TTL(300), "10 mailhost1.example.com", "10 mailhost2.example.com"),
endpoint.NewEndpointWithTTL("_service._tls.example.com", endpoint.RecordTypeSRV, endpoint.TTL(300), "100 1 443 service.example.com"),
}
endpointsMultipleZones = []*endpoint.Endpoint{
@ -233,7 +256,7 @@ var (
Type_: "Zone",
Url: "/api/v1/servers/localhost/zones/example.com.",
Kind: "Native",
Rrsets: []pgo.RrSet{RRSetCNAMERecord, RRSetTXTRecord, RRSetMultipleRecords, RRSetALIASRecord},
Rrsets: []pgo.RrSet{RRSetCNAMERecord, RRSetTXTRecord, RRSetMultipleRecords, RRSetALIASRecord, RRSetMXRecord, RRSetSRVRecord},
}
ZoneEmptyToSimplePatch = pgo.Zone{
@ -944,6 +967,20 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSConvertEndpointsToZones() {
}
}
// Check endpoints of type MX and SRV always have their values end with a trailing dot.
zlist, err = p.ConvertEndpointsToZones(endpointsMixedRecords, PdnsReplace)
assert.Nil(suite.T(), err)
for _, z := range zlist {
for _, rs := range z.Rrsets {
if rs.Type_ == "MX" || rs.Type_ == "SRV" {
for _, r := range rs.Records {
assert.Equal(suite.T(), uint8(0x2e), r.Content[len(r.Content)-1])
}
}
}
}
// Check endpoints of type CNAME are converted to ALIAS on the domain apex
zlist, err = p.ConvertEndpointsToZones(endpointsApexRecords, PdnsReplace)
assert.Nil(suite.T(), err)

View File

@ -1,551 +0,0 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rdns
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"math/rand"
"os"
"regexp"
"strings"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
clientv3 "go.etcd.io/etcd/client/v3"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)
const (
etcdTimeout = 5 * time.Second
rdnsMaxHosts = 10
rdnsOriginalLabel = "originalText"
rdnsPrefix = "/rdnsv3"
rdnsTimeout = 5 * time.Second
)
func init() {
rand.New(rand.NewSource(time.Now().UnixNano()))
}
// RDNSClient is an interface to work with Rancher DNS(RDNS) records in etcdv3 backend.
type RDNSClient interface {
Get(key string) ([]RDNSRecord, error)
List(rootDomain string) ([]RDNSRecord, error)
Set(value RDNSRecord) error
Delete(key string) error
}
// RDNSConfig contains configuration to create a new Rancher DNS(RDNS) provider.
type RDNSConfig struct {
DryRun bool
DomainFilter endpoint.DomainFilter
RootDomain string
}
// RDNSProvider is an implementation of Provider for Rancher DNS(RDNS).
type RDNSProvider struct {
provider.BaseProvider
client RDNSClient
dryRun bool
domainFilter endpoint.DomainFilter
rootDomain string
}
// RDNSRecord represents Rancher DNS(RDNS) etcdv3 record.
type RDNSRecord struct {
AggregationHosts []string `json:"aggregation_hosts,omitempty"`
Host string `json:"host,omitempty"`
Text string `json:"text,omitempty"`
TTL uint32 `json:"ttl,omitempty"`
Key string `json:"-"`
}
// RDNSRecordType represents Rancher DNS(RDNS) etcdv3 record type.
type RDNSRecordType struct {
Type string `json:"type,omitempty"`
Domain string `json:"domain,omitempty"`
}
type etcdv3Client struct {
client *clientv3.Client
ctx context.Context
}
var _ RDNSClient = etcdv3Client{}
// NewRDNSProvider initializes a new Rancher DNS(RDNS) based Provider.
func NewRDNSProvider(config RDNSConfig) (*RDNSProvider, error) {
client, err := newEtcdv3Client()
if err != nil {
return nil, err
}
domain := os.Getenv("RDNS_ROOT_DOMAIN")
if domain == "" {
return nil, errors.New("needed root domain environment")
}
return &RDNSProvider{
client: client,
dryRun: config.DryRun,
domainFilter: config.DomainFilter,
rootDomain: domain,
}, nil
}
// Records returns all DNS records found in Rancher DNS(RDNS) etcdv3 backend. Depending on the record fields
// it may be mapped to one or two records of type A, TXT, A+TXT.
func (p RDNSProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
var result []*endpoint.Endpoint
rs, err := p.client.List(p.rootDomain)
if err != nil {
return nil, err
}
for _, r := range rs {
domains := strings.Split(strings.TrimPrefix(r.Key, rdnsPrefix+"/"), "/")
keyToDNSNameSplits(domains)
dnsName := strings.Join(domains, ".")
if !p.domainFilter.Match(dnsName) {
continue
}
// only return rdnsMaxHosts at most
if len(r.AggregationHosts) > 0 {
if len(r.AggregationHosts) > rdnsMaxHosts {
r.AggregationHosts = r.AggregationHosts[:rdnsMaxHosts]
}
ep := endpoint.NewEndpointWithTTL(
dnsName,
endpoint.RecordTypeA,
endpoint.TTL(r.TTL),
r.AggregationHosts...,
)
ep.Labels[rdnsOriginalLabel] = r.Text
result = append(result, ep)
}
if r.Text != "" {
ep := endpoint.NewEndpoint(
dnsName,
endpoint.RecordTypeTXT,
r.Text,
)
result = append(result, ep)
}
}
return result, nil
}
// ApplyChanges stores changes back to etcdv3 converting them to Rancher DNS(RDNS) format and aggregating A and TXT records.
func (p RDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
grouped := map[string][]*endpoint.Endpoint{}
for _, ep := range changes.Create {
grouped[ep.DNSName] = append(grouped[ep.DNSName], ep)
}
for _, ep := range changes.UpdateNew {
if ep.RecordType == endpoint.RecordTypeA {
// append useless domain records to the changes.Delete
if err := p.filterAndRemoveUseless(ep, changes); err != nil {
return err
}
}
grouped[ep.DNSName] = append(grouped[ep.DNSName], ep)
}
for dnsName, group := range grouped {
if !p.domainFilter.Match(dnsName) {
log.Debugf("Skipping record %s because it was filtered out by the specified --domain-filter", dnsName)
continue
}
var rs []RDNSRecord
for _, ep := range group {
if ep.RecordType == endpoint.RecordTypeTXT {
continue
}
for _, target := range ep.Targets {
rs = append(rs, RDNSRecord{
Host: target,
Text: ep.Labels[rdnsOriginalLabel],
Key: keyFor(ep.DNSName) + "/" + formatKey(target),
TTL: uint32(ep.RecordTTL),
})
}
}
// Add the TXT attribute to the existing A record
for _, ep := range group {
if ep.RecordType != endpoint.RecordTypeTXT {
continue
}
for i, r := range rs {
if strings.Contains(r.Key, keyFor(ep.DNSName)) {
r.Text = ep.Targets[0]
rs[i] = r
}
}
}
for _, r := range rs {
log.Infof("Add/set key %s to Host=%s, Text=%s, TTL=%d", r.Key, r.Host, r.Text, r.TTL)
if !p.dryRun {
err := p.client.Set(r)
if err != nil {
return err
}
}
}
}
for _, ep := range changes.Delete {
key := keyFor(ep.DNSName)
log.Infof("Delete key %s", key)
if !p.dryRun {
err := p.client.Delete(key)
if err != nil {
return err
}
}
}
return nil
}
// filterAndRemoveUseless filter and remove useless records.
func (p *RDNSProvider) filterAndRemoveUseless(ep *endpoint.Endpoint, changes *plan.Changes) error {
rs, err := p.client.Get(keyFor(ep.DNSName))
if err != nil {
return err
}
for _, r := range rs {
exist := false
for _, target := range ep.Targets {
if strings.Contains(r.Key, formatKey(target)) {
exist = true
continue
}
}
if !exist {
ds := strings.Split(strings.TrimPrefix(r.Key, rdnsPrefix+"/"), "/")
keyToDNSNameSplits(ds)
changes.Delete = append(changes.Delete, &endpoint.Endpoint{
DNSName: strings.Join(ds, "."),
})
}
}
return nil
}
// newEtcdv3Client is an etcdv3 client constructor.
func newEtcdv3Client() (RDNSClient, error) {
cfg := &clientv3.Config{}
endpoints := os.Getenv("ETCD_URLS")
ca := os.Getenv("ETCD_CA_FILE")
cert := os.Getenv("ETCD_CERT_FILE")
key := os.Getenv("ETCD_KEY_FILE")
name := os.Getenv("ETCD_TLS_SERVER_NAME")
insecure := os.Getenv("ETCD_TLS_INSECURE")
if endpoints == "" {
endpoints = "http://localhost:2379"
}
urls := strings.Split(endpoints, ",")
scheme := strings.ToLower(urls[0])[0:strings.Index(strings.ToLower(urls[0]), "://")]
switch scheme {
case "http":
cfg.Endpoints = urls
case "https":
var certificates []tls.Certificate
insecure = strings.ToLower(insecure)
isInsecure := insecure == "true" || insecure == "yes" || insecure == "1"
if ca != "" && key == "" || cert == "" && key != "" {
return nil, errors.New("either both cert and key or none must be provided")
}
if cert != "" {
cert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return nil, fmt.Errorf("could not load TLS cert: %w", err)
}
certificates = append(certificates, cert)
}
config := &tls.Config{
Certificates: certificates,
InsecureSkipVerify: isInsecure,
ServerName: name,
}
if ca != "" {
roots := x509.NewCertPool()
pem, err := os.ReadFile(ca)
if err != nil {
return nil, fmt.Errorf("error reading %s: %w", ca, err)
}
ok := roots.AppendCertsFromPEM(pem)
if !ok {
return nil, fmt.Errorf("could not read root certs: %w", err)
}
config.RootCAs = roots
}
cfg.Endpoints = urls
cfg.TLS = config
default:
return nil, errors.New("etcdv3 URLs must start with either http:// or https://")
}
c, err := clientv3.New(*cfg)
if err != nil {
return nil, err
}
return etcdv3Client{c, context.Background()}, nil
}
// Get return A records stored in etcdv3 stored anywhere under the given key (recursively).
func (c etcdv3Client) Get(key string) ([]RDNSRecord, error) {
ctx, cancel := context.WithTimeout(c.ctx, rdnsTimeout)
defer cancel()
result, err := c.client.Get(ctx, key, clientv3.WithPrefix())
if err != nil {
return nil, err
}
rs := make([]RDNSRecord, 0)
for _, v := range result.Kvs {
r := new(RDNSRecord)
if err := json.Unmarshal(v.Value, r); err != nil {
return nil, fmt.Errorf("%s: %w", v.Key, err)
}
r.Key = string(v.Key)
rs = append(rs, *r)
}
return rs, nil
}
// List return all records stored in etcdv3 stored anywhere under the given rootDomain (recursively).
func (c etcdv3Client) List(rootDomain string) ([]RDNSRecord, error) {
ctx, cancel := context.WithTimeout(c.ctx, rdnsTimeout)
defer cancel()
path := keyFor(rootDomain)
result, err := c.client.Get(ctx, path, clientv3.WithPrefix())
if err != nil {
return nil, err
}
return c.aggregationRecords(result)
}
// Set persists records data into etcdv3.
func (c etcdv3Client) Set(r RDNSRecord) error {
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
defer cancel()
v, err := json.Marshal(&r)
if err != nil {
return err
}
if r.Text == "" && r.Host == "" {
return nil
}
_, err = c.client.Put(ctx, r.Key, string(v))
if err != nil {
return err
}
return nil
}
// Delete deletes record from etcdv3.
func (c etcdv3Client) Delete(key string) error {
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
defer cancel()
_, err := c.client.Delete(ctx, key, clientv3.WithPrefix())
return err
}
// aggregationRecords will aggregation multi A records under the given path.
// e.g. A: 1_1_1_1.xxx.lb.rancher.cloud & 2_2_2_2.sample.lb.rancher.cloud => sample.lb.rancher.cloud {"aggregation_hosts": ["1.1.1.1", "2.2.2.2"]}
// e.g. TXT: sample.lb.rancher.cloud => sample.lb.rancher.cloud => {"text": "xxx"}
func (c etcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRecord, error) {
var rs []RDNSRecord
bx := make(map[RDNSRecordType]RDNSRecord)
for _, n := range result.Kvs {
r := new(RDNSRecord)
if err := json.Unmarshal(n.Value, r); err != nil {
return nil, fmt.Errorf("%s: %w", n.Key, err)
}
r.Key = string(n.Key)
if r.Host == "" && r.Text == "" {
continue
}
if r.Host != "" {
c := RDNSRecord{
AggregationHosts: r.AggregationHosts,
Host: r.Host,
Text: r.Text,
TTL: r.TTL,
Key: r.Key,
}
n, isContinue := appendRecords(c, endpoint.RecordTypeA, bx, rs)
if isContinue {
continue
}
rs = n
}
if r.Text != "" && r.Host == "" {
c := RDNSRecord{
AggregationHosts: []string{},
Host: r.Host,
Text: r.Text,
TTL: r.TTL,
Key: r.Key,
}
n, isContinue := appendRecords(c, endpoint.RecordTypeTXT, bx, rs)
if isContinue {
continue
}
rs = n
}
}
return rs, nil
}
// appendRecords append record to an array
func appendRecords(r RDNSRecord, dnsType string, bx map[RDNSRecordType]RDNSRecord, rs []RDNSRecord) ([]RDNSRecord, bool) {
dnsName := keyToParentDNSName(r.Key)
bt := RDNSRecordType{Domain: dnsName, Type: dnsType}
if v, ok := bx[bt]; ok {
// skip the TXT records if already added to record list.
// append A record if dnsName already added to record list but not found the value.
// the same record might be found in multiple etcdv3 nodes.
if bt.Type == endpoint.RecordTypeA {
exist := false
for _, h := range v.AggregationHosts {
if h == r.Host {
exist = true
break
}
}
if !exist {
for i, t := range rs {
if !strings.HasPrefix(r.Key, t.Key) {
continue
}
t.Host = ""
t.AggregationHosts = append(t.AggregationHosts, r.Host)
bx[bt] = t
rs[i] = t
}
}
}
return rs, true
}
if bt.Type == endpoint.RecordTypeA {
r.AggregationHosts = append(r.AggregationHosts, r.Host)
}
r.Key = rdnsPrefix + dnsNameToKey(dnsName)
r.Host = ""
bx[bt] = r
rs = append(rs, r)
return rs, false
}
// keyFor used to get a path as etcdv3 preferred.
// e.g. sample.lb.rancher.cloud => /rdnsv3/cloud/rancher/lb/sample
func keyFor(fqdn string) string {
return rdnsPrefix + dnsNameToKey(fqdn)
}
// keyToParentDNSName used to get dnsName.
// e.g. /rdnsv3/cloud/rancher/lb/sample/xxx => xxx.sample.lb.rancher.cloud
// e.g. /rdnsv3/cloud/rancher/lb/sample/xxx/1_1_1_1 => xxx.sample.lb.rancher.cloud
func keyToParentDNSName(key string) string {
ds := strings.Split(strings.TrimPrefix(key, rdnsPrefix+"/"), "/")
keyToDNSNameSplits(ds)
dns := strings.Join(ds, ".")
prefix := strings.Split(dns, ".")[0]
p := `^\d{1,3}_\d{1,3}_\d{1,3}_\d{1,3}$`
m, _ := regexp.MatchString(p, prefix)
if prefix != "" && strings.Contains(prefix, "_") && m {
// 1_1_1_1.xxx.sample.lb.rancher.cloud => xxx.sample.lb.rancher.cloud
return strings.Join(strings.Split(dns, ".")[1:], ".")
}
return dns
}
// dnsNameToKey used to convert domain to a path as etcdv3 preferred.
// e.g. sample.lb.rancher.cloud => /cloud/rancher/lb/sample
func dnsNameToKey(domain string) string {
ss := strings.Split(domain, ".")
last := len(ss) - 1
for i := 0; i < len(ss)/2; i++ {
ss[i], ss[last-i] = ss[last-i], ss[i]
}
return "/" + strings.Join(ss, "/")
}
// keyToDNSNameSplits used to reverse etcdv3 path to domain splits.
// e.g. /cloud/rancher/lb/sample => [sample lb rancher cloud]
func keyToDNSNameSplits(ss []string) {
for i := 0; i < len(ss)/2; i++ {
j := len(ss) - i - 1
ss[i], ss[j] = ss[j], ss[i]
}
}
// formatKey used to format a key as etcdv3 preferred
// e.g. 1.1.1.1 => 1_1_1_1
// e.g. sample.lb.rancher.cloud => sample_lb_rancher_cloud
func formatKey(key string) string {
return strings.Replace(key, ".", "_", -1)
}

View File

@ -1,355 +0,0 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rdns
import (
"context"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
type fakeEtcdv3Client struct {
rs map[string]RDNSRecord
}
func (c fakeEtcdv3Client) Get(key string) ([]RDNSRecord, error) {
rs := make([]RDNSRecord, 0)
for k, v := range c.rs {
if strings.Contains(k, key) {
rs = append(rs, v)
}
}
return rs, nil
}
func (c fakeEtcdv3Client) List(rootDomain string) ([]RDNSRecord, error) {
var result []RDNSRecord
for key, value := range c.rs {
rootPath := rdnsPrefix + dnsNameToKey(rootDomain)
if strings.HasPrefix(key, rootPath) {
value.Key = key
result = append(result, value)
}
}
r := &clientv3.GetResponse{}
for _, v := range result {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
k := &mvccpb.KeyValue{
Key: []byte(v.Key),
Value: b,
}
r.Kvs = append(r.Kvs, k)
}
return c.aggregationRecords(r)
}
func (c fakeEtcdv3Client) Set(r RDNSRecord) error {
c.rs[r.Key] = r
return nil
}
func (c fakeEtcdv3Client) Delete(key string) error {
ks := make([]string, 0)
for k := range c.rs {
if strings.Contains(k, key) {
ks = append(ks, k)
}
}
for _, v := range ks {
delete(c.rs, v)
}
return nil
}
func TestARecordTranslation(t *testing.T) {
expectedTarget1 := "1.2.3.4"
expectedTarget2 := "2.3.4.5"
expectedTargets := []string{expectedTarget1, expectedTarget2}
expectedDNSName := "p1xaf1.lb.rancher.cloud"
expectedRecordType := endpoint.RecordTypeA
client := fakeEtcdv3Client{
map[string]RDNSRecord{
"/rdnsv3/cloud/rancher/lb/p1xaf1/1_2_3_4": {Host: expectedTarget1},
"/rdnsv3/cloud/rancher/lb/p1xaf1/2_3_4_5": {Host: expectedTarget2},
},
}
provider := RDNSProvider{
client: client,
rootDomain: "lb.rancher.cloud",
}
endpoints, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
if len(endpoints) != 1 {
t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
}
ep := endpoints[0]
if ep.DNSName != expectedDNSName {
t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName)
}
assert.Contains(t, expectedTargets, ep.Targets[0])
assert.Contains(t, expectedTargets, ep.Targets[1])
if ep.RecordType != expectedRecordType {
t.Errorf("got unexpected DNS record type: %s != %s", ep.RecordType, expectedRecordType)
}
}
func TestTXTRecordTranslation(t *testing.T) {
expectedTarget := "string"
expectedDNSName := "p1xaf1.lb.rancher.cloud"
expectedRecordType := endpoint.RecordTypeTXT
client := fakeEtcdv3Client{
map[string]RDNSRecord{
"/rdnsv3/cloud/rancher/lb/p1xaf1": {Text: expectedTarget},
},
}
provider := RDNSProvider{
client: client,
rootDomain: "lb.rancher.cloud",
}
endpoints, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
if len(endpoints) != 1 {
t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
}
if endpoints[0].DNSName != expectedDNSName {
t.Errorf("got unexpected DNS name: %s != %s", endpoints[0].DNSName, expectedDNSName)
}
if endpoints[0].Targets[0] != expectedTarget {
t.Errorf("got unexpected DNS target: %s != %s", endpoints[0].Targets[0], expectedTarget)
}
if endpoints[0].RecordType != expectedRecordType {
t.Errorf("got unexpected DNS record type: %s != %s", endpoints[0].RecordType, expectedRecordType)
}
}
func TestAWithTXTRecordTranslation(t *testing.T) {
expectedTargets := map[string]string{
endpoint.RecordTypeA: "1.2.3.4",
endpoint.RecordTypeTXT: "string",
}
expectedDNSName := "p1xaf1.lb.rancher.cloud"
client := fakeEtcdv3Client{
map[string]RDNSRecord{
"/rdnsv3/cloud/rancher/lb/p1xaf1": {Host: "1.2.3.4", Text: "string"},
},
}
provider := RDNSProvider{
client: client,
rootDomain: "lb.rancher.cloud",
}
endpoints, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
if len(endpoints) != len(expectedTargets) {
t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
}
for _, ep := range endpoints {
expectedTarget := expectedTargets[ep.RecordType]
if expectedTarget == "" {
t.Errorf("got unexpected DNS record type: %s", ep.RecordType)
continue
}
delete(expectedTargets, ep.RecordType)
if ep.DNSName != expectedDNSName {
t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName)
}
if ep.Targets[0] != expectedTarget {
t.Errorf("got unexpected DNS target: %s != %s", ep.Targets[0], expectedTarget)
}
}
}
func TestRDNSApplyChanges(t *testing.T) {
client := fakeEtcdv3Client{
map[string]RDNSRecord{},
}
provider := RDNSProvider{
client: client,
rootDomain: "lb.rancher.cloud",
}
changes1 := &plan.Changes{
Create: []*endpoint.Endpoint{
endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "5.5.5.5", "6.6.6.6"),
endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeTXT, "string1"),
},
}
if err := provider.ApplyChanges(context.Background(), changes1); err != nil {
t.Error(err)
}
expectedRecords1 := map[string]RDNSRecord{
"/rdnsv3/cloud/rancher/lb/p1xaf1/5_5_5_5": {Host: "5.5.5.5", Text: "string1"},
"/rdnsv3/cloud/rancher/lb/p1xaf1/6_6_6_6": {Host: "6.6.6.6", Text: "string1"},
}
client.validateRecords(client.rs, expectedRecords1, t)
changes2 := &plan.Changes{
Create: []*endpoint.Endpoint{
endpoint.NewEndpoint("abx1v1.lb.rancher.cloud", endpoint.RecordTypeA, "7.7.7.7"),
},
UpdateNew: []*endpoint.Endpoint{
endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "8.8.8.8", "9.9.9.9"),
},
}
records, _ := provider.Records(context.Background())
for _, ep := range records {
if ep.DNSName == "p1xaf1.lb.rancher.cloud" {
changes2.UpdateOld = append(changes2.UpdateOld, ep)
}
}
if err := provider.ApplyChanges(context.Background(), changes2); err != nil {
t.Error(err)
}
expectedRecords2 := map[string]RDNSRecord{
"/rdnsv3/cloud/rancher/lb/p1xaf1/8_8_8_8": {Host: "8.8.8.8"},
"/rdnsv3/cloud/rancher/lb/p1xaf1/9_9_9_9": {Host: "9.9.9.9"},
"/rdnsv3/cloud/rancher/lb/abx1v1/7_7_7_7": {Host: "7.7.7.7"},
}
client.validateRecords(client.rs, expectedRecords2, t)
changes3 := &plan.Changes{
Delete: []*endpoint.Endpoint{
endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "8.8.8.8", "9.9.9.9"),
},
}
if err := provider.ApplyChanges(context.Background(), changes3); err != nil {
t.Error(err)
}
expectedRecords3 := map[string]RDNSRecord{
"/rdnsv3/cloud/rancher/lb/abx1v1/7_7_7_7": {Host: "7.7.7.7"},
}
client.validateRecords(client.rs, expectedRecords3, t)
}
func (c fakeEtcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRecord, error) {
var rs []RDNSRecord
bx := make(map[RDNSRecordType]RDNSRecord)
for _, n := range result.Kvs {
r := new(RDNSRecord)
if err := json.Unmarshal(n.Value, r); err != nil {
return nil, fmt.Errorf("%s: %s", n.Key, err.Error())
}
r.Key = string(n.Key)
if r.Host == "" && r.Text == "" {
continue
}
if r.Host != "" {
c := RDNSRecord{
AggregationHosts: r.AggregationHosts,
Host: r.Host,
Text: r.Text,
TTL: r.TTL,
Key: r.Key,
}
n, isContinue := appendRecords(c, endpoint.RecordTypeA, bx, rs)
if isContinue {
continue
}
rs = n
}
if r.Text != "" && r.Host == "" {
c := RDNSRecord{
AggregationHosts: []string{},
Host: r.Host,
Text: r.Text,
TTL: r.TTL,
Key: r.Key,
}
n, isContinue := appendRecords(c, endpoint.RecordTypeTXT, bx, rs)
if isContinue {
continue
}
rs = n
}
}
return rs, nil
}
func (c fakeEtcdv3Client) validateRecords(rs, expectedRs map[string]RDNSRecord, t *testing.T) {
if len(rs) != len(expectedRs) {
t.Errorf("wrong number of records: %d != %d", len(rs), len(expectedRs))
}
for key, value := range rs {
if _, ok := expectedRs[key]; !ok {
t.Errorf("unexpected record %s", key)
continue
}
expected := expectedRs[key]
delete(expectedRs, key)
if value.Host != expected.Host {
t.Errorf("wrong host for record %s: %s != %s", key, value.Host, expected.Host)
}
if value.Text != expected.Text {
t.Errorf("wrong text for record %s: %s != %s", key, value.Text, expected.Text)
}
}
}