mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Merge branch 'kubernetes-sigs:master' into aws-sd-tags
This commit is contained in:
commit
ad744bda83
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -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
|
||||
|
2
.github/workflows/codeql-analysis.yaml
vendored
2
.github/workflows/codeql-analysis.yaml
vendored
@ -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:
|
||||
|
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@ -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
|
||||
|
||||
|
2
.github/workflows/json-yaml-validate.yml
vendored
2
.github/workflows/json-yaml-validate.yml
vendored
@ -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
|
||||
|
2
.github/workflows/lint-test-chart.yaml
vendored
2
.github/workflows/lint-test-chart.yaml
vendored
@ -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
|
||||
|
||||
|
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
@ -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
|
||||
|
2
.github/workflows/release-chart.yaml
vendored
2
.github/workflows/release-chart.yaml
vendored
@ -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
|
||||
|
||||
|
2
.github/workflows/staging-image-tester.yaml
vendored
2
.github/workflows/staging-image-tester.yaml
vendored
@ -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
|
||||
|
36
README.md
36
README.md
@ -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)
|
||||
|
@ -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
|
||||
|
30
docs/sources/txt-record.md
Normal file
30
docs/sources/txt-record.md
Normal 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
|
||||
```
|
@ -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"
|
||||
```
|
@ -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
134
go.mod
@ -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
268
go.sum
@ -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=
|
||||
|
9
main.go
9
main.go
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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{
|
||||
|
@ -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})
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user