diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 48fc93b06..768f0d87a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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 diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 0db3d20ff..237c70015 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -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: diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 9aef40d53..43ccfed34 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -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 diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml index cca01ea11..356119146 100644 --- a/.github/workflows/json-yaml-validate.yml +++ b/.github/workflows/json-yaml-validate.yml @@ -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 diff --git a/.github/workflows/lint-test-chart.yaml b/.github/workflows/lint-test-chart.yaml index b153ab136..112e40640 100644 --- a/.github/workflows/lint-test-chart.yaml +++ b/.github/workflows/lint-test-chart.yaml @@ -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 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index b547756b2..9ef05fcab 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -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 diff --git a/.github/workflows/release-chart.yaml b/.github/workflows/release-chart.yaml index 6c3c25b8e..ec326758a 100644 --- a/.github/workflows/release-chart.yaml +++ b/.github/workflows/release-chart.yaml @@ -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 diff --git a/.github/workflows/staging-image-tester.yaml b/.github/workflows/staging-image-tester.yaml index dd3d5e450..a42c86e77 100644 --- a/.github/workflows/staging-image-tester.yaml +++ b/.github/workflows/staging-image-tester.yaml @@ -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 diff --git a/README.md b/README.md index 282fa8edc..6ab5602b3 100644 --- a/README.md +++ b/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) diff --git a/docs/sources/mx-record.md b/docs/sources/mx-record.md index 53c2b3065..725491b23 100644 --- a/docs/sources/mx-record.md +++ b/docs/sources/mx-record.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 diff --git a/docs/sources/txt-record.md b/docs/sources/txt-record.md new file mode 100644 index 000000000..1786d2499 --- /dev/null +++ b/docs/sources/txt-record.md @@ -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 +``` diff --git a/docs/tutorials/pdns.md b/docs/tutorials/pdns.md index 365fffb80..edcd651f6 100644 --- a/docs/tutorials/pdns.md +++ b/docs/tutorials/pdns.md @@ -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" +``` \ No newline at end of file diff --git a/docs/tutorials/rdns.md b/docs/tutorials/rdns.md deleted file mode 100644 index 7e10403c1..000000000 --- a/docs/tutorials/rdns.md +++ /dev/null @@ -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# -``` diff --git a/go.mod b/go.mod index a4350b89c..92badfe27 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 70d598d21..0f62732ea 100644 --- a/go.sum +++ b/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= diff --git a/main.go b/main.go index 215a16e3a..b02714aca 100644 --- a/main.go +++ b/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, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 1bbd20ea8..43880a1fe 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -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) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 79abdf08a..f06e54f16 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -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", diff --git a/provider/alibabacloud/alibaba_cloud_test.go b/provider/alibabacloud/alibaba_cloud_test.go index edb6d12a0..8d4b47464 100644 --- a/provider/alibabacloud/alibaba_cloud_test.go +++ b/provider/alibabacloud/alibaba_cloud_test.go @@ -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( diff --git a/provider/aws/aws.go b/provider/aws/aws.go index 0e2926bf4..f4ad9bdea 100644 --- a/provider/aws/aws.go +++ b/provider/aws/aws.go @@ -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) diff --git a/provider/aws/aws_test.go b/provider/aws/aws_test.go index 806ed1d5c..4681aff28 100644 --- a/provider/aws/aws_test.go +++ b/provider/aws/aws_test.go @@ -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()-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()-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()-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()-codes.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "example").WithProviderSpecific(providerSpecificAlias, "false"), + endpoint.NewEndpointWithTTL("escape-%!s()-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), + endpoint.NewEndpointWithTTL("escape-%!s()-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()-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()-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) + }) + } +} diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index 22e0b753c..564fa0461 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -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, diff --git a/provider/digitalocean/digital_ocean.go b/provider/digitalocean/digital_ocean.go index 20cfcefa0..29412e84d 100644 --- a/provider/digitalocean/digital_ocean.go +++ b/provider/digitalocean/digital_ocean.go @@ -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 +} diff --git a/provider/digitalocean/digital_ocean_test.go b/provider/digitalocean/digital_ocean_test.go index d16be2471..cd5555b32 100644 --- a/provider/digitalocean/digital_ocean_test.go +++ b/provider/digitalocean/digital_ocean_test.go @@ -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) } diff --git a/provider/google/google.go b/provider/google/google.go index c3c9270f0..3502d6474 100644 --- a/provider/google/google.go +++ b/provider/google/google.go @@ -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) diff --git a/provider/google/google_test.go b/provider/google/google_test.go index dea1774a7..89a51b08b 100644 --- a/provider/google/google_test.go +++ b/provider/google/google_test.go @@ -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{ diff --git a/provider/pdns/pdns.go b/provider/pdns/pdns.go index fbc7cc22f..bbb473139 100644 --- a/provider/pdns/pdns.go +++ b/provider/pdns/pdns.go @@ -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}) diff --git a/provider/pdns/pdns_test.go b/provider/pdns/pdns_test.go index 01489da9c..13f5a7efc 100644 --- a/provider/pdns/pdns_test.go +++ b/provider/pdns/pdns_test.go @@ -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) diff --git a/provider/rdns/rdns.go b/provider/rdns/rdns.go deleted file mode 100644 index 10766c876..000000000 --- a/provider/rdns/rdns.go +++ /dev/null @@ -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) -} diff --git a/provider/rdns/rdns_test.go b/provider/rdns/rdns_test.go deleted file mode 100644 index 99358f4ce..000000000 --- a/provider/rdns/rdns_test.go +++ /dev/null @@ -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) - } - } -}