diff --git a/.golangci.yml b/.golangci.yml index d43f059095..ccbcb74d27 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -20,12 +20,10 @@ linters: - dogsled - gofmt - goimports - - golint - goprintffuncname - gosimple - govet - ineffassign - - interfacer - misspell - rowserrcheck - staticcheck @@ -47,12 +45,10 @@ issues: - dogsled - gofmt - goimports - - golint - goprintffuncname - gosimple - govet - ineffassign - - interfacer - misspell - nolintlint - rowserrcheck diff --git a/README.md b/README.md index 6e0469fac9..e16381d2ea 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,14 @@ To see ExternalDNS in action, have a look at this [video](https://www.youtube.co ## The Latest Release +--- +**WARNING** +Releases v0.12.0 - v0.12.2 (current) have a *major* bug for InfoBlox providers. It will cause *all* DNS records not managed by external-dns to be deleted. This was identified in issue [#2931](https://github.com/kubernetes-sigs/external-dns/issues/2931) and fixed in PR [#2890](https://github.com/kubernetes-sigs/external-dns/pull/2890). *BUT* there is no external-dns release with this fix. + +Do *not* upgrade to these versions if you use external-dns + +--- + ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchronized with Ingresses and Services of `type=LoadBalancer` and nodes in various cloud providers: * [Google Cloud DNS](https://cloud.google.com/dns/docs/) * [AWS Route 53](https://aws.amazon.com/route53/) @@ -57,6 +65,9 @@ ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchroniz * [Gandi](https://www.gandi.net) * [ANS Group SafeDNS](https://portal.ans.co.uk/safedns/) * [IBM Cloud DNS](https://www.ibm.com/cloud/dns) +* [TencentCloud PrivateDNS](https://cloud.tencent.com/product/privatedns) +* [TencentCloud DNSPod](https://cloud.tencent.com/product/cns) +* [Plural](https://www.plural.sh/) From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API. @@ -115,6 +126,8 @@ The following table clarifies the current status of the providers according to t | Gandi | Alpha | @packi | | SafeDNS | Alpha | @assureddt | | IBMCloud | Alpha | @hughhuangzh | +| TencentCloud | Alpha | @Hyzhou | +| Plural | Alpha | @michaeljguarino | ## Kubernetes version compatibility @@ -183,6 +196,8 @@ The following tutorials are provided: * [SafeDNS](docs/tutorials/UKFast_SafeDNS.md) * [IBM Cloud](docs/tutorials/ibmcloud.md) * [Nodes as source](docs/tutorials/nodes.md) +* [TencentCloud](docs/tutorials/tencentcloud.md) +* [Plural](docs/tutorials/plural.md) ### Running Locally diff --git a/docs/scripts/docs.go b/docs/scripts/docs.go index 2a79b6f01b..3482ee156e 100644 --- a/docs/scripts/docs.go +++ b/docs/scripts/docs.go @@ -31,7 +31,7 @@ func removeLinkPrefixInIndex() { updatedContent := strings.ReplaceAll(string(content), "](./docs/", "](") updatedContent = strings.ReplaceAll(updatedContent, "](docs/", "](") - f, err := os.OpenFile("./docs/index.md", os.O_RDWR, 0644) + f, err := os.OpenFile("./docs/index.md", os.O_RDWR, 0o644) if err != nil { log.Fatalf("Could not open index.md file to update content. Original error: %s", err) } diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 1bc1fac670..61986172b8 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -97,7 +97,7 @@ The role name of the role associated with the node(s) where ExternalDNS will run ##### Get role name with a single managed nodegroup -From the comand line, if you have a single managed node group, the default with `eksctl create cluster`, you can find the role name with the following: +From the command line, if you have a single managed node group, the default with `eksctl create cluster`, you can find the role name with the following: ```bash # get managed node group name (assuming there's only one node group) @@ -112,7 +112,7 @@ ROLE_NAME=${NODE_ROLE_ARN##*/} ##### Get role name with other configurations -If you have multiple node groups or any unmanaged node groups, the process gets more complex. The first step is to get the instance host name of the destired node to where ExternalDNS will be deployed or is already deployed: +If you have multiple node groups or any unmanaged node groups, the process gets more complex. The first step is to get the instance host name of the desired node to where ExternalDNS will be deployed or is already deployed: ```bash # node instance name of one of the external dns pods currently running @@ -124,7 +124,7 @@ INSTANCE_NAME=$(kubectl get pods --all-namespaces \ INSTANCE_NAME=$(kubectl get nodes --output name | cut -d'/' -f2 | tail -1) ``` -With the instnace host name, you can then get the instance id: +With the instance host name, you can then get the instance id: ```bash get_instance_id() { @@ -157,7 +157,7 @@ findRoleName() { --role-name $ROLE --query InstanceProfiles[0].Arn --output text) # if there is an instance profile if [[ "$PROFILE_ARN" != "None" ]]; then - # get all the instances with this associated instance prfile + # get all the instances with this associated instance profile INSTANCES=$(aws ec2 describe-instances \ --filters Name=iam-instance-profile.arn,Values=$PROFILE_ARN \ --query Reservations[*].Instances[0].InstanceId --out text) @@ -194,7 +194,7 @@ If ExternalDNS is not yet deployed, follow the steps under [Deploy ExternalDNS]( In this method, the policy is attached to an IAM user, and the credentials secrets for the IAM user are then made available using a Kubernetes secret. -This method is not the preferred method as the secrets in the credential file could be copied and used by an unauthorized threat actor. However, if the Kubernetes cluster is not hosted on AWS, it may be the only method available. Given this situation, it is important to limit the associated privileges to just minimal requried privileges, i.e. read-write access to Route53, and not used a credentials file that has extra privileges beyond what is required. +This method is not the preferred method as the secrets in the credential file could be copied and used by an unauthorized threat actor. However, if the Kubernetes cluster is not hosted on AWS, it may be the only method available. Given this situation, it is important to limit the associated privileges to just minimal required privileges, i.e. read-write access to Route53, and not used a credentials file that has extra privileges beyond what is required. #### Create IAM user and attach the policy @@ -565,7 +565,7 @@ Create the following sample application to test that ExternalDNS works. > If you want to give multiple names to service, you can set it to external-dns.alpha.kubernetes.io/hostname with a comma `,` separator. -For this verification phase, you can can use default or another namespace for the nginx demo, for example: +For this verification phase, you can use default or another namespace for the nginx demo, for example: ```bash NGINXDEMO_NS="nginx" @@ -701,7 +701,7 @@ If you hooked up your DNS zone with its parent zone correctly you can use `curl` curl nginx.example.com. ``` -This shold show something like: +This should show something like: ```html @@ -906,7 +906,7 @@ aws iam detach-role-policy --role-name $IRSA_ROLE --policy-arn $POLICY_ARN aws iam delete-role --role-name $IRSA_ROLE ``` -Delete any uneeded policies: +Delete any unneeded policies: ```bash aws iam delete-policy --policy-arn $POLICY_ARN diff --git a/docs/tutorials/gateway-api.md b/docs/tutorials/gateway-api.md index 72c3584fce..9456deada4 100644 --- a/docs/tutorials/gateway-api.md +++ b/docs/tutorials/gateway-api.md @@ -5,9 +5,10 @@ It is meant to supplement the other provider-specific setup tutorials. ## Supported API Versions -The currently supported version of Gateway API is v1alpha2. However, the maintainers of ExternalDNS -make no backwards compatibility guarantees with alpha versions of the API. Future releases may only -support beta or stable API versions. +As the Gateway API is still in an experimental phase, ExternalDNS makes no backwards +compatibilty guarantees regarding its support. However, it currently supports a mixture of +v1alpha2 and v1beta1 APIs. Gateways and HTTPRoutes are supported using the v1beta1 API. +TLSRoutes, TCPRoutes, and UDPRoutes are supported using the v1alpha2 API. ## Hostnames diff --git a/docs/tutorials/gke.md b/docs/tutorials/gke.md index 902d4ceeb6..e44c319ada 100644 --- a/docs/tutorials/gke.md +++ b/docs/tutorials/gke.md @@ -325,6 +325,7 @@ spec: - --source=ingress - --domain-filter=example.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones - --provider=google + - --log-format=json # google cloud logs parses severity of the "text" log format incorrectly # - --google-project=my-cloud-dns-project # Use this to specify a project different from the one external-dns is running inside - --google-zone-visibility=public # Use this to filter to only zones with this visibility. Set to either 'public' or 'private'. Omitting will match public and private zones - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization diff --git a/docs/tutorials/plural.md b/docs/tutorials/plural.md new file mode 100644 index 0000000000..c8f77c0b71 --- /dev/null +++ b/docs/tutorials/plural.md @@ -0,0 +1,197 @@ +# Setting up ExternalDNS for Services on Plural + +This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Plural DNS. + +Make sure to use **>=0.12.3** version of ExternalDNS for this tutorial. + +## Creating Plural Credentials + +A secret containing the a Plural access token is needed for this provider. You can get a token for your user [here](https://app.plural.sh/profile/tokens). + +To create the secret you can run `kubectl create secret generic plural-env --from-literal=PLURAL_ACCESS_TOKEN=`. + +## Deploy ExternalDNS + +Connect your `kubectl` client to the cluster you want to test ExternalDNS with. +Then apply one of the following manifests file to deploy ExternalDNS. + +### Manifest (for clusters without RBAC enabled) + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + selector: + matchLabels: + app: external-dns + template: + metadata: + labels: + app: external-dns + spec: + containers: + - name: external-dns + image: k8s.gcr.io/external-dns/external-dns:v0.7.6 + args: + - --source=service # ingress is also possible + - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. + - --provider=plural + - --plural-cluster=example-plural-cluster + - --plural-provider=aws # gcp, azure, equinix and kind are also possible + env: + - name: PLURAL_ACCESS_TOKEN + valueFrom: + secretKeyRef: + key: PLURAL_ACCESS_TOKEN + name: plural-env + - name: PLURAL_ENDPOINT # (optional) use an alternative endpoint for Plural; defaults to https://app.plural.sh + value: https://app.plural.sh +``` + +### Manifest (for clusters with RBAC enabled) + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: external-dns +rules: +- apiGroups: [""] + resources: ["services","endpoints","pods"] + verbs: ["get","watch","list"] +- apiGroups: ["extensions","networking.k8s.io"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["nodes"] + verbs: ["list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: +- kind: ServiceAccount + name: external-dns + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + selector: + matchLabels: + app: external-dns + template: + metadata: + labels: + app: external-dns + spec: + containers: + - name: external-dns + image: k8s.gcr.io/external-dns/external-dns:v0.7.6 + args: + - --source=service # ingress is also possible + - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. + - --provider=plural + - --plural-cluster=example-plural-cluster + - --plural-provider=aws # gcp, azure, equinix and kind are also possible + env: + - name: PLURAL_ACCESS_TOKEN + valueFrom: + secretKeyRef: + key: PLURAL_ACCESS_TOKEN + name: plural-env + - name: PLURAL_ENDPOINT # (optional) use an alternative endpoint for Plural; defaults to https://app.plural.sh + value: https://app.plural.sh +``` + +## Deploying an Nginx Service + +Create a service file called 'nginx.yaml' with the following contents: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + annotations: + external-dns.alpha.kubernetes.io/hostname: example.com +spec: + selector: + app: nginx + type: LoadBalancer + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` + +Note the annotation on the service; use the same hostname as the Plural DNS zone created above. The annotation may also be a subdomain +of the DNS zone (e.g. 'www.example.com'). + +By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above. +This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300. + +ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation +will cause ExternalDNS to remove the corresponding DNS records. + +Create the deployment and service: + +``` +$ kubectl create -f nginx.yaml +``` + +Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service. + +Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize +the Plural DNS records. + +## Verifying Plural DNS records + +Check your [Plural domain overview](https://app.plural.sh/account/domains) to view the domains associated with your Plural account. There you can view the records for each domain. + +The records should show the external IP address of the service as the A record for your domain. + +## Cleanup + +Now that we have verified that ExternalDNS will automatically manage Plural DNS records, we can delete the tutorial's example: + +``` +$ kubectl delete -f nginx.yaml +$ kubectl delete -f externaldns.yaml diff --git a/docs/tutorials/tencentcloud.md b/docs/tutorials/tencentcloud.md new file mode 100644 index 0000000000..76033d410f --- /dev/null +++ b/docs/tutorials/tencentcloud.md @@ -0,0 +1,208 @@ +# Setting up ExternalDNS for Tencent Cloud + +## External Dns Version +* Make sure to use **>=1.7.2** version of ExternalDNS for this tutorial + +## Set up PrivateDns or DNSPod + +Tencent Cloud DNSPod Service is the domain name resolution and management service for public access. +Tencent Cloud PrivateDNS Service is the domain name resolution and management service for VPC internal access. + +* If you want to use internal dns service in Tencent Cloud. +1. Set up the args `--tencent-cloud-zone-type=private` +2. Create a DNS domain in PrivateDNS console. DNS domain which will contain the managed DNS records. + +* If you want to use public dns service in Tencent Cloud. +1. Set up the args `--tencent-cloud-zone-type=public` +2. Create a Domain in DnsPod console. DNS domain which will contain the managed DNS records. + +## Set up CAM for API Key + +In Tencent CAM Console. you may get the secretId and secretKey pair. make sure the key pair has those Policy. +```json +{ + "version": "2.0", + "statement": [ + { + "effect": "allow", + "action": [ + "dnspod:ModifyRecord", + "dnspod:DeleteRecord", + "dnspod:CreateRecord", + "dnspod:DescribeRecordList", + "dnspod:DescribeDomainList" + ], + "resource": [ + "*" + ] + }, + { + "effect": "allow", + "action": [ + "privatedns:DescribePrivateZoneList", + "privatedns:DescribePrivateZoneRecordList", + "privatedns:CreatePrivateZoneRecord", + "privatedns:DeletePrivateZoneRecord", + "privatedns:ModifyPrivateZoneRecord" + ], + "resource": [ + "*" + ] + } + ] +} +``` + +# Deploy ExternalDNS + +## Manifest (for clusters with RBAC enabled) + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: external-dns +rules: +- apiGroups: [""] + resources: ["services","endpoints","pods"] + verbs: ["get","watch","list"] +- apiGroups: ["extensions","networking.k8s.io"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["nodes"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: +- kind: ServiceAccount + name: external-dns + namespace: default +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: external-dns +data: + tencent-cloud.json: | + { + "regionId": "ap-shanghai", + "secretId": "******", + "secretKey": "******", + "vpcId": "vpc-******" + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + selector: + matchLabels: + app: external-dns + template: + metadata: + labels: + app: external-dns + spec: + containers: + - args: + - --source=service + - --source=ingress + - --domain-filter=external-dns-test.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones + - --provider=tencentcloud + - --policy=sync # set `upsert-only` would prevent ExternalDNS from deleting any records + - --tencent-cloud-zone-type=private # only look at private hosted zones. set `public` to use the public dns service. + - --tencent-cloud-config-file=/etc/kubernetes/tencent-cloud.json + image: k8s.gcr.io/external-dns/external-dns:v1.7.2 + imagePullPolicy: Always + name: external-dns + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /etc/kubernetes + name: config-volume + readOnly: true + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + serviceAccount: external-dns + serviceAccountName: external-dns + terminationGracePeriodSeconds: 30 + volumes: + - configMap: + defaultMode: 420 + items: + - key: tencent-cloud.json + path: tencent-cloud.json + name: external-dns + name: config-volume +``` + +# Example + +## Service + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: nginx + annotations: + external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.com + external-dns.alpha.kubernetes.io/internal-hostname: nginx-internal.external-dns-test.com + external-dns.alpha.kubernetes.io/ttl: "600" +spec: + type: LoadBalancer + ports: + - port: 80 + name: http + targetPort: 80 + selector: + app: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 80 + name: http +``` + +`nginx.external-dns-test.com` will record to the Loadbalancer VIP. +`nginx-internal.external-dns-test.com` will record to the ClusterIP. +all of the DNS Record ttl will be 600. + +# Attention + +This makes ExternalDNS safe for running in environments where there are other records managed via other means. + diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index 68f9b34a17..428a7961b6 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -190,6 +190,13 @@ func NewEndpointWithTTL(dnsName, recordType string, ttl TTL, targets ...string) cleanTargets[idx] = strings.TrimSuffix(target, ".") } + for _, label := range strings.Split(dnsName, ".") { + if len(label) > 63 { + log.Errorf("label %s in %s is longer than 63 characters. Cannot create endpoint", label, dnsName) + return nil + } + } + return &Endpoint{ DNSName: strings.TrimSuffix(dnsName, "."), Targets: cleanTargets, diff --git a/endpoint/labels.go b/endpoint/labels.go index 26743ea3fd..544eb3e7a5 100644 --- a/endpoint/labels.go +++ b/endpoint/labels.go @@ -23,10 +23,8 @@ import ( "strings" ) -var ( - // ErrInvalidHeritage is returned when heritage was not found, or different heritage is found - ErrInvalidHeritage = errors.New("heritage is unknown or not found") -) +// ErrInvalidHeritage is returned when heritage was not found, or different heritage is found +var ErrInvalidHeritage = errors.New("heritage is unknown or not found") const ( heritage = "external-dns" diff --git a/endpoint/labels_test.go b/endpoint/labels_test.go index 05d57758ea..9386a23e6a 100644 --- a/endpoint/labels_test.go +++ b/endpoint/labels_test.go @@ -32,7 +32,7 @@ type LabelsSuite struct { barTextAsMap Labels noHeritageText string wrongHeritageText string - multipleHeritageText string //considered invalid + multipleHeritageText string // considered invalid } func (suite *LabelsSuite) SetupTest() { @@ -48,7 +48,7 @@ func (suite *LabelsSuite) SetupTest() { "resource": "bar-resource", "new-key": "bar-new-key", } - suite.barText = "heritage=external-dns,,external-dns/owner=bar-owner,external-dns/resource=bar-resource,external-dns/new-key=bar-new-key,random=stuff,no-equal-sign,," //also has some random gibberish + suite.barText = "heritage=external-dns,,external-dns/owner=bar-owner,external-dns/resource=bar-resource,external-dns/new-key=bar-new-key,random=stuff,no-equal-sign,," // also has some random gibberish suite.noHeritageText = "external-dns/owner=random-owner" suite.wrongHeritageText = "heritage=mate,external-dns/owner=random-owner" diff --git a/endpoint/zz_generated.deepcopy.go b/endpoint/zz_generated.deepcopy.go index fd75c7a3d9..aaa69927df 100644 --- a/endpoint/zz_generated.deepcopy.go +++ b/endpoint/zz_generated.deepcopy.go @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* diff --git a/go.mod b/go.mod index 9fef9a842c..48024b8303 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,22 @@ module sigs.k8s.io/external-dns go 1.18 require ( - cloud.google.com/go/compute v1.7.0 - github.com/Azure/azure-sdk-for-go v61.5.0+incompatible - github.com/Azure/go-autorest/autorest v0.11.21 + cloud.google.com/go/compute v1.9.0 + github.com/Azure/azure-sdk-for-go v66.0.0+incompatible + github.com/Azure/go-autorest/autorest v0.11.27 github.com/Azure/go-autorest/autorest/adal v0.9.20 github.com/Azure/go-autorest/autorest/to v0.4.0 - github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.11.0 - github.com/IBM/go-sdk-core/v5 v5.6.5 - github.com/IBM/networking-go-sdk v0.24.0 + github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0 + github.com/IBM/go-sdk-core/v5 v5.8.0 + github.com/IBM/networking-go-sdk v0.32.0 github.com/StackExchange/dnscontrol v0.2.8 - github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 - github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect - github.com/alecthomas/colour v0.1.0 // indirect + github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 github.com/alecthomas/kingpin v2.2.5+incompatible - github.com/aliyun/alibaba-cloud-sdk-go v1.61.1483 - github.com/ans-group/sdk-go v1.7.0 - github.com/aws/aws-sdk-go v1.42.52 + github.com/aliyun/alibaba-cloud-sdk-go v1.61.1742 + github.com/ans-group/sdk-go v1.10.4 + github.com/aws/aws-sdk-go v1.44.119 github.com/bodgit/tsig v1.2.0 - github.com/cloudflare/cloudflare-go v0.25.0 + github.com/cloudflare/cloudflare-go v0.50.0 github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 github.com/datawire/ambassador v1.6.0 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba @@ -35,8 +33,8 @@ require ( github.com/hooklift/gowsdl v0.5.0 github.com/infobloxopen/infoblox-go-client/v2 v2.1.2-0.20220407114022-6f4c71443168 github.com/linki/instrumented_http v0.3.0 - github.com/linode/linodego v0.32.2 - github.com/maxatome/go-testdeep v1.11.0 + github.com/linode/linodego v1.9.1 + github.com/maxatome/go-testdeep v1.12.0 github.com/miekg/dns v1.1.48 github.com/nesv/go-dynect v0.6.0 github.com/nic-at/rc0go v1.1.1 @@ -46,11 +44,15 @@ require ( github.com/oracle/oci-go-sdk v24.3.0+incompatible github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 github.com/pkg/errors v0.9.1 + github.com/pluralsh/gqlclient v1.1.6 github.com/projectcontour/contour v1.21.1 github.com/prometheus/client_golang v1.12.2 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f - github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.0 + github.com/sirupsen/logrus v1.9.0 + github.com/stretchr/testify v1.8.0 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.344 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.344 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.344 github.com/transip/gotransip/v6 v6.17.0 github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556 @@ -58,17 +60,18 @@ require ( go.etcd.io/etcd/api/v3 v3.5.4 go.etcd.io/etcd/client/v3 v3.5.2 go.uber.org/ratelimit v0.2.0 - golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e + golang.org/x/net v0.1.0 golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 google.golang.org/api v0.93.0 gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 gopkg.in/yaml.v2 v2.4.0 istio.io/api v0.0.0-20210128181506-0c4b8e54850f istio.io/client-go v0.0.0-20210128182905-ee2edd059e02 - k8s.io/api v0.24.4 - k8s.io/apimachinery v0.24.4 - k8s.io/client-go v0.24.1 + k8s.io/api v0.25.2 + k8s.io/apimachinery v0.25.2 + k8s.io/client-go v0.25.2 + sigs.k8s.io/gateway-api v0.5.0 ) require ( @@ -78,6 +81,11 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Masterminds/semver v1.4.2 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/Yamashou/gqlgenc v0.11.0 // indirect + github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect + github.com/alecthomas/colour v0.1.0 // indirect github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect @@ -90,24 +98,30 @@ require ( github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v1.2.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/errors v0.19.8 // indirect - github.com/go-openapi/strfmt v0.20.1 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/strfmt v0.20.2 // indirect + github.com/go-openapi/swag v0.19.14 // indirect github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.0.0 // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect github.com/googleapis/gax-go/v2 v2.4.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -123,13 +137,18 @@ require ( github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/jinzhu/copier v0.3.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect @@ -138,11 +157,14 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/schollz/progressbar/v3 v3.8.6 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/smartystreets/gunit v1.3.4 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.3.0 // indirect + github.com/stretchr/objx v0.4.0 // indirect github.com/terra-farm/udnssdk v1.3.5 // indirect + github.com/vektah/gqlparser/v2 v2.5.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect go.mongodb.org/mongo-driver v1.5.1 // indirect go.opencensus.io v0.23.0 // indirect @@ -150,46 +172,30 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect - golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect - golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect - golang.org/x/tools v0.1.10 // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect - google.golang.org/grpc v1.47.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/genproto v0.0.0-20220804142021-4e6b2dfa6612 // indirect + google.golang.org/grpc v1.48.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/go-playground/validator.v9 v9.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.3 // indirect + gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/resty.v1 v1.12.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a // indirect - k8s.io/klog/v2 v2.60.1 // indirect - k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect - sigs.k8s.io/controller-runtime v0.11.0 // indirect - sigs.k8s.io/gateway-api v0.4.3 - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect + k8s.io/klog/v2 v2.70.1 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + sigs.k8s.io/controller-runtime v0.12.1 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) -require ( - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/emicklei/go-restful v2.9.5+incompatible // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect -) - replace k8s.io/klog/v2 => github.com/Raffo/knolog v0.0.0-20211016155154-e4d5e0cc970a diff --git a/go.sum b/go.sum index 68db6015b4..a61ca10ea2 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,9 @@ cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJW cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.9.0 h1:ED/FP4xv8GJw63v556/ASNc1CeeLUO2Bs8nzaHchkHg= +cloud.google.com/go/compute v1.9.0/go.mod h1:lWv1h/zUWTm/LozzfTJhBSkd6ShQq8la8VeeuOEGxfY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -68,8 +69,8 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zum git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v56.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v61.5.0+incompatible h1:OSHSFeNm7D1InGsQrFjyN9hpxD5Ec60PdsWWudCpah4= -github.com/Azure/azure-sdk-for-go v61.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE= +github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -81,13 +82,14 @@ github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKn github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk= -github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= @@ -96,8 +98,9 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= @@ -113,14 +116,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.8.1 h1:Nvwzsp7e0j9hj7JQ0ZJ0UkD6XMaryA/G4zDbJsRmGnw= -github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.8.1/go.mod h1:RiUvKuHKTBmBApDMUQzBL14pQUGKcx/IioKQPIcRQjs= -github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.11.0 h1:75KEvjN+5lXcFzvW7RBoZY1YALJSNctcXFEfUyFz5Vo= -github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.11.0/go.mod h1:+GAqrO/rFsYnhzTIxYLXCHxHVZyrtzBLyKjV6hi73YQ= -github.com/IBM/go-sdk-core/v5 v5.6.5 h1:zXlt5x/udqZTD617SKUQzPDON4x5y0+22vNxsdCGYpg= -github.com/IBM/go-sdk-core/v5 v5.6.5/go.mod h1:tt/B9rxLkRtglE7pvqLuYikgCXaZFL3btdruJaoUeek= -github.com/IBM/networking-go-sdk v0.24.0 h1:3AE23TBbcsB/2c15kuHuAnXlUom5FHMqxGxBRA94WS8= -github.com/IBM/networking-go-sdk v0.24.0/go.mod h1:vX/4URo6J6e6QCDhsntk6OAA4G27jp+v3+ZMb9WyBQY= +github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0 h1:2gzVSELk4I4ncZNrsaKI6fvZ3to60iYnig+lTFcGCEM= +github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0/go.mod h1:P9YNyJaJazc49fLNFG4uQ61VZVptykWqNU2vWLWcxu0= +github.com/IBM/go-sdk-core/v5 v5.8.0 h1:Bn9BxTaKYKWpd+BDpVsL6XOOJl4QDgxux4gSdWi31vE= +github.com/IBM/go-sdk-core/v5 v5.8.0/go.mod h1:+YbdhrjCHC84ls4MeBp+Hj4NZCni+tDAc0XQUqRO9Jc= +github.com/IBM/networking-go-sdk v0.32.0 h1:QWd7CxC+Wzap+zWFfXMjbqB5LpvrB1KvNtIbKrWIkhA= +github.com/IBM/networking-go-sdk v0.32.0/go.mod h1:tVxXclpQs8nQJYPTr9ZPNC1voaPNQLy8iy/72oVfFtM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -161,14 +162,17 @@ github.com/StackExchange/dnscontrol v0.2.8 h1:7jviqDH9cIqRSRpH0UxgmpT7a8CwEhs9mL github.com/StackExchange/dnscontrol v0.2.8/go.mod h1:BH+5nX50JxHDdb3+AD/z/UfYMCc7iaqEkRtQ+NjcFGE= github.com/Venafi/vcert/v4 v4.14.3/go.mod h1:IL+6LA8QRWZbmcMzIr/vRhf9Aa6XDM2cQO50caWevjA= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Yamashou/gqlgenc v0.11.0 h1:y6I7CDrUdY4JBxfwss9168HTP5k/CdExLV5+YPG/3nY= +github.com/Yamashou/gqlgenc v0.11.0/go.mod h1:OeQhghEgvGWvRwzx9XjMeg3FUQOHnTo5/12iuJSJxLg= 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/ahmetb/gen-crd-api-reference-docs v0.2.1-0.20201224172655-df869c1245d4/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 h1:5BIsppVPdWJA29Yb5cYawQYeh5geN413WxAgBZvEtdA= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk= @@ -187,16 +191,17 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2c github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 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.61.1483 h1:J8HaD+Zpfi1gcel3HCKpoHHEsrcuRrZlSnx7R9SCf5I= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1483/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.1742 h1:1pBiWcgrwB5LN52SXAn6rHN3dIBOTQ+7Mk+ctQ0/+ZA= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.1742/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +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= github.com/ans-group/go-durationstring v1.2.0 h1:UJIuQATkp0t1rBvZsHRwki33YHV9E+Ulro+3NbMB7MM= github.com/ans-group/go-durationstring v1.2.0/go.mod h1:QGF9Mdpq9058QXaut8r55QWu6lcHX6i/GvF1PZVkV6o= -github.com/ans-group/sdk-go v1.7.0 h1:Riy6MwVf6fkmQvxPPA1UfrBoqo3GYghXV9MhCzlCPg8= -github.com/ans-group/sdk-go v1.7.0/go.mod h1:XSKXEDfKobnDtZoyia5DhJxxaDMcCjr76e1KJ9dU/xc= +github.com/ans-group/sdk-go v1.10.4 h1:wZzojt99wtVIEHs8zNQzp1Xhqme5tD5NqMM1VLmG6xQ= +github.com/ans-group/sdk-go v1.10.4/go.mod h1:XSKXEDfKobnDtZoyia5DhJxxaDMcCjr76e1KJ9dU/xc= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= @@ -210,7 +215,6 @@ github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4 github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= @@ -225,8 +229,10 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.40.14/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.52 h1:/+TZ46+0qu9Ph/UwjVrU3SG8OBi87uJLrLiYRNZKbHQ= -github.com/aws/aws-sdk-go v1.42.52/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.44.81 h1:C8oBZ+a+ka0qk3Q24MohQIFq0tkbO8IAu5tfpAMKVWE= +github.com/aws/aws-sdk-go v1.44.81/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.119 h1:TPkpDsanBMcZaF5wHwpKhjkapRV/b7d2qdC+a+IPbmY= +github.com/aws/aws-sdk-go v1.44.119/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -274,8 +280,8 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI= -github.com/cloudflare/cloudflare-go v0.25.0 h1:GwyKwGq8ciGNjKiTpjj6RvU3+uJNuPBNjlUkeQRx0yU= -github.com/cloudflare/cloudflare-go v0.25.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI= +github.com/cloudflare/cloudflare-go v0.50.0 h1:RS4tttMecD1rYCiMMfJeW8s9OEhCm85Y+70RJuOoxNA= +github.com/cloudflare/cloudflare-go v0.50.0/go.mod h1:4+j2gGo6xyrFiYmpa2y4mNzu7pPPN42kyv1b2EqiZGQ= 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 v0.0.0-20200324003616-bae28a880fdb/go.mod h1:HNVadOiXCy7Jk3R2knJ+qm++zkncJxxBMpjdGgJ+UJc= @@ -388,8 +394,9 @@ github.com/ecodia/golang-awaitility v0.0.0-20180710094957-fb55e59708c7/go.mod h1 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815/go.mod h1:wYFFK4LYXbX7j+76mOq7aiC/EAw2S22CrzPHqgsisPw= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -476,8 +483,9 @@ github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= @@ -527,8 +535,8 @@ github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pL github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.20.1 h1:1VgxvehFne1mbChGeCmZ5pc0LxUf6yaACVSIYAR91Xc= -github.com/go-openapi/strfmt v0.20.1/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= +github.com/go-openapi/strfmt v0.20.2 h1:6XZL+fF4VZYFxKQGLAUB358hOrRh/wS51uWEtlONADE= +github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= @@ -608,8 +616,9 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= @@ -789,8 +798,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -805,7 +814,6 @@ github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= @@ -914,6 +922,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= @@ -955,8 +964,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 v0.32.2 h1:ubQMQuQGt73N1hMzY15lx2MRdZbcGbuvEVlSpJEB+lc= -github.com/linode/linodego v0.32.2/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= +github.com/linode/linodego v1.9.1 h1:29UpEPpYcGFnbwiJW8mbk/bjBZpgd/pv68io2IKTo34= +github.com/linode/linodego v1.9.1/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w= 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-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= @@ -1000,6 +1009,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -1008,8 +1019,8 @@ github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxatome/go-testdeep v1.11.0 h1:Tgh5efyCYyJFGUYiT0qxBSIDeXw0F5zSoatlou685kk= -github.com/maxatome/go-testdeep v1.11.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= +github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -1021,6 +1032,8 @@ github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ= github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.1.1/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1105,6 +1118,7 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1112,12 +1126,11 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1181,6 +1194,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pluralsh/gqlclient v1.1.6 h1:lj6KVfUET9DPyB95+T+hXtCGqqOm+TXOB3jYzuBX1Zc= +github.com/pluralsh/gqlclient v1.1.6/go.mod h1:qcE4KD7hBGl/JFCoXXy8zgUP0mWHJsAM6bhDAf592AE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -1239,6 +1254,8 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1246,8 +1263,8 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc= @@ -1266,6 +1283,8 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f h1:WSnaD0/cvbKJgSTYbjAPf4RJXVvNNDAwVm+W8wEmnGE= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= +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= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -1281,8 +1300,9 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -1338,17 +1358,25 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= -github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.344 h1:QhPDamT0YL04UaoteA9AEHnE/sklwYr+VSKd/pPQ6r8= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.344/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.344 h1:pdwJ6T3iEjP5nB9Mgi4y/OBO8XNtkGN2/+mjGZ8yCbw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.344/go.mod h1:CuOaLxOQr477GhMWAQPYQFUJrsZbW+ZqkAgP2uHDZXg= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.344 h1:q4r39zJkMyHvrORok48IOJz/nJ235dIkHStA9LZYwgw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.344/go.mod h1:En+pdagcHkAASorHT1l8R6tUtieRNNxaQ7nfyqWPefk= 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 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= @@ -1356,8 +1384,6 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/transip/gotransip/v6 v6.14.0 h1:TOZYH+9W64ZqH2S/7MoPXGcJ3EyBSjBoMUsLMsA59JA= -github.com/transip/gotransip/v6 v6.14.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/transip/gotransip/v6 v6.17.0 h1:2RCyqYqz5+Ej8z96EyE4sf6tQrrfEBaFDO0LliSl6+8= github.com/transip/gotransip/v6 v6.17.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/tsaarni/certyaml v0.6.2/go.mod h1:fMHgNcBc7APv85tYQQ5186Qx+OeUDmLbGHZTRI988RM= @@ -1375,11 +1401,11 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vektah/gqlparser/v2 v2.5.0 h1:GwEwy7AJsqPWrey0bHnn+3JLaHLZVT66wY/+O+Tf9SU= +github.com/vektah/gqlparser/v2 v2.5.0/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= github.com/vektra/mockery/v2 v2.10.0/go.mod h1:m/WO2UzWzqgVX3nvqpRQq70I4Z7jbSCRhdmkgtp+Ab4= github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556 h1:UbVjBjgJUYGD8MlobEdOR+yTeNqaNa2Gf1/nskVNCSE= github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= -github.com/vultr/govultr/v2 v2.14.1 h1:Z4nd9mXNQ5wd63aw0MZOalFeTkJ8L6Sed3PTqagp4TA= -github.com/vultr/govultr/v2 v2.14.1/go.mod h1:JjUljQdSZx+MELCAJvZ/JH32bJotmflnsyS0NOjb8Jg= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1520,9 +1546,10 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1576,8 +1603,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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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= @@ -1622,7 +1649,6 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1648,15 +1674,17 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1694,8 +1722,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180425194835-bb9c189858d9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1819,14 +1848,20 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1838,6 +1873,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1848,8 +1885,8 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1938,8 +1975,8 @@ golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyj golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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= @@ -2103,8 +2140,9 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f h1:hJ/Y5SqPXbarffmAsApliUlcvMU+wScNGfyop4bZm8o= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220804142021-4e6b2dfa6612 h1:NX3L5YesD5qgxxrPHdKqHH38Ao0AG6poRXG+JljPsGU= +google.golang.org/genproto v0.0.0-20220804142021-4e6b2dfa6612/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -2146,8 +2184,9 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -2162,8 +2201,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2191,8 +2231,9 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w= gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= +gopkg.in/ini.v1 v1.66.6/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.0.0-20190322154155-0dafb5275fd1 h1:+fgY/3ngqdBW9oLQCMwL5g+QRkKFPJH05fx2/pipqRQ= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= @@ -2256,9 +2297,8 @@ k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg= k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= k8s.io/api v0.23.1/go.mod h1:WfXnOnwSqNtG62Y1CdjoMxh7r7u9QXGCkA1u0na2jgo= -k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= -k8s.io/api v0.24.4 h1:I5Y645gJ8zWKawyr78lVfDQkZrAViSbeRXsPZWTxmXk= -k8s.io/api v0.24.4/go.mod h1:42pVfA0NRxrtJhZQOvRSyZcJihzAdU59WBtTjYcB0/M= +k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= +k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= 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= @@ -2283,9 +2323,8 @@ k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCF k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= k8s.io/apimachinery v0.23.1/go.mod h1:SADt2Kl8/sttJ62RRsi9MIV4o8f5S3coArm0Iu3fBno= -k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.24.4 h1:S0Ur3J/PbivTcL43EdSdPhqCqKla2NIuneNwZcTDeGQ= -k8s.io/apimachinery v0.24.4/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= +k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= 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= @@ -2314,8 +2353,8 @@ k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU= k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA= k8s.io/client-go v0.23.1/go.mod h1:6QSI8fEuqD4zgFK0xbdwfB/PthBsIxCJMa3s17WlcO0= -k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= -k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= +k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= +k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= 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.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= @@ -2360,8 +2399,8 @@ k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iL k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210527164424-3c818078ee3d/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= k8s.io/kubectl v0.21.0/go.mod h1:EU37NukZRXn1TpAkMUoy8Z/B2u6wjHDS4aInsDzVvks= k8s.io/kubectl v0.21.3/go.mod h1:/x/kzrhfL1h1W07z6a1UTbd8SWZUYAWXskigkG4OBCg= @@ -2380,8 +2419,8 @@ k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= @@ -2396,18 +2435,20 @@ sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gE sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= sigs.k8s.io/controller-runtime v0.9.6/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA= -sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= +sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI= +sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0= sigs.k8s.io/controller-tools v0.3.1-0.20200517180335-820a4a27ea84/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= sigs.k8s.io/controller-tools v0.5.0/go.mod h1:JTsstrMpxs+9BUj6eGuAaEb6SDSPTeVtUyp0jmnAM/I= sigs.k8s.io/controller-tools v0.6.0/go.mod h1:baRMVPrctU77F+rfAuH2uPqW93k6yQnZA2dhUOr7ihc= sigs.k8s.io/controller-tools v0.6.2/go.mod h1:oaeGpjXn6+ZSEIQkUe/+3I40PNiDYp9aeawbt3xTgJ8= sigs.k8s.io/gateway-api v0.3.0/go.mod h1:Wb8bx7QhGVZxOSEU3i9vw/JqTB5Nlai9MLMYVZeDmRQ= -sigs.k8s.io/gateway-api v0.4.3 h1:9kdHAcfkyP7jVMSFshc8EYEKNLlFM7hbZL8vCKcMwps= sigs.k8s.io/gateway-api v0.4.3/go.mod h1:r3eiNP+0el+NTLwaTfOrCNXy8TukC+dIM3ggc+fbNWk= +sigs.k8s.io/gateway-api v0.5.0 h1:ze+k9fJqvmL8s1t3e4q1ST8RnN+f09dEv+gfacahlAE= +sigs.k8s.io/gateway-api v0.5.0/go.mod h1:x0AP6gugkFV8fC/oTlnOMU0pnmuzIR8LfIPRVUjxSqA= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY= sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY= @@ -2423,8 +2464,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/kustomize/external-dns-deployment.yaml b/kustomize/external-dns-deployment.yaml index 212e166147..989587b61c 100644 --- a/kustomize/external-dns-deployment.yaml +++ b/kustomize/external-dns-deployment.yaml @@ -16,7 +16,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: k8s.gcr.io/external-dns/external-dns + image: registry.k8s.io/external-dns/external-dns args: - --source=service - --source=ingress diff --git a/main.go b/main.go index 8ac832f270..086add11c2 100644 --- a/main.go +++ b/main.go @@ -59,11 +59,13 @@ import ( "sigs.k8s.io/external-dns/provider/oci" "sigs.k8s.io/external-dns/provider/ovh" "sigs.k8s.io/external-dns/provider/pdns" + "sigs.k8s.io/external-dns/provider/plural" "sigs.k8s.io/external-dns/provider/rcode0" "sigs.k8s.io/external-dns/provider/rdns" "sigs.k8s.io/external-dns/provider/rfc2136" "sigs.k8s.io/external-dns/provider/safedns" "sigs.k8s.io/external-dns/provider/scaleway" + "sigs.k8s.io/external-dns/provider/tencentcloud" "sigs.k8s.io/external-dns/provider/transip" "sigs.k8s.io/external-dns/provider/ultradns" "sigs.k8s.io/external-dns/provider/vinyldns" @@ -77,15 +79,15 @@ func main() { if err := cfg.ParseFlags(os.Args[1:]); err != nil { log.Fatalf("flag parsing error: %v", err) } + if cfg.LogFormat == "json" { + log.SetFormatter(&log.JSONFormatter{}) + } log.Infof("config: %s", cfg) if err := validation.ValidateConfig(cfg); err != nil { log.Fatalf("config validation failed: %v", err) } - if cfg.LogFormat == "json" { - log.SetFormatter(&log.JSONFormatter{}) - } if cfg.DryRun { log.Info("running in dry-run mode. No changes to DNS records will be made.") } @@ -334,6 +336,10 @@ func main() { p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun) case "safedns": p, err = safedns.NewSafeDNSProvider(domainFilter, cfg.DryRun) + case "plural": + p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider) + case "tencentcloud": + p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun) default: log.Fatalf("unknown dns provider: %s", cfg.Provider) } diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index e62720227d..a5dce75cc3 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -37,10 +37,8 @@ const ( passwordMask = "******" ) -var ( - // Version is the current version of the app, generated at build time - Version = "unknown" -) +// Version is the current version of the app, generated at build time +var Version = "unknown" // Config is a project-wide configuration type Config struct { @@ -192,6 +190,10 @@ type Config struct { OCPRouterName string IBMCloudProxied bool IBMCloudConfigFile string + TencentCloudConfigFile string + TencentCloudZoneType string + PluralCluster string + PluralProvider string } var defaultConfig = &Config{ @@ -223,6 +225,7 @@ var defaultConfig = &Config{ GoogleBatchChangeInterval: time.Second, GoogleZoneVisibility: "", DomainFilter: []string{}, + ZoneIDFilter: []string{}, ExcludeDomains: []string{}, RegexDomainFilter: regexp.MustCompile(""), RegexDomainExclusion: regexp.MustCompile(""), @@ -326,6 +329,10 @@ var defaultConfig = &Config{ GoDaddyOTE: false, IBMCloudProxied: false, IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json", + TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json", + TencentCloudZoneType: "", + PluralCluster: "", + PluralProvider: "", } // NewConfig returns new Config object @@ -415,7 +422,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets) // Flags related to providers - app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, ibmcloud, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi, safedns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "ibmcloud", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi", "safedns") + app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, ibmcloud, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi, safedns, tencentcloud)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "ibmcloud", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi", "safedns", "tencentcloud", "plural") app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains) app.Flag("regex-domain-filter", "Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional)").Default(defaultConfig.RegexDomainFilter.String()).RegexpVar(&cfg.RegexDomainFilter) @@ -443,6 +450,8 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) app.Flag("azure-subscription-id", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure-private-dns)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID) app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID) + app.Flag("tencent-cloud-config-file", "When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud").Default(defaultConfig.TencentCloudConfigFile).StringVar(&cfg.TencentCloudConfigFile) + app.Flag("tencent-cloud-zone-type", "When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private)").Default(defaultConfig.TencentCloudZoneType).EnumVar(&cfg.TencentCloudZoneType, "", "public", "private") // Flags related to BlueCat provider app.Flag("bluecat-dns-configuration", "When using the Bluecat provider, specify the Bluecat DNS configuration string (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatDNSConfiguration) @@ -527,6 +536,10 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("transip-account", "When using the TransIP provider, specify the account name (required when --provider=transip)").Default(defaultConfig.TransIPAccountName).StringVar(&cfg.TransIPAccountName) app.Flag("transip-keyfile", "When using the TransIP provider, specify the path to the private key file (required when --provider=transip)").Default(defaultConfig.TransIPPrivateKeyFile).StringVar(&cfg.TransIPPrivateKeyFile) + // Flags related to the Plural provider + app.Flag("plural-cluster", "When using the plural provider, specify the cluster name you're running with").Default(defaultConfig.PluralCluster).StringVar(&cfg.PluralCluster) + app.Flag("plural-provider", "When using the plural provider, specify the provider name you're running with").Default(defaultConfig.PluralProvider).StringVar(&cfg.PluralProvider) + // Flags related to policies app.Flag("policy", "Modify how DNS records are synchronized between sources and providers (default: sync, options: sync, upsert-only, create-only)").Default(defaultConfig.Policy).EnumVar(&cfg.Policy, "sync", "upsert-only", "create-only") diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 1d6f202e6c..6cd42b5b4f 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -127,6 +127,8 @@ var ( OCPRouterName: "default", IBMCloudProxied: false, IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json", + TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json", + TencentCloudZoneType: "", } overriddenConfig = &Config{ @@ -235,6 +237,8 @@ var ( RFC2136BatchChangeSize: 100, IBMCloudProxied: true, IBMCloudConfigFile: "ibmcloud.json", + TencentCloudConfigFile: "tencent-cloud.json", + TencentCloudZoneType: "private", } ) @@ -373,6 +377,8 @@ func TestParseFlags(t *testing.T) { "--rfc2136-batch-change-size=100", "--ibmcloud-proxied", "--ibmcloud-config-file=ibmcloud.json", + "--tencent-cloud-config-file=tencent-cloud.json", + "--tencent-cloud-zone-type=private", }, envVars: map[string]string{}, expected: overriddenConfig, @@ -486,6 +492,8 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100", "EXTERNAL_DNS_IBMCLOUD_PROXIED": "1", "EXTERNAL_DNS_IBMCLOUD_CONFIG_FILE": "ibmcloud.json", + "EXTERNAL_DNS_TENCENT_CLOUD_CONFIG_FILE": "tencent-cloud.json", + "EXTERNAL_DNS_TENCENT_CLOUD_ZONE_TYPE": "private", }, expected: overriddenConfig, }, diff --git a/pkg/apis/externaldns/validation/validation_test.go b/pkg/apis/externaldns/validation/validation_test.go index 55f5e63540..8573aa6cf1 100644 --- a/pkg/apis/externaldns/validation/validation_test.go +++ b/pkg/apis/externaldns/validation/validation_test.go @@ -168,7 +168,7 @@ func TestValidateGoodRfc2136Config(t *testing.T) { } func TestValidateBadRfc2136GssTsigConfig(t *testing.T) { - var invalidRfc2136GssTsigConfigs = []*externaldns.Config{ + invalidRfc2136GssTsigConfigs := []*externaldns.Config{ { LogFormat: "json", Sources: []string{"test-source"}, @@ -258,7 +258,7 @@ func TestValidateBadRfc2136GssTsigConfig(t *testing.T) { } func TestValidateGoodRfc2136GssTsigConfig(t *testing.T) { - var validRfc2136GssTsigConfigs = []*externaldns.Config{ + validRfc2136GssTsigConfigs := []*externaldns.Config{ { LogFormat: "json", Sources: []string{"test-source"}, diff --git a/plan/conflict_test.go b/plan/conflict_test.go index f40f7d4c3d..491b42bff7 100644 --- a/plan/conflict_test.go +++ b/plan/conflict_test.go @@ -84,7 +84,7 @@ func (suite *ResolverSuite) SetupTest() { endpoint.ResourceLabelKey: "ingress/default/bar-127", }, } - suite.bar127AAnother = &endpoint.Endpoint{ //TODO: remove this once we move to multiple targets under same endpoint + suite.bar127AAnother = &endpoint.Endpoint{ // TODO: remove this once we move to multiple targets under same endpoint DNSName: "bar", Targets: endpoint.Targets{"8.8.8.8"}, RecordType: "A", @@ -113,7 +113,7 @@ func (suite *ResolverSuite) TestStrictResolver() { suite.Equal(suite.fooA5, suite.perResource.ResolveCreate([]*endpoint.Endpoint{suite.fooA5, suite.fooV1Cname}), "should pick min one") suite.Equal(suite.fooV1Cname, suite.perResource.ResolveCreate([]*endpoint.Endpoint{suite.fooV2Cname, suite.fooV1Cname}), "should pick min one") - //test that perResource resolver preserves resource if it still exists + // test that perResource resolver preserves resource if it still exists suite.Equal(suite.bar127AAnother, suite.perResource.ResolveUpdate(suite.bar127A, []*endpoint.Endpoint{suite.bar127AAnother, suite.bar127A}), "should pick min for update when same resource endpoint occurs multiple times (remove after multiple-target support") // TODO:remove this test suite.Equal(suite.bar127A, suite.perResource.ResolveUpdate(suite.bar127A, []*endpoint.Endpoint{suite.bar192A, suite.bar127A}), "should pick existing resource") suite.Equal(suite.fooV2Cname, suite.perResource.ResolveUpdate(suite.fooV2Cname, []*endpoint.Endpoint{suite.fooV2Cname, suite.fooV2CnameDuplicate}), "should pick existing resource even if targets are same") diff --git a/plan/plan.go b/plan/plan.go index d166764521..d7bac70909 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -82,7 +82,7 @@ type planTable struct { resolver ConflictResolver } -func newPlanTable() planTable { //TODO: make resolver configurable +func newPlanTable() planTable { // TODO: make resolver configurable return planTable{map[string]map[string]*planTableRow{}, PerResource{}} } @@ -148,7 +148,7 @@ func (p *Plan) Calculate() *Plan { for _, topRow := range t.rows { for _, row := range topRow { - if row.current == nil { //dns name not taken + if row.current == nil { // dns name not taken changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates)) } if row.current != nil && len(row.candidates) == 0 { @@ -156,7 +156,7 @@ func (p *Plan) Calculate() *Plan { } // TODO: allows record type change, which might not be supported by all dns providers - if row.current != nil && len(row.candidates) > 0 { //dns name is taken + if row.current != nil && len(row.candidates) > 0 { // dns name is taken update := t.resolver.ResolveUpdate(row.current, row.candidates) // compare "update" to "current" to figure out if actual update is required if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) { diff --git a/plan/plan_test.go b/plan/plan_test.go index 42b0fb295c..d34c932494 100644 --- a/plan/plan_test.go +++ b/plan/plan_test.go @@ -226,7 +226,7 @@ func (suite *PlanTestSuite) SetupTest() { func (suite *PlanTestSuite) TestSyncFirstRound() { current := []*endpoint.Endpoint{} desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname, suite.bar127A} - expectedCreate := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar127A} //v1 is chosen because of resolver taking "min" + expectedCreate := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar127A} // v1 is chosen because of resolver taking "min" expectedUpdateOld := []*endpoint.Endpoint{} expectedUpdateNew := []*endpoint.Endpoint{} expectedDelete := []*endpoint.Endpoint{} @@ -544,7 +544,7 @@ func (suite *PlanTestSuite) TestRemoveEndpointWithUpsert() { validateEntries(suite.T(), changes.Delete, expectedDelete) } -//TODO: remove once multiple-target per endpoint is supported +// TODO: remove once multiple-target per endpoint is supported func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() { current := []*endpoint.Endpoint{suite.fooV3CnameSameResource, suite.bar192A} desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource} @@ -567,9 +567,8 @@ func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() { validateEntries(suite.T(), changes.Delete, expectedDelete) } -//TODO: remove once multiple-target per endpoint is supported +// TODO: remove once multiple-target per endpoint is supported func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() { - current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A} desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource} expectedCreate := []*endpoint.Endpoint{} @@ -592,7 +591,6 @@ func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() { } func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() { - current := []*endpoint.Endpoint{suite.multiple1} desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3} expectedCreate := []*endpoint.Endpoint{suite.multiple3} @@ -615,7 +613,6 @@ func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() } func (suite *PlanTestSuite) TestSetIdentifierUpdateCreatesAndDeletes() { - current := []*endpoint.Endpoint{suite.multiple2} desired := []*endpoint.Endpoint{suite.multiple3} expectedCreate := []*endpoint.Endpoint{suite.multiple3} @@ -638,7 +635,6 @@ func (suite *PlanTestSuite) TestSetIdentifierUpdateCreatesAndDeletes() { } func (suite *PlanTestSuite) TestDomainFiltersInitial() { - current := []*endpoint.Endpoint{suite.domainFilterExcluded} desired := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3} expectedCreate := []*endpoint.Endpoint{suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3} @@ -662,7 +658,6 @@ func (suite *PlanTestSuite) TestDomainFiltersInitial() { } func (suite *PlanTestSuite) TestDomainFiltersUpdate() { - current := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2} desired := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3} expectedCreate := []*endpoint.Endpoint{suite.domainFilterFiltered3} @@ -686,7 +681,6 @@ func (suite *PlanTestSuite) TestDomainFiltersUpdate() { } func (suite *PlanTestSuite) TestMissing() { - missing := []*endpoint.Endpoint{suite.domainFilterFilteredTXT1, suite.domainFilterFilteredTXT2, suite.domainFilterExcludedTXT} expectedCreate := []*endpoint.Endpoint{suite.domainFilterFilteredTXT1, suite.domainFilterFilteredTXT2} @@ -893,7 +887,6 @@ func TestShouldUpdateProviderSpecific(tt *testing.T) { } b := plan.shouldUpdateProviderSpecific(test.desired, test.current) assert.Equal(t, test.shouldUpdate, b) - }) } } diff --git a/provider/akamai/akamai.go b/provider/akamai/akamai.go index 730cf15da0..6547edce08 100644 --- a/provider/akamai/akamai.go +++ b/provider/akamai/akamai.go @@ -193,7 +193,6 @@ func (p AkamaiProvider) fetchZones() (akamaiZones, error) { queryArgs.ContractIds = strings.Join(p.zoneIDFilter.ZoneIDs, ",") } resp, err := p.client.ListZones(queryArgs) // retrieve all primary zones filtered by contract ids - if err != nil { log.Errorf("Failed to fetch zones from Akamai") return filteredZones, err @@ -215,7 +214,7 @@ func (p AkamaiProvider) fetchZones() (akamaiZones, error) { return filteredZones, nil } -//Records returns the list of records in a given zone. +// Records returns the list of records in a given zone. func (p AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint, err error) { zones, err := p.fetchZones() // returns a filtered set of zones if err != nil { @@ -242,7 +241,7 @@ func (p AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint continue } var temp interface{} = int64(recordset.TTL) - var ttl = endpoint.TTL(temp.(int64)) + ttl := endpoint.TTL(temp.(int64)) endpoints = append(endpoints, endpoint.NewEndpointWithTTL(recordset.Name, recordset.Type, ttl, @@ -359,7 +358,7 @@ func trimTxtRdata(rdata []string, rtype string) []string { func ttlAsInt(src endpoint.TTL) int { var temp interface{} = int64(src) - var temp64 = temp.(int64) + temp64 := temp.(int64) var ttl int = edgeDNSRecordTTL if temp64 > 0 && temp64 <= int64(maxInt) { ttl = int(temp64) diff --git a/provider/akamai/akamai_test.go b/provider/akamai/akamai_test.go index 0edc078727..0e602097d3 100644 --- a/provider/akamai/akamai_test.go +++ b/provider/akamai/akamai_test.go @@ -19,9 +19,10 @@ package akamai import ( "context" "encoding/json" - log "github.com/sirupsen/logrus" "testing" + log "github.com/sirupsen/logrus" + dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2" "github.com/stretchr/testify/assert" "sigs.k8s.io/external-dns/endpoint" @@ -47,7 +48,6 @@ func newStub() *edgednsStub { } func createAkamaiStubProvider(stub *edgednsStub, domfilter endpoint.DomainFilter, idfilter provider.ZoneIDFilter) (*AkamaiProvider, error) { - akamaiConfig := AkamaiConfig{ DomainFilter: domfilter, ZoneIDFilter: idfilter, @@ -63,7 +63,6 @@ func createAkamaiStubProvider(stub *edgednsStub, domfilter endpoint.DomainFilter } func (r *edgednsStub) createStubDataEntry(objtype string) { - log.Debugf("Creating stub data entry") if _, exists := r.stubData[objtype]; !exists { r.stubData[objtype] = edgednsStubData{objType: objtype} @@ -73,7 +72,6 @@ func (r *edgednsStub) createStubDataEntry(objtype string) { } func (r *edgednsStub) setOutput(objtype string, output []interface{}) { - log.Debugf("Setting output to %v", output) r.createStubDataEntry(objtype) stubdata := r.stubData[objtype] @@ -84,7 +82,6 @@ func (r *edgednsStub) setOutput(objtype string, output []interface{}) { } func (r *edgednsStub) setUpdateRecords(objtype string, records []interface{}) { - log.Debugf("Setting updaterecords to %v", records) r.createStubDataEntry(objtype) stubdata := r.stubData[objtype] @@ -95,7 +92,6 @@ func (r *edgednsStub) setUpdateRecords(objtype string, records []interface{}) { } func (r *edgednsStub) setCreateRecords(objtype string, records []interface{}) { - log.Debugf("Setting createrecords to %v", records) r.createStubDataEntry(objtype) stubdata := r.stubData[objtype] @@ -106,7 +102,6 @@ func (r *edgednsStub) setCreateRecords(objtype string, records []interface{}) { } func (r *edgednsStub) ListZones(queryArgs dns.ZoneListQueryArgs) (*dns.ZoneListResponse, error) { - log.Debugf("Entering ListZones") // Ignore Metadata` resp := &dns.ZoneListResponse{} @@ -122,7 +117,6 @@ func (r *edgednsStub) ListZones(queryArgs dns.ZoneListQueryArgs) (*dns.ZoneListR } func (r *edgednsStub) GetRecordsets(zone string, queryArgs dns.RecordsetQueryArgs) (*dns.RecordSetResponse, error) { - log.Debugf("Entering GetRecordsets") // Ignore Metadata` resp := &dns.RecordSetResponse{} @@ -137,30 +131,25 @@ func (r *edgednsStub) GetRecordsets(zone string, queryArgs dns.RecordsetQueryArg } func (r *edgednsStub) CreateRecordsets(recordsets *dns.Recordsets, zone string, reclock bool) error { - return nil } func (r *edgednsStub) GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error) { - resp := &dns.RecordBody{} return resp, nil } func (r *edgednsStub) DeleteRecord(record *dns.RecordBody, zone string, recLock bool) error { - return nil } func (r *edgednsStub) UpdateRecord(record *dns.RecordBody, zone string, recLock bool) error { - return nil } // Test FetchZones func TestFetchZonesZoneIDFilter(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.NewZoneIDFilter([]string{"Test"}) @@ -176,7 +165,6 @@ func TestFetchZonesZoneIDFilter(t *testing.T) { } func TestFetchZonesEmpty(t *testing.T) { - stub := newStub() domfilter := endpoint.NewDomainFilter([]string{"Nonexistent"}) idfilter := provider.NewZoneIDFilter([]string{"Nonexistent"}) @@ -193,7 +181,6 @@ func TestFetchZonesEmpty(t *testing.T) { // TestAkamaiRecords tests record endpoint func TestAkamaiRecords(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.ZoneIDFilter{} @@ -229,7 +216,6 @@ func TestAkamaiRecords(t *testing.T) { } func TestAkamaiRecordsEmpty(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.NewZoneIDFilter([]string{"Nonexistent"}) @@ -244,7 +230,6 @@ func TestAkamaiRecordsEmpty(t *testing.T) { } func TestAkamaiRecordsFilters(t *testing.T) { - stub := newStub() domfilter := endpoint.NewDomainFilter([]string{"www.exclude.me"}) idfilter := provider.ZoneIDFilter{} @@ -275,7 +260,6 @@ func TestAkamaiRecordsFilters(t *testing.T) { // TestCreateRecords tests create function // (p AkamaiProvider) createRecordsets(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) error func TestCreateRecords(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.ZoneIDFilter{} @@ -292,7 +276,6 @@ func TestCreateRecords(t *testing.T) { } func TestCreateRecordsDomainFilter(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.ZoneIDFilter{} @@ -311,7 +294,6 @@ func TestCreateRecordsDomainFilter(t *testing.T) { // TestDeleteRecords validate delete func TestDeleteRecords(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.ZoneIDFilter{} @@ -327,9 +309,7 @@ func TestDeleteRecords(t *testing.T) { assert.Nil(t, err) } -// func TestDeleteRecordsDomainFilter(t *testing.T) { - stub := newStub() domfilter := endpoint.NewDomainFilter([]string{"example.com"}) idfilter := provider.ZoneIDFilter{} @@ -348,7 +328,6 @@ func TestDeleteRecordsDomainFilter(t *testing.T) { // Test record update func func TestUpdateRecords(t *testing.T) { - stub := newStub() domfilter := endpoint.DomainFilter{} idfilter := provider.ZoneIDFilter{} @@ -364,9 +343,7 @@ func TestUpdateRecords(t *testing.T) { assert.Nil(t, err) } -// func TestUpdateRecordsDomainFilter(t *testing.T) { - stub := newStub() domfilter := endpoint.NewDomainFilter([]string{"example.com"}) idfilter := provider.ZoneIDFilter{} @@ -384,7 +361,6 @@ func TestUpdateRecordsDomainFilter(t *testing.T) { } func TestAkamaiApplyChanges(t *testing.T) { - stub := newStub() domfilter := endpoint.NewDomainFilter([]string{"example.com"}) idfilter := provider.ZoneIDFilter{} diff --git a/provider/alibabacloud/alibaba_cloud.go b/provider/alibabacloud/alibaba_cloud.go index 54b3fd7b47..d64c947794 100644 --- a/provider/alibabacloud/alibaba_cloud.go +++ b/provider/alibabacloud/alibaba_cloud.go @@ -433,7 +433,6 @@ func (p *AlibabaCloudProvider) getDomainRecords(domainName string) ([]alidns.Rec request.PageNumber = "1" for { response, err := p.getDNSClient().DescribeDomainRecords(request) - if err != nil { log.Errorf("Failed to describe domain records for Alibaba Cloud DNS: %v", err) return nil, err @@ -449,7 +448,7 @@ func (p *AlibabaCloudProvider) getDomainRecords(domainName string) ([]alidns.Rec if !provider.SupportedRecordType(recordType) { continue } - //TODO filter Locked record + // TODO filter Locked record results = append(results, record) } nextPage := getNextPageNumber(response.PageNumber, defaultAlibabaCloudPageSize, response.TotalCount) @@ -761,7 +760,6 @@ func (p *AlibabaCloudProvider) getPrivateZones() (map[string]*alibabaPrivateZone recordsCount := 0 zones, err := p.privateZones() - if err != nil { return nil, err } @@ -776,7 +774,6 @@ func (p *AlibabaCloudProvider) getPrivateZones() (map[string]*alibabaPrivateZone for { response, err := p.getPvtzClient().DescribeZoneRecords(request) - if err != nil { log.Errorf("Failed to describe zone record '%s' in Alibaba Cloud DNS: %v", zone.ZoneId, err) return nil, err @@ -789,7 +786,7 @@ func (p *AlibabaCloudProvider) getPrivateZones() (map[string]*alibabaPrivateZone continue } - //TODO filter Locked + // TODO filter Locked records = append(records, record) } nextPage := getNextPageNumber(int64(response.PageNumber), defaultAlibabaCloudPageSize, int64(response.TotalItems)) diff --git a/provider/alibabacloud/alibaba_cloud_test.go b/provider/alibabacloud/alibaba_cloud_test.go index 6e78b6b1ae..2e0fc86527 100644 --- a/provider/alibabacloud/alibaba_cloud_test.go +++ b/provider/alibabacloud/alibaba_cloud_test.go @@ -435,7 +435,6 @@ func TestAlibabaCloudProvider_splitDNSName(t *testing.T) { } func TestAlibabaCloudProvider_TXTEndpoint(t *testing.T) { - p := newTestAlibabaCloudProvider(false) const recordValue = "heritage=external-dns,external-dns/owner=default" const endpointTarget = "\"heritage=external-dns,external-dns/owner=default\"" @@ -448,9 +447,8 @@ func TestAlibabaCloudProvider_TXTEndpoint(t *testing.T) { } } -//TestAlibabaCloudProvider_TXTEndpoint_PrivateZone +// TestAlibabaCloudProvider_TXTEndpoint_PrivateZone func TestAlibabaCloudProvider_TXTEndpoint_PrivateZone(t *testing.T) { - p := newTestAlibabaCloudProvider(true) const recordValue = "heritage=external-dns,external-dns/owner=default" const endpointTarget = "\"heritage=external-dns,external-dns/owner=default\"" diff --git a/provider/aws/aws.go b/provider/aws/aws.go index fbbe55cf4d..448c8d80bc 100644 --- a/provider/aws/aws.go +++ b/provider/aws/aws.go @@ -63,66 +63,66 @@ const ( sameZoneAlias = "same-zone" ) -var ( - // see: https://docs.aws.amazon.com/general/latest/gr/elb.html - canonicalHostedZones = map[string]string{ - // Application Load Balancers and Classic Load Balancers - "us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2", - "us-east-1.elb.amazonaws.com": "Z35SXDOTRQ7X7K", - "us-west-1.elb.amazonaws.com": "Z368ELLRRE2KJ0", - "us-west-2.elb.amazonaws.com": "Z1H1FL5HABSF5", - "ca-central-1.elb.amazonaws.com": "ZQSVJUPU6J1EY", - "ap-east-1.elb.amazonaws.com": "Z3DQVH9N71FHZ0", - "ap-south-1.elb.amazonaws.com": "ZP97RAFLXTNZK", - "ap-northeast-2.elb.amazonaws.com": "ZWKZPGTI48KDX", - "ap-northeast-3.elb.amazonaws.com": "Z5LXEXXYW11ES", - "ap-southeast-1.elb.amazonaws.com": "Z1LMS91P8CMLE5", - "ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65", - "ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT", - "eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5", - "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", - "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", - "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", - "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", - "eu-south-1.elb.amazonaws.com": "Z3ULH7SSC9OV64", - "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", - "cn-north-1.elb.amazonaws.com.cn": "Z1GDH35T77C1KE", - "cn-northwest-1.elb.amazonaws.com.cn": "ZM7IZAIOVVDZF", - "us-gov-west-1.elb.amazonaws.com": "Z33AYJ8TM3BH4J", - "us-gov-east-1.elb.amazonaws.com": "Z166TLBEWOO7G0", - "me-south-1.elb.amazonaws.com": "ZS929ML54UICD", - "af-south-1.elb.amazonaws.com": "Z268VQBMOI5EKX", - // Network Load Balancers - "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", - "elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI", - "elb.us-west-1.amazonaws.com": "Z24FKFUX50B4VW", - "elb.us-west-2.amazonaws.com": "Z18D5FSROUN65G", - "elb.ca-central-1.amazonaws.com": "Z2EPGBW3API2WT", - "elb.ap-east-1.amazonaws.com": "Z12Y7K3UBGUAD1", - "elb.ap-south-1.amazonaws.com": "ZVDDRBQ08TROA", - "elb.ap-northeast-2.amazonaws.com": "ZIBE1TIR4HY56", - "elb.ap-southeast-1.amazonaws.com": "ZKVM4W9LS7TM", - "elb.ap-southeast-2.amazonaws.com": "ZCT6FZBF4DROD", - "elb.ap-northeast-1.amazonaws.com": "Z31USIVHYNEOWT", - "elb.eu-central-1.amazonaws.com": "Z3F0SRJ5LGBH90", - "elb.eu-west-1.amazonaws.com": "Z2IFOLAFXWLO4F", - "elb.eu-west-2.amazonaws.com": "ZD4D7Y8KGAS4G", - "elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5", - "elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM", - "elb.eu-south-1.amazonaws.com": "Z23146JA1KNAFP", - "elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU", - "elb.cn-north-1.amazonaws.com.cn": "Z3QFB96KMJ7ED6", - "elb.cn-northwest-1.amazonaws.com.cn": "ZQEIKTCZ8352D", - "elb.us-gov-west-1.amazonaws.com": "ZMG1MZ2THAWF1", - "elb.us-gov-east-1.amazonaws.com": "Z1ZSMQQ6Q24QQ8", - "elb.me-south-1.amazonaws.com": "Z3QSRYVP46NYYV", - "elb.af-south-1.amazonaws.com": "Z203XCE67M25HM", - // Global Accelerator - "awsglobalaccelerator.com": "Z2BJ6XQ5FK7U4H", - // Cloudfront - "cloudfront.net": "Z2FDTNDATAQYW2", - } -) +// see: https://docs.aws.amazon.com/general/latest/gr/elb.html +var canonicalHostedZones = map[string]string{ + // Application Load Balancers and Classic Load Balancers + "us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2", + "us-east-1.elb.amazonaws.com": "Z35SXDOTRQ7X7K", + "us-west-1.elb.amazonaws.com": "Z368ELLRRE2KJ0", + "us-west-2.elb.amazonaws.com": "Z1H1FL5HABSF5", + "ca-central-1.elb.amazonaws.com": "ZQSVJUPU6J1EY", + "ap-east-1.elb.amazonaws.com": "Z3DQVH9N71FHZ0", + "ap-south-1.elb.amazonaws.com": "ZP97RAFLXTNZK", + "ap-northeast-2.elb.amazonaws.com": "ZWKZPGTI48KDX", + "ap-northeast-3.elb.amazonaws.com": "Z5LXEXXYW11ES", + "ap-southeast-1.elb.amazonaws.com": "Z1LMS91P8CMLE5", + "ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65", + "ap-southeast-3.elb.amazonaws.com": "Z08888821HLRG5A9ZRTER", + "ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT", + "eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5", + "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", + "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", + "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", + "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", + "eu-south-1.elb.amazonaws.com": "Z3ULH7SSC9OV64", + "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", + "cn-north-1.elb.amazonaws.com.cn": "Z1GDH35T77C1KE", + "cn-northwest-1.elb.amazonaws.com.cn": "ZM7IZAIOVVDZF", + "us-gov-west-1.elb.amazonaws.com": "Z33AYJ8TM3BH4J", + "us-gov-east-1.elb.amazonaws.com": "Z166TLBEWOO7G0", + "me-south-1.elb.amazonaws.com": "ZS929ML54UICD", + "af-south-1.elb.amazonaws.com": "Z268VQBMOI5EKX", + // Network Load Balancers + "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", + "elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI", + "elb.us-west-1.amazonaws.com": "Z24FKFUX50B4VW", + "elb.us-west-2.amazonaws.com": "Z18D5FSROUN65G", + "elb.ca-central-1.amazonaws.com": "Z2EPGBW3API2WT", + "elb.ap-east-1.amazonaws.com": "Z12Y7K3UBGUAD1", + "elb.ap-south-1.amazonaws.com": "ZVDDRBQ08TROA", + "elb.ap-northeast-2.amazonaws.com": "ZIBE1TIR4HY56", + "elb.ap-southeast-1.amazonaws.com": "ZKVM4W9LS7TM", + "elb.ap-southeast-2.amazonaws.com": "ZCT6FZBF4DROD", + "elb.ap-southeast-3.amazonaws.com": "Z01971771FYVNCOVWJU1G", + "elb.ap-northeast-1.amazonaws.com": "Z31USIVHYNEOWT", + "elb.eu-central-1.amazonaws.com": "Z3F0SRJ5LGBH90", + "elb.eu-west-1.amazonaws.com": "Z2IFOLAFXWLO4F", + "elb.eu-west-2.amazonaws.com": "ZD4D7Y8KGAS4G", + "elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5", + "elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM", + "elb.eu-south-1.amazonaws.com": "Z23146JA1KNAFP", + "elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU", + "elb.cn-north-1.amazonaws.com.cn": "Z3QFB96KMJ7ED6", + "elb.cn-northwest-1.amazonaws.com.cn": "ZQEIKTCZ8352D", + "elb.us-gov-west-1.amazonaws.com": "ZMG1MZ2THAWF1", + "elb.us-gov-east-1.amazonaws.com": "Z1ZSMQQ6Q24QQ8", + "elb.me-south-1.amazonaws.com": "Z3QSRYVP46NYYV", + "elb.af-south-1.amazonaws.com": "Z203XCE67M25HM", + // Global Accelerator + "awsglobalaccelerator.com": "Z2BJ6XQ5FK7U4H", + // Cloudfront + "cloudfront.net": "Z2FDTNDATAQYW2", +} // Route53API is the subset of the AWS Route53 API that we actually use. Add methods as required. Signatures must match exactly. // mostly taken from: https://github.com/kubernetes/kubernetes/blob/853167624edb6bc0cfdcdfb88e746e178f5db36c/federation/pkg/dnsprovider/providers/aws/route53/stubs/route53api.go @@ -526,7 +526,7 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes []*route53.Chan if _, err := p.client.ChangeResourceRecordSetsWithContext(ctx, params); err != nil { log.Errorf("Failure in zone %s [Id: %s]", aws.StringValue(zones[z].Name), z) - log.Error(err) //TODO(ideahitme): consider changing the interface in cases when this error might be a concern for other components + log.Error(err) // TODO(ideahitme): consider changing the interface in cases when this error might be a concern for other components failedUpdate = true } else { // z is the R53 Hosted Zone ID already as aws.StringValue @@ -663,7 +663,7 @@ func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint) (*route53. change.ResourceRecordSet.MultiValueAnswer = aws.Bool(true) } - var geolocation = &route53.GeoLocation{} + geolocation := &route53.GeoLocation{} useGeolocation := false if prop, ok := ep.GetProviderSpecificProperty(providerSpecificGeolocationContinentCode); ok { geolocation.ContinentCode = aws.String(prop.Value) @@ -818,7 +818,8 @@ func changesByZone(zones map[string]*route53.HostedZone, changeSet []*route53.Ch } // suitableZones returns all suitable private zones and the most suitable public zone -// for a given hostname and a set of zones. +// +// for a given hostname and a set of zones. func suitableZones(hostname string, zones map[string]*route53.HostedZone) []*route53.HostedZone { var matchingZones []*route53.HostedZone var publicZone *route53.HostedZone @@ -888,6 +889,12 @@ func canonicalHostedZone(hostname string) string { } } + if strings.HasSuffix(hostname, ".amazonaws.com") { + // hostname is an AWS hostname, but could not find canonical hosted zone. + // This could mean that a new region has been added but is not supported yet. + log.Warnf("Could not find canonical hosted zone for domain %s. This may be because your region is not supported yet.", hostname) + } + return "" } diff --git a/provider/aws/aws_test.go b/provider/aws/aws_test.go index c87de29f24..0a0e5a39aa 100644 --- a/provider/aws/aws_test.go +++ b/provider/aws/aws_test.go @@ -62,7 +62,7 @@ type Route53APIStub struct { // MockMethod starts a description of an expectation of the specified method // being called. // -// Route53APIStub.MockMethod("MyMethod", arg1, arg2) +// Route53APIStub.MockMethod("MyMethod", arg1, arg2) func (r *Route53APIStub) MockMethod(method string, args ...interface{}) *mock.Call { return r.m.On(method, args...) } @@ -1084,11 +1084,13 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.ap-northeast-3.elb.amazonaws.com", "Z5LXEXXYW11ES"}, {"foo.ap-southeast-1.elb.amazonaws.com", "Z1LMS91P8CMLE5"}, {"foo.ap-southeast-2.elb.amazonaws.com", "Z1GM3OXH4ZPM65"}, + {"foo.ap-southeast-3.elb.amazonaws.com", "Z08888821HLRG5A9ZRTER"}, {"foo.ap-northeast-1.elb.amazonaws.com", "Z14GRHDCWA56QT"}, {"foo.eu-central-1.elb.amazonaws.com", "Z215JYRZR1TBD5"}, {"foo.eu-west-1.elb.amazonaws.com", "Z32O12XQLNTSW2"}, {"foo.eu-west-2.elb.amazonaws.com", "ZHURV8PSTC4K8"}, {"foo.eu-west-3.elb.amazonaws.com", "Z3Q77PNBQS71R4"}, + {"foo.eu-south-1.elb.amazonaws.com", "Z3ULH7SSC9OV64"}, {"foo.sa-east-1.elb.amazonaws.com", "Z2P70J7HTTTPLU"}, {"foo.cn-north-1.elb.amazonaws.com.cn", "Z1GDH35T77C1KE"}, {"foo.cn-northwest-1.elb.amazonaws.com.cn", "ZM7IZAIOVVDZF"}, @@ -1104,11 +1106,13 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.elb.ap-northeast-2.amazonaws.com", "ZIBE1TIR4HY56"}, {"foo.elb.ap-southeast-1.amazonaws.com", "ZKVM4W9LS7TM"}, {"foo.elb.ap-southeast-2.amazonaws.com", "ZCT6FZBF4DROD"}, + {"foo.elb.ap-southeast-3.amazonaws.com", "Z01971771FYVNCOVWJU1G"}, {"foo.elb.ap-northeast-1.amazonaws.com", "Z31USIVHYNEOWT"}, {"foo.elb.eu-central-1.amazonaws.com", "Z3F0SRJ5LGBH90"}, {"foo.elb.eu-west-1.amazonaws.com", "Z2IFOLAFXWLO4F"}, {"foo.elb.eu-west-2.amazonaws.com", "ZD4D7Y8KGAS4G"}, {"foo.elb.eu-west-3.amazonaws.com", "Z1CMS0P5QUZ6D5"}, + {"foo.elb.eu-south-1.amazonaws.com", "Z23146JA1KNAFP"}, {"foo.elb.sa-east-1.amazonaws.com", "ZTK26PT1VY4CU"}, {"foo.elb.cn-north-1.amazonaws.com.cn", "Z3QFB96KMJ7ED6"}, {"foo.elb.cn-northwest-1.amazonaws.com.cn", "ZQEIKTCZ8352D"}, @@ -1237,7 +1241,6 @@ func setupAWSRecords(t *testing.T, provider *AWSProvider, endpoints []*endpoint. _, err = provider.Records(ctx) require.NoError(t, err) - } func listAWSRecords(t *testing.T, client Route53API, zone string) []*route53.ResourceRecordSet { @@ -1297,6 +1300,7 @@ func escapeAWSRecords(t *testing.T, provider *AWSProvider, zone string) { require.NoError(t, err) } } + func newAWSProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, evaluateTargetHealth, dryRun bool, records []*endpoint.Endpoint) (*AWSProvider, *Route53APIStub) { return newAWSProviderWithTagFilter(t, domainFilter, zoneIDFilter, zoneTypeFilter, provider.NewZoneTagFilter([]string{}), evaluateTargetHealth, dryRun, records) } diff --git a/provider/awssd/aws_sd.go b/provider/awssd/aws_sd.go index a87a8411d0..144a0e2978 100644 --- a/provider/awssd/aws_sd.go +++ b/provider/awssd/aws_sd.go @@ -18,13 +18,11 @@ package awssd import ( "context" - "strings" - "crypto/sha256" "encoding/hex" "fmt" - "regexp" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials/stscreds" @@ -493,7 +491,9 @@ func (p *AWSSDProvider) UpdateService(service *sd.Service, ep *endpoint.Endpoint Type: aws.String(srvType), TTL: aws.Int64(ttl), }}, - }}}) + }, + }, + }) if err != nil { return err } diff --git a/provider/awssd/aws_sd_test.go b/provider/awssd/aws_sd_test.go index 9821003792..ad73b07f91 100644 --- a/provider/awssd/aws_sd_test.go +++ b/provider/awssd/aws_sd_test.go @@ -50,7 +50,6 @@ type AWSSDClientStub struct { } func (s *AWSSDClientStub) CreateService(input *sd.CreateServiceInput) (*sd.CreateServiceOutput, error) { - srv := &sd.Service{ Id: aws.String(strconv.Itoa(rand.Intn(10000))), DnsConfig: input.DnsConfig, @@ -150,7 +149,6 @@ func (s *AWSSDClientStub) ListServicesPages(input *sd.ListServicesInput, fn func } func (s *AWSSDClientStub) RegisterInstance(input *sd.RegisterInstanceInput) (*sd.RegisterInstanceOutput, error) { - srvInstances, ok := s.instances[*input.ServiceId] if !ok { srvInstances = make(map[string]*sd.Instance) diff --git a/provider/azure/azure_privatedns_test.go b/provider/azure/azure_privatedns_test.go index 7f232430e5..f357201548 100644 --- a/provider/azure/azure_privatedns_test.go +++ b/provider/azure/azure_privatedns_test.go @@ -139,12 +139,15 @@ func privateOthersRecordSetPropertiesGetter(values []string, ttl int64) *private TTL: to.Int64Ptr(ttl), } } + func createPrivateMockRecordSet(name, recordType string, values ...string) privatedns.RecordSet { return createPrivateMockRecordSetMultiWithTTL(name, recordType, 0, values...) } + func createPrivateMockRecordSetWithTTL(name, recordType, value string, ttl int64) privatedns.RecordSet { return createPrivateMockRecordSetMultiWithTTL(name, recordType, ttl, value) } + func createPrivateMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values ...string) privatedns.RecordSet { var getterFunc func(values []string, ttl int64) *privatedns.RecordSetProperties @@ -163,7 +166,6 @@ func createPrivateMockRecordSetMultiWithTTL(name, recordType string, ttl int64, Type: to.StringPtr("Microsoft.Network/privateDnsZones/" + recordType), RecordSetProperties: getterFunc(values, ttl), } - } func (client *mockPrivateRecordSetsClient) ListComplete(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordSetNameSuffix string) (result privatedns.RecordSetListResultIterator, err error) { @@ -265,13 +267,11 @@ func TestAzurePrivateDNSRecord(t *testing.T) { createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), }) - if err != nil { t.Fatal(err) } actual, err := provider.Records(context.Background()) - if err != nil { t.Fatal(err) } @@ -284,7 +284,6 @@ func TestAzurePrivateDNSRecord(t *testing.T) { } validateAzureEndpoints(t, actual, expected) - } func TestAzurePrivateDNSMultiRecord(t *testing.T) { @@ -301,13 +300,11 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) { createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), }) - if err != nil { t.Fatal(err) } actual, err := provider.Records(context.Background()) - if err != nil { t.Fatal(err) } @@ -320,7 +317,6 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) { } validateAzureEndpoints(t, actual, expected) - } func TestAzurePrivateDNSApplyChanges(t *testing.T) { diff --git a/provider/azure/azure_test.go b/provider/azure/azure_test.go index a45defd143..0598dd4f3c 100644 --- a/provider/azure/azure_test.go +++ b/provider/azure/azure_test.go @@ -138,12 +138,15 @@ func othersRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetP TTL: to.Int64Ptr(ttl), } } + func createMockRecordSet(name, recordType string, values ...string) dns.RecordSet { return createMockRecordSetMultiWithTTL(name, recordType, 0, values...) } + func createMockRecordSetWithTTL(name, recordType, value string, ttl int64) dns.RecordSet { return createMockRecordSetMultiWithTTL(name, recordType, ttl, value) } + func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values ...string) dns.RecordSet { var getterFunc func(values []string, ttl int64) *dns.RecordSetProperties @@ -162,7 +165,6 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values Type: to.StringPtr("Microsoft.Network/dnszones/" + recordType), RecordSetProperties: getterFunc(values, ttl), } - } func (client *mockRecordSetsClient) ListAllByDNSZoneComplete(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordSetNameSuffix string) (result dns.RecordSetListResultIterator, err error) { @@ -270,14 +272,12 @@ func TestAzureRecord(t *testing.T) { createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), }) - if err != nil { t.Fatal(err) } ctx := context.Background() actual, err := provider.Records(ctx) - if err != nil { t.Fatal(err) } @@ -290,7 +290,6 @@ func TestAzureRecord(t *testing.T) { } validateAzureEndpoints(t, actual, expected) - } func TestAzureMultiRecord(t *testing.T) { @@ -307,14 +306,12 @@ func TestAzureMultiRecord(t *testing.T) { createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), }) - if err != nil { t.Fatal(err) } ctx := context.Background() actual, err := provider.Records(ctx) - if err != nil { t.Fatal(err) } @@ -327,7 +324,6 @@ func TestAzureMultiRecord(t *testing.T) { } validateAzureEndpoints(t, actual, expected) - } func TestAzureApplyChanges(t *testing.T) { @@ -461,14 +457,12 @@ func TestAzureNameFilter(t *testing.T) { createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), }) - if err != nil { t.Fatal(err) } ctx := context.Background() actual, err := provider.Records(ctx) - if err != nil { t.Fatal(err) } @@ -479,7 +473,6 @@ func TestAzureNameFilter(t *testing.T) { } validateAzureEndpoints(t, actual, expected) - } func TestAzureApplyChangesZoneName(t *testing.T) { diff --git a/provider/azure/config.go b/provider/azure/config.go index 5b282af8b4..67baed947d 100644 --- a/provider/azure/config.go +++ b/provider/azure/config.go @@ -109,7 +109,6 @@ func getAccessToken(cfg config, environment azure.Environment) (*adal.ServicePri token, err := adal.NewServicePrincipalTokenFromManagedIdentity(environment.ServiceManagementEndpoint, &adal.ManagedIdentityOptions{ ClientID: cfg.UserAssignedIdentityID, }) - if err != nil { return nil, fmt.Errorf("failed to create the managed service identity token: %v", err) } diff --git a/provider/azure/config_test.go b/provider/azure/config_test.go index bf591b0fc4..a2129113a3 100644 --- a/provider/azure/config_test.go +++ b/provider/azure/config_test.go @@ -18,11 +18,12 @@ package azure import ( "fmt" - "github.com/Azure/go-autorest/autorest/azure" "io/ioutil" "os" "reflect" "testing" + + "github.com/Azure/go-autorest/autorest/azure" ) func TestGetAzureEnvironmentConfig(t *testing.T) { diff --git a/provider/bluecat/bluecat_test.go b/provider/bluecat/bluecat_test.go index a5cc8787b3..ed7c0de048 100644 --- a/provider/bluecat/bluecat_test.go +++ b/provider/bluecat/bluecat_test.go @@ -48,14 +48,17 @@ type Changes struct { func (g mockGatewayClient) GetBluecatZones(zoneName string) ([]api.BluecatZone, error) { return *g.mockBluecatZones, nil } + func (g mockGatewayClient) GetHostRecords(zone string, records *[]api.BluecatHostRecord) error { *records = *g.mockBluecatHosts return nil } + func (g mockGatewayClient) GetCNAMERecords(zone string, records *[]api.BluecatCNAMERecord) error { *records = *g.mockBluecatCNAMEs return nil } + func (g mockGatewayClient) GetHostRecord(name string, record *api.BluecatHostRecord) error { for _, currentRecord := range *g.mockBluecatHosts { if currentRecord.Name == strings.Split(name, ".")[0] { @@ -65,6 +68,7 @@ func (g mockGatewayClient) GetHostRecord(name string, record *api.BluecatHostRec } return nil } + func (g mockGatewayClient) GetCNAMERecord(name string, record *api.BluecatCNAMERecord) error { for _, currentRecord := range *g.mockBluecatCNAMEs { if currentRecord.Name == strings.Split(name, ".")[0] { @@ -74,24 +78,30 @@ func (g mockGatewayClient) GetCNAMERecord(name string, record *api.BluecatCNAMER } return nil } + func (g mockGatewayClient) CreateHostRecord(zone string, req *api.BluecatCreateHostRecordRequest) (err error) { return nil } + func (g mockGatewayClient) CreateCNAMERecord(zone string, req *api.BluecatCreateCNAMERecordRequest) (err error) { return nil } + func (g mockGatewayClient) DeleteHostRecord(name string, zone string) (err error) { *g.mockBluecatHosts = nil return nil } + func (g mockGatewayClient) DeleteCNAMERecord(name string, zone string) (err error) { *g.mockBluecatCNAMEs = nil return nil } + func (g mockGatewayClient) GetTXTRecords(zone string, records *[]api.BluecatTXTRecord) error { *records = *g.mockBluecatTXTs return nil } + func (g mockGatewayClient) GetTXTRecord(name string, record *api.BluecatTXTRecord) error { for _, currentRecord := range *g.mockBluecatTXTs { if currentRecord.Name == name { @@ -101,13 +111,16 @@ func (g mockGatewayClient) GetTXTRecord(name string, record *api.BluecatTXTRecor } return nil } + func (g mockGatewayClient) CreateTXTRecord(zone string, req *api.BluecatCreateTXTRecordRequest) error { return nil } + func (g mockGatewayClient) DeleteTXTRecord(name string, zone string) error { *g.mockBluecatTXTs = nil return nil } + func (g mockGatewayClient) ServerFullDeploy() error { return nil } @@ -271,6 +284,7 @@ func TestBluecatApplyChangesCreate(t *testing.T) { validateEndpoints(t, actual, []*endpoint.Endpoint{}) } } + func TestBluecatApplyChangesDelete(t *testing.T) { client := mockGatewayClient{ mockBluecatZones: &[]api.BluecatZone{ @@ -354,7 +368,6 @@ func TestBluecatApplyChangesDeleteWithOwner(t *testing.T) { } validateEndpoints(t, actual, []*endpoint.Endpoint{}) } - } // TODO: ensure findZone method is tested diff --git a/provider/bluecat/gateway/api.go b/provider/bluecat/gateway/api.go index 2f43895407..8cd593462d 100644 --- a/provider/bluecat/gateway/api.go +++ b/provider/bluecat/gateway/api.go @@ -176,7 +176,6 @@ func GetBluecatGatewayToken(cfg BluecatConfig) (string, http.Cookie, error) { url := cfg.GatewayHost + "/rest_login" response, err := executeHTTPRequest(cfg.SkipTLSVerify, http.MethodPost, url, "", bytes.NewBuffer(body), http.Cookie{}) - if err != nil { return "", http.Cookie{}, errors.Wrap(err, "error obtaining API token from bluecat gateway") } diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index 411139fe2d..7fd5b2d420 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -96,9 +96,11 @@ func (z zoneService) CreateDNSRecord(ctx context.Context, zoneID string, rr clou func (z zoneService) DNSRecords(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) { return z.service.DNSRecords(ctx, zoneID, rr) } + func (z zoneService) UpdateDNSRecord(ctx context.Context, zoneID, recordID string, rr cloudflare.DNSRecord) error { return z.service.UpdateDNSRecord(ctx, zoneID, recordID, rr) } + func (z zoneService) DeleteDNSRecord(ctx context.Context, zoneID, recordID string) error { return z.service.DeleteDNSRecord(ctx, zoneID, recordID) } @@ -145,7 +147,7 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov return nil, fmt.Errorf("failed to initialize cloudflare provider: %v", err) } provider := &CloudFlareProvider{ - //Client: config, + // Client: config, Client: zoneService{config}, domainFilter: domainFilter, zoneIDFilter: zoneIDFilter, diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index 4a5ec3b07a..4d676fabcf 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -244,7 +244,6 @@ func AssertActions(t *testing.T, provider *CloudFlareProvider, endpoints []*endp ctx := context.Background() records, err := provider.Records(ctx) - if err != nil { t.Fatalf("cannot fetch records, %s", err) } @@ -309,7 +308,6 @@ func TestCloudflareA(t *testing.T) { }, []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, ) - } func TestCloudflareCname(t *testing.T) { @@ -501,7 +499,7 @@ func TestCloudflareProxiedOverrideIllegal(t *testing.T) { func TestCloudflareSetProxied(t *testing.T) { var proxied *bool = proxyEnabled var notProxied *bool = proxyDisabled - var testCases = []struct { + testCases := []struct { recordType string domain string proxiable *bool @@ -674,7 +672,6 @@ func TestCloudflareApplyChanges(t *testing.T) { Targets: endpoint.Targets{"target-new"}, }} err := provider.ApplyChanges(context.Background(), changes) - if err != nil { t.Errorf("should not fail, %s", err) } @@ -1111,7 +1108,6 @@ func TestCloudflareComplexUpdate(t *testing.T) { ctx := context.Background() records, err := provider.Records(ctx) - if err != nil { t.Errorf("should not fail, %s", err) } @@ -1146,7 +1142,7 @@ func TestCloudflareComplexUpdate(t *testing.T) { } td.CmpDeeply(t, client.Actions, []MockAction{ - MockAction{ + { Name: "Create", ZoneId: "001", RecordData: cloudflare.DNSRecord{ @@ -1157,7 +1153,7 @@ func TestCloudflareComplexUpdate(t *testing.T) { Proxied: proxyEnabled, }, }, - MockAction{ + { Name: "Update", ZoneId: "001", RecordId: "1234567890", @@ -1169,7 +1165,7 @@ func TestCloudflareComplexUpdate(t *testing.T) { Proxied: proxyEnabled, }, }, - MockAction{ + { Name: "Delete", ZoneId: "001", RecordId: "2345678901", @@ -1179,7 +1175,7 @@ func TestCloudflareComplexUpdate(t *testing.T) { func TestCustomTTLWithEnabledProxyNotChanged(t *testing.T) { client := NewMockCloudFlareClientWithRecords(map[string][]cloudflare.DNSRecord{ - "001": []cloudflare.DNSRecord{ + "001": { { ID: "1234567890", ZoneID: "001", @@ -1197,7 +1193,6 @@ func TestCustomTTLWithEnabledProxyNotChanged(t *testing.T) { } records, err := provider.Records(context.Background()) - if err != nil { t.Errorf("should not fail, %s", err) } diff --git a/provider/coredns/coredns.go b/provider/coredns/coredns.go index f4d16755cd..520dc54506 100644 --- a/provider/coredns/coredns.go +++ b/provider/coredns/coredns.go @@ -232,7 +232,7 @@ func getETCDConfig() (*etcdcv3.Config, error) { } } -//newETCDClient is an etcd client constructor +// newETCDClient is an etcd client constructor func newETCDClient() (coreDNSClient, error) { cfg, err := getETCDConfig() if err != nil { diff --git a/provider/designate/designate_test.go b/provider/designate/designate_test.go index 290ee295dd..0afc72fa6b 100644 --- a/provider/designate/designate_test.go +++ b/provider/designate/designate_test.go @@ -329,7 +329,6 @@ func TestDesignateCreateRecords(t *testing.T) { } func testDesignateCreateRecords(t *testing.T, client *fakeDesignateClient) []*recordsets.RecordSet { - for i, zoneName := range []string{"example.com.", "test.net."} { client.AddZone(zones.Zone{ ID: fmt.Sprintf("zone-%d", i+1), diff --git a/provider/digitalocean/digital_ocean_test.go b/provider/digitalocean/digital_ocean_test.go index 22c6849d96..ab53ca49cd 100644 --- a/provider/digitalocean/digital_ocean_test.go +++ b/provider/digitalocean/digital_ocean_test.go @@ -75,9 +75,11 @@ func (m *mockDigitalOceanClient) CreateRecord(context.Context, string, *godo.Dom func (m *mockDigitalOceanClient) Delete(context.Context, string) (*godo.Response, error) { return nil, nil } + func (m *mockDigitalOceanClient) DeleteRecord(ctx context.Context, domain string, id int) (*godo.Response, error) { return nil, nil } + func (m *mockDigitalOceanClient) EditRecord(ctx context.Context, domain string, id int, editRequest *godo.DomainRecordEditRequest) (*godo.DomainRecord, *godo.Response, error) { return &godo.DomainRecord{ID: 1}, nil, nil } @@ -157,9 +159,11 @@ func (m *mockDigitalOceanRecordsFail) CreateRecord(context.Context, string, *god func (m *mockDigitalOceanRecordsFail) Delete(context.Context, string) (*godo.Response, error) { return nil, nil } + func (m *mockDigitalOceanRecordsFail) DeleteRecord(ctx context.Context, domain string, id int) (*godo.Response, error) { return nil, nil } + func (m *mockDigitalOceanRecordsFail) EditRecord(ctx context.Context, domain string, id int, editRequest *godo.DomainRecordEditRequest) (*godo.DomainRecord, *godo.Response, error) { return &godo.DomainRecord{ID: 1}, nil, nil } diff --git a/provider/dnsimple/dnsimple_test.go b/provider/dnsimple/dnsimple_test.go index ed3032eddc..892cbe0e8b 100644 --- a/provider/dnsimple/dnsimple_test.go +++ b/provider/dnsimple/dnsimple_test.go @@ -32,9 +32,11 @@ import ( "sigs.k8s.io/external-dns/provider" ) -var mockProvider dnsimpleProvider -var dnsimpleListRecordsResponse dnsimple.ZoneRecordsResponse -var dnsimpleListZonesResponse dnsimple.ZonesResponse +var ( + mockProvider dnsimpleProvider + dnsimpleListRecordsResponse dnsimple.ZoneRecordsResponse + dnsimpleListZonesResponse dnsimple.ZonesResponse +) func TestDnsimpleServices(t *testing.T) { // Setup example responses diff --git a/provider/dyn/dyn.go b/provider/dyn/dyn.go index 49da03651c..4b5725392f 100644 --- a/provider/dyn/dyn.go +++ b/provider/dyn/dyn.go @@ -252,9 +252,9 @@ func apiRetryLoop(f func() error) error { func (d *dynProviderState) allRecordsToEndpoints(records *dynsoap.GetAllRecordsResponseType) []*endpoint.Endpoint { result := []*endpoint.Endpoint{} - //Convert each record to an endpoint + // Convert each record to an endpoint - //Process A Records + // Process A Records for _, rec := range records.Data.A_records { ep := &endpoint.Endpoint{ DNSName: rec.Fqdn, @@ -266,7 +266,7 @@ func (d *dynProviderState) allRecordsToEndpoints(records *dynsoap.GetAllRecordsR result = append(result, ep) } - //Process CNAME Records + // Process CNAME Records for _, rec := range records.Data.Cname_records { ep := &endpoint.Endpoint{ DNSName: rec.Fqdn, @@ -278,7 +278,7 @@ func (d *dynProviderState) allRecordsToEndpoints(records *dynsoap.GetAllRecordsR result = append(result, ep) } - //Process TXT Records + // Process TXT Records for _, rec := range records.Data.Txt_records { ep := &endpoint.Endpoint{ DNSName: rec.Fqdn, @@ -321,7 +321,6 @@ func (d *dynProviderState) fetchZoneSerial(client *dynect.Client, zone string) ( var resp dynect.ZoneResponse err := client.Do("GET", fmt.Sprintf("Zone/%s", zone), nil, &resp) - if err != nil { return 0, err } @@ -329,7 +328,7 @@ func (d *dynProviderState) fetchZoneSerial(client *dynect.Client, zone string) ( return resp.Data.Serial, nil } -//Use SOAP to fetch all records with a single call +// Use SOAP to fetch all records with a single call func (d *dynProviderState) fetchAllRecordsInZone(zone string) (*dynsoap.GetAllRecordsResponseType, error) { var err error @@ -368,7 +367,7 @@ func (d *dynProviderState) fetchAllRecordsInZone(zone string) (*dynsoap.GetAllRe Fault_incompat: 0, } - var records = &dynsoap.GetAllRecordsResponseType{} + records := &dynsoap.GetAllRecordsResponseType{} err = apiRetryLoop(func() error { records, err = service.GetAllRecords(&req) @@ -388,7 +387,7 @@ func (d *dynProviderState) fetchAllRecordsInZone(zone string) (*dynsoap.GetAllRe Fault_incompat: 0, } - var jobResults = dynsoap.GetJobResponseType{} + jobResults := dynsoap.GetJobResponseType{} err = apiRetryLoop(func() error { jobResults, err := service.GetJob(&jobRequest) if strings.ToLower(jobResults.Status) == "incomplete" { @@ -415,7 +414,7 @@ func (d *dynProviderState) buildLinkToRecord(ep *endpoint.Endpoint) string { if ep == nil { return "" } - var matchingZone = "" + matchingZone := "" for _, zone := range d.ZoneIDFilter.ZoneIDs { if strings.HasSuffix(ep.DNSName, zone) { matchingZone = zone @@ -448,10 +447,11 @@ func (d *dynProviderState) login() (*dynect.Client, error) { } client := dynect.NewClient(d.CustomerName) - var req = dynect.LoginBlock{ + req := dynect.LoginBlock{ Username: d.Username, Password: d.Password, - CustomerName: d.CustomerName} + CustomerName: d.CustomerName, + } var resp dynect.LoginResponse @@ -618,7 +618,7 @@ func (d *dynProviderState) Records(ctx context.Context) ([]*endpoint.Endpoint, e continue } - //Fetch All Records + // Fetch All Records records, err := d.fetchAllRecordsInZone(zone) if err != nil { return nil, err diff --git a/provider/exoscale/exoscale_test.go b/provider/exoscale/exoscale_test.go index d82d0d7cd1..b4ac332ab2 100644 --- a/provider/exoscale/exoscale_test.go +++ b/provider/exoscale/exoscale_test.go @@ -43,12 +43,13 @@ type updateRecordExoscale struct { updateDNSRecord egoscale.UpdateDNSRecord } -var createExoscale []createRecordExoscale -var deleteExoscale []deleteRecordExoscale -var updateExoscale []updateRecordExoscale +var ( + createExoscale []createRecordExoscale + deleteExoscale []deleteRecordExoscale + updateExoscale []updateRecordExoscale +) -type ExoscaleClientStub struct { -} +type ExoscaleClientStub struct{} func NewExoscaleClientStub() EgoscaleClientI { ep := &ExoscaleClientStub{} @@ -59,6 +60,7 @@ func (ep *ExoscaleClientStub) DeleteRecord(ctx context.Context, name string, rec deleteExoscale = append(deleteExoscale, deleteRecordExoscale{name: name, recordID: recordID}) return nil } + func (ep *ExoscaleClientStub) GetRecords(ctx context.Context, name string) ([]egoscale.DNSRecord, error) { init := []egoscale.DNSRecord{ {ID: 0, Name: "v4.barfoo.com", RecordType: "ALIAS"}, @@ -79,14 +81,17 @@ func (ep *ExoscaleClientStub) GetRecords(ctx context.Context, name string) ([]eg return rec, nil } + func (ep *ExoscaleClientStub) UpdateRecord(ctx context.Context, name string, rec egoscale.UpdateDNSRecord) (*egoscale.DNSRecord, error) { updateExoscale = append(updateExoscale, updateRecordExoscale{name: name, updateDNSRecord: rec}) return nil, nil } + func (ep *ExoscaleClientStub) CreateRecord(ctx context.Context, name string, rec egoscale.DNSRecord) (*egoscale.DNSRecord, error) { createExoscale = append(createExoscale, createRecordExoscale{name: name, rec: rec}) return nil, nil } + func (ep *ExoscaleClientStub) GetDomains(ctx context.Context) ([]egoscale.DNSDomain, error) { dom := []egoscale.DNSDomain{ {ID: 1, Name: "foo.com"}, diff --git a/provider/gandi/gandi_test.go b/provider/gandi/gandi_test.go index 64be771162..cd9529f05e 100644 --- a/provider/gandi/gandi_test.go +++ b/provider/gandi/gandi_test.go @@ -16,15 +16,15 @@ package gandi import ( "context" "fmt" - "github.com/go-gandi/go-gandi/domain" - "github.com/go-gandi/go-gandi/livedns" - "github.com/maxatome/go-testdeep/td" - "strings" - "os" "reflect" + "strings" "testing" + "github.com/go-gandi/go-gandi/domain" + "github.com/go-gandi/go-gandi/livedns" + "github.com/maxatome/go-testdeep/td" + "github.com/stretchr/testify/assert" "sigs.k8s.io/external-dns/endpoint" @@ -62,9 +62,11 @@ func mockGandiClientNewWithFailure(functionToFail string) *mockGandiClient { } } -const domainUriPrefix = "https://api.gandi.net/v5/domain/domains/" -const exampleDotComUri = domainUriPrefix + "example.com" -const exampleDotNetUri = domainUriPrefix + "example.net" +const ( + domainUriPrefix = "https://api.gandi.net/v5/domain/domains/" + exampleDotComUri = domainUriPrefix + "example.com" + exampleDotNetUri = domainUriPrefix + "example.net" +) func testRecords() []livedns.DomainRecord { return []livedns.DomainRecord{ @@ -286,7 +288,6 @@ func TestGandiProvider_TestData(t *testing.T) { if !reflect.DeepEqual(expectedRecordsAnswer, testingRecordsAnswer) { t.Errorf("should be equal, %s", err) } - } func TestGandiProvider_Records(t *testing.T) { @@ -319,7 +320,6 @@ func TestGandiProvider_Records(t *testing.T) { } func TestGandiProvider_RecordsAppliesDomainFilter(t *testing.T) { - mockedClient := mockGandiClientNew() mockedProvider := &GandiProvider{ @@ -343,7 +343,6 @@ func TestGandiProvider_RecordsAppliesDomainFilter(t *testing.T) { } func TestGandiProvider_RecordsErrorOnMultipleValues(t *testing.T) { - mockedClient := mockGandiClientNewWithRecords([]livedns.DomainRecord{ { RrsetValues: []string{"foo", "bar"}, diff --git a/provider/godaddy/client.go b/provider/godaddy/client.go index 3631387b1d..d4be96a321 100644 --- a/provider/godaddy/client.go +++ b/provider/godaddy/client.go @@ -259,7 +259,7 @@ func (c *Client) CallAPI(method, path string, reqBody, resType interface{}, need // - full serialized request body // - server current time (takes time delta into account) // -// Context is used by http.Client to handle context cancelation +// # Context is used by http.Client to handle context cancelation // // Call will automatically assemble the target url from the endpoint // configured in the client instance and the path argument. If the reqBody diff --git a/provider/godaddy/godaddy.go b/provider/godaddy/godaddy.go index fcc7a2cbd5..76b0520766 100644 --- a/provider/godaddy/godaddy.go +++ b/provider/godaddy/godaddy.go @@ -44,10 +44,8 @@ var actionNames = []string{ "delete", } -var ( - // ErrRecordToMutateNotFound when ApplyChange has to update/delete and didn't found the record in the existing zone (Change with no record ID) - ErrRecordToMutateNotFound = errors.New("record to mutate not found in current zone") -) +// ErrRecordToMutateNotFound when ApplyChange has to update/delete and didn't found the record in the existing zone (Change with no record ID) +var ErrRecordToMutateNotFound = errors.New("record to mutate not found in current zone") type gdClient interface { Patch(string, interface{}, interface{}) error @@ -141,7 +139,6 @@ func (z gdZoneIDName) findZoneRecord(hostname string) (suitableZoneID string, su // NewGoDaddyProvider initializes a new GoDaddy DNS based Provider. func NewGoDaddyProvider(ctx context.Context, domainFilter endpoint.DomainFilter, ttl int64, apiKey, apiSecret string, useOTE, dryRun bool) (*GDProvider, error) { client, err := NewClient(useOTE, apiKey, apiSecret) - if err != nil { return nil, err } @@ -177,7 +174,6 @@ func (p *GDProvider) zones() ([]string, error) { func (p *GDProvider) zonesRecords(ctx context.Context, all bool) ([]string, []gdRecords, error) { var allRecords []gdRecords zones, err := p.zones() - if err != nil { return nil, nil, err } @@ -186,7 +182,6 @@ func (p *GDProvider) zonesRecords(ctx context.Context, all bool) ([]string, []gd allRecords = []gdRecords{} } else if len(zones) == 1 { record, err := p.records(&ctx, zones[0], all) - if err != nil { return nil, nil, err } @@ -201,7 +196,6 @@ func (p *GDProvider) zonesRecords(ctx context.Context, all bool) ([]string, []gd zone := zoneName eg.Go(func() error { record, err := p.records(&ctx, zone, all) - if err != nil { return err } @@ -316,7 +310,6 @@ func (p *GDProvider) groupByNameAndType(zoneRecords []gdRecords) []*endpoint.End // Records returns the list of records in all relevant zones. func (p *GDProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { _, records, err := p.zonesRecords(ctx, false) - if err != nil { return nil, err } @@ -390,7 +383,6 @@ func (p *GDProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) er } _, records, err := p.zonesRecords(ctx, true) - if err != nil { return err } @@ -549,7 +541,6 @@ func maxOf(vars ...int64) int64 { func toString(obj interface{}) string { b, err := json.MarshalIndent(obj, "", " ") - if err != nil { return fmt.Sprintf("<%v>", err) } diff --git a/provider/google/google_test.go b/provider/google/google_test.go index 9037326c88..3fe6100914 100644 --- a/provider/google/google_test.go +++ b/provider/google/google_test.go @@ -745,7 +745,6 @@ func newGoogleProviderZoneOverlap(t *testing.T, domainFilter endpoint.DomainFilt provider.dryRun = dryRun return provider - } func newGoogleProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider { diff --git a/provider/infoblox/infoblox.go b/provider/infoblox/infoblox.go index 851799e335..38e7fe8581 100644 --- a/provider/infoblox/infoblox.go +++ b/provider/infoblox/infoblox.go @@ -156,7 +156,6 @@ func NewInfobloxProvider(ibStartupCfg StartupConfig) (*ProviderConfig, error) { requestor := &ibclient.WapiHttpRequestor{} client, err := ibclient.NewConnector(hostCfg, authCfg, transportConfig, requestBuilder, requestor) - if err != nil { return nil, err } diff --git a/provider/infoblox/infoblox_test.go b/provider/infoblox/infoblox_test.go index 6edaeece33..521b3f8f83 100644 --- a/provider/infoblox/infoblox_test.go +++ b/provider/infoblox/infoblox_test.go @@ -419,7 +419,6 @@ func TestInfobloxRecords(t *testing.T) { providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, false, &client) actual, err := providerCfg.Records(context.Background()) - if err != nil { t.Fatal(err) } @@ -471,7 +470,6 @@ func TestInfobloxAdjustEndpoints(t *testing.T) { } func TestInfobloxRecordsReverse(t *testing.T) { - client := mockIBConnector{ mockInfobloxZones: &[]ibclient.ZoneAuth{ createMockInfobloxZone("10.0.0.0/24"), @@ -485,7 +483,6 @@ func TestInfobloxRecordsReverse(t *testing.T) { providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"10.0.0.0/24"}), provider.NewZoneIDFilter([]string{""}), true, true, &client) actual, err := providerCfg.Records(context.Background()) - if err != nil { t.Fatal(err) } diff --git a/provider/inmemory/inmemory_test.go b/provider/inmemory/inmemory_test.go index 9c2b842e7c..2772afed62 100644 --- a/provider/inmemory/inmemory_test.go +++ b/provider/inmemory/inmemory_test.go @@ -29,9 +29,7 @@ import ( "sigs.k8s.io/external-dns/provider" ) -var ( - _ provider.Provider = &InMemoryProvider{} -) +var _ provider.Provider = &InMemoryProvider{} func TestInMemoryProvider(t *testing.T) { t.Run("findByType", testInMemoryFindByType) @@ -684,20 +682,17 @@ func testInMemoryApplyChanges(t *testing.T) { "org": { "example.org": []*inMemoryRecord{ { - Name: "example.org", Target: "8.8.8.8", Type: endpoint.RecordTypeA, }, { - Name: "example.org", Type: endpoint.RecordTypeTXT, }, }, "foo.org": []*inMemoryRecord{ { - Name: "foo.org", Target: "4.4.4.4", Type: endpoint.RecordTypeCNAME, @@ -792,7 +787,6 @@ func testInMemoryApplyChanges(t *testing.T) { }, } { t.Run(ti.title, func(t *testing.T) { - im := NewInMemoryProvider() c := &inMemoryClient{} c.zones = getInitData() diff --git a/provider/linode/linode.go b/provider/linode/linode.go index 3ecd2e55f5..75e2aac210 100644 --- a/provider/linode/linode.go +++ b/provider/linode/linode.go @@ -158,7 +158,6 @@ func (p *LinodeProvider) fetchZones(ctx context.Context) ([]linodego.Domain, err var zones []linodego.Domain allZones, err := p.Client.ListDomains(ctx, linodego.NewListOptions(0, "")) - if err != nil { return nil, err } @@ -262,7 +261,6 @@ func (p *LinodeProvider) ApplyChanges(ctx context.Context, changes *plan.Changes recordsByZoneID := make(map[string][]linodego.DomainRecord) zones, err := p.fetchZones(ctx) - if err != nil { return err } @@ -279,7 +277,6 @@ func (p *LinodeProvider) ApplyChanges(ctx context.Context, changes *plan.Changes // Fetch records for each zone for _, zone := range zones { records, err := p.fetchRecords(ctx, zone.ID) - if err != nil { return err } @@ -322,7 +319,6 @@ func (p *LinodeProvider) ApplyChanges(ctx context.Context, changes *plan.Changes } recordType, err := convertRecordType(ep.RecordType) - if err != nil { return err } @@ -371,7 +367,6 @@ func (p *LinodeProvider) ApplyChanges(ctx context.Context, changes *plan.Changes } recordType, err := convertRecordType(ep.RecordType) - if err != nil { return err } diff --git a/provider/linode/linode_test.go b/provider/linode/linode_test.go index b55e3762d1..8af9c2b268 100644 --- a/provider/linode/linode_test.go +++ b/provider/linode/linode_test.go @@ -43,14 +43,17 @@ func (m *MockDomainClient) ListDomains(ctx context.Context, opts *linodego.ListO args := m.Called(ctx, opts) return args.Get(0).([]linodego.Domain), args.Error(1) } + func (m *MockDomainClient) CreateDomainRecord(ctx context.Context, domainID int, opts linodego.DomainRecordCreateOptions) (*linodego.DomainRecord, error) { args := m.Called(ctx, domainID, opts) return args.Get(0).(*linodego.DomainRecord), args.Error(1) } + func (m *MockDomainClient) DeleteDomainRecord(ctx context.Context, domainID int, recordID int) error { args := m.Called(ctx, domainID, recordID) return args.Error(0) } + func (m *MockDomainClient) UpdateDomainRecord(ctx context.Context, domainID int, recordID int, opts linodego.DomainRecordUpdateOptions) (*linodego.DomainRecord, error) { args := m.Called(ctx, domainID, recordID, opts) return args.Get(0).(*linodego.DomainRecord), args.Error(1) diff --git a/provider/ns1/ns1.go b/provider/ns1/ns1.go index c0e3adde0d..8845daa966 100644 --- a/provider/ns1/ns1.go +++ b/provider/ns1/ns1.go @@ -184,7 +184,7 @@ func (p *NS1Provider) ns1BuildRecord(zoneName string, change *ns1Change) *dns.Re record.AddAnswer(dns.NewAnswer(strings.Split(v, " "))) } // set default ttl, but respect minTTLSeconds - var ttl = ns1DefaultTTL + ttl := ns1DefaultTTL if p.minTTLSeconds > ttl { ttl = p.minTTLSeconds } diff --git a/provider/oci/oci_test.go b/provider/oci/oci_test.go index 5f890a4b55..01bed1d97c 100644 --- a/provider/oci/oci_test.go +++ b/provider/oci/oci_test.go @@ -76,10 +76,11 @@ func (c *mockOCIDNSClient) GetZoneRecords(ctx context.Context, request dns.GetZo }} response.OpcNextPage = common.String("1") } else { - response.Items = []dns.Record{{Domain: common.String("bar.foo.com"), - Rdata: common.String("bar.com."), - Rtype: common.String(endpoint.RecordTypeCNAME), - Ttl: common.Int(ociRecordTTL), + response.Items = []dns.Record{{ + Domain: common.String("bar.foo.com"), + Rdata: common.String("bar.com."), + Rtype: common.String(endpoint.RecordTypeCNAME), + Ttl: common.Int(ociRecordTTL), }} } case "ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404": diff --git a/provider/pdns/pdns.go b/provider/pdns/pdns.go index cf8132b7fb..6f9ca02660 100644 --- a/provider/pdns/pdns.go +++ b/provider/pdns/pdns.go @@ -257,7 +257,7 @@ func NewPDNSProvider(ctx context.Context, config PDNSConfig) (*PDNSProvider, err func (p *PDNSProvider) convertRRSetToEndpoints(rr pgo.RrSet) (endpoints []*endpoint.Endpoint, _ error) { endpoints = []*endpoint.Endpoint{} - var targets = []string{} + targets := []string{} for _, record := range rr.Records { // If a record is "Disabled", it's not supposed to be "visible" diff --git a/provider/pdns/pdns_test.go b/provider/pdns/pdns_test.go index d9fd9b4011..5e7eeb365b 100644 --- a/provider/pdns/pdns_test.go +++ b/provider/pdns/pdns_test.go @@ -540,18 +540,20 @@ var ( /******************************************************************************/ // API that returns a zone with multiple record types -type PDNSAPIClientStub struct { -} +type PDNSAPIClientStub struct{} func (c *PDNSAPIClientStub) ListZones() ([]pgo.Zone, *http.Response, error) { return []pgo.Zone{ZoneMixed}, nil, nil } + func (c *PDNSAPIClientStub) PartitionZones(zones []pgo.Zone) ([]pgo.Zone, []pgo.Zone) { return zones, nil } + func (c *PDNSAPIClientStub) ListZone(zoneID string) (pgo.Zone, *http.Response, error) { return ZoneMixed, nil, nil } + func (c *PDNSAPIClientStub) PatchZone(zoneID string, zoneStruct pgo.Zone) (*http.Response, error) { return nil, nil } @@ -566,11 +568,12 @@ type PDNSAPIClientStubEmptyZones struct { func (c *PDNSAPIClientStubEmptyZones) ListZones() ([]pgo.Zone, *http.Response, error) { return []pgo.Zone{ZoneEmpty, ZoneEmptyLong, ZoneEmpty2}, nil, nil } + func (c *PDNSAPIClientStubEmptyZones) PartitionZones(zones []pgo.Zone) ([]pgo.Zone, []pgo.Zone) { return zones, nil } -func (c *PDNSAPIClientStubEmptyZones) ListZone(zoneID string) (pgo.Zone, *http.Response, error) { +func (c *PDNSAPIClientStubEmptyZones) ListZone(zoneID string) (pgo.Zone, *http.Response, error) { if strings.Contains(zoneID, "example.com") { return ZoneEmpty, nil, nil } else if strings.Contains(zoneID, "mock.test") { @@ -579,8 +582,8 @@ func (c *PDNSAPIClientStubEmptyZones) ListZone(zoneID string) (pgo.Zone, *http.R return ZoneEmptyLong, nil, nil } return pgo.Zone{}, nil, nil - } + func (c *PDNSAPIClientStubEmptyZones) PatchZone(zoneID string, zoneStruct pgo.Zone) (*http.Response, error) { c.patchedZones = append(c.patchedZones, zoneStruct) return nil, nil @@ -608,7 +611,6 @@ type PDNSAPIClientStubListZoneFailure struct { // Just overwrite the ListZone method to introduce a failure func (c *PDNSAPIClientStubListZoneFailure) ListZone(zoneID string) (pgo.Zone, *http.Response, error) { return pgo.Zone{}, nil, errors.New("Generic PDNS Error") - } /******************************************************************************/ @@ -635,7 +637,6 @@ func (c *PDNSAPIClientStubPartitionZones) ListZones() ([]pgo.Zone, *http.Respons } func (c *PDNSAPIClientStubPartitionZones) ListZone(zoneID string) (pgo.Zone, *http.Response, error) { - if strings.Contains(zoneID, "example.com") { return ZoneEmpty, nil, nil } else if strings.Contains(zoneID, "mock.test") { @@ -651,7 +652,6 @@ func (c *PDNSAPIClientStubPartitionZones) ListZone(zoneID string) (pgo.Zone, *ht // Just overwrite the ListZones method to introduce a failure func (c *PDNSAPIClientStubPartitionZones) PartitionZones(zones []pgo.Zone) ([]pgo.Zone, []pgo.Zone) { return []pgo.Zone{ZoneEmpty}, []pgo.Zone{ZoneEmptyLong, ZoneEmpty2} - } /******************************************************************************/ @@ -661,7 +661,6 @@ type NewPDNSProviderTestSuite struct { } func (suite *NewPDNSProviderTestSuite) TestPDNSProviderCreate() { - _, err := NewPDNSProvider( context.Background(), PDNSConfig{ @@ -701,7 +700,6 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSProviderCreate() { } func (suite *NewPDNSProviderTestSuite) TestPDNSProviderCreateTLS() { - _, err := NewPDNSProvider( context.Background(), PDNSConfig{ @@ -829,7 +827,6 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSRRSetToEndpoints() { eps, err = p.convertRRSetToEndpoints(RRSetDisabledRecord) assert.Nil(suite.T(), err) assert.Equal(suite.T(), endpointsDisabledRecord, eps) - } func (suite *NewPDNSProviderTestSuite) TestPDNSRecords() { @@ -861,7 +858,6 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSRecords() { } _, err = p.Records(ctx) assert.NotNil(suite.T(), err) - } func (suite *NewPDNSProviderTestSuite) TestPDNSConvertEndpointsToZones() { @@ -996,7 +992,6 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSmutateRecords() { // Check inserting endpoints from a single zone err = p.mutateRecords(endpointsSimpleRecord, pdnsChangeType("REPLACE")) assert.NotNil(suite.T(), err) - } func (suite *NewPDNSProviderTestSuite) TestPDNSClientPartitionZones() { diff --git a/provider/plural/client.go b/provider/plural/client.go new file mode 100644 index 0000000000..0db3659b5c --- /dev/null +++ b/provider/plural/client.go @@ -0,0 +1,146 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plural + +import ( + "context" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/pluralsh/gqlclient" + "github.com/pluralsh/gqlclient/pkg/utils" +) + +type authedTransport struct { + key string + wrapped http.RoundTripper +} + +func (t *authedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Set("Authorization", "Bearer "+t.key) + return t.wrapped.RoundTrip(req) +} + +type Client interface { + DnsRecords() ([]*DnsRecord, error) + CreateRecord(record *DnsRecord) (*DnsRecord, error) + DeleteRecord(name, ttype string) error +} + +type Config struct { + Token string + Endpoint string + Cluster string + Provider string +} + +type client struct { + ctx context.Context + pluralClient *gqlclient.Client + config *Config +} + +type DnsRecord struct { + Type string + Name string + Records []string +} + +func NewClient(conf *Config) (Client, error) { + base, err := conf.BaseUrl() + if err != nil { + return nil, err + } + httpClient := http.Client{ + Transport: &authedTransport{ + key: conf.Token, + wrapped: http.DefaultTransport, + }, + } + endpoint := base + "/gql" + return &client{ + ctx: context.Background(), + pluralClient: gqlclient.NewClient(&httpClient, endpoint), + config: conf, + }, nil +} + +func (c *Config) BaseUrl() (string, error) { + host := "https://app.plural.sh" + if c.Endpoint != "" { + host = fmt.Sprintf("https://%s", c.Endpoint) + if _, err := url.Parse(host); err != nil { + return "", err + } + } + return host, nil +} + +func (client *client) DnsRecords() ([]*DnsRecord, error) { + resp, err := client.pluralClient.GetDNSRecords(client.ctx, client.config.Cluster, gqlclient.Provider(strings.ToUpper(client.config.Provider))) + if err != nil { + return nil, err + } + + records := make([]*DnsRecord, 0) + for _, edge := range resp.DNSRecords.Edges { + if edge.Node != nil { + record := &DnsRecord{ + Type: string(edge.Node.Type), + Name: edge.Node.Name, + Records: utils.ConvertStringArrayPointer(edge.Node.Records), + } + records = append(records, record) + } + } + return records, nil +} + +func (client *client) CreateRecord(record *DnsRecord) (*DnsRecord, error) { + provider := gqlclient.Provider(strings.ToUpper(client.config.Provider)) + cluster := client.config.Cluster + attr := gqlclient.DNSRecordAttributes{ + Name: record.Name, + Type: gqlclient.DNSRecordType(record.Type), + Records: []*string{}, + } + + for _, record := range record.Records { + attr.Records = append(attr.Records, &record) + } + + resp, err := client.pluralClient.CreateDNSRecord(client.ctx, cluster, provider, attr) + if err != nil { + return nil, err + } + + return &DnsRecord{ + Type: string(resp.CreateDNSRecord.Type), + Name: resp.CreateDNSRecord.Name, + Records: utils.ConvertStringArrayPointer(resp.CreateDNSRecord.Records), + }, nil +} + +func (client *client) DeleteRecord(name, ttype string) error { + if _, err := client.pluralClient.DeleteDNSRecord(client.ctx, name, gqlclient.DNSRecordType(ttype)); err != nil { + return err + } + + return nil +} diff --git a/provider/plural/plural.go b/provider/plural/plural.go new file mode 100644 index 0000000000..c4dba70cc6 --- /dev/null +++ b/provider/plural/plural.go @@ -0,0 +1,145 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plural + +import ( + "context" + "fmt" + "os" + + log "github.com/sirupsen/logrus" + + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/plan" + "sigs.k8s.io/external-dns/provider" +) + +const ( + CreateAction = "c" + DeleteAction = "d" +) + +type PluralProvider struct { + provider.BaseProvider + Client Client +} + +type RecordChange struct { + Action string + Record *DnsRecord +} + +func NewPluralProvider(cluster, provider string) (*PluralProvider, error) { + token := os.Getenv("PLURAL_ACCESS_TOKEN") + endpoint := os.Getenv("PLURAL_ENDPOINT") + + if token == "" { + return nil, fmt.Errorf("No plural access token provided, you must set the PLURAL_ACCESS_TOKEN env var") + } + + config := &Config{ + Token: token, + Endpoint: endpoint, + Cluster: cluster, + Provider: provider, + } + + client, err := NewClient(config) + if err != nil { + return nil, err + } + + prov := &PluralProvider{ + Client: client, + } + + return prov, nil +} + +func (p *PluralProvider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, err error) { + records, err := p.Client.DnsRecords() + if err != nil { + return + } + + endpoints = make([]*endpoint.Endpoint, len(records)) + for i, record := range records { + endpoints[i] = endpoint.NewEndpoint(record.Name, record.Type, record.Records...) + } + return +} + +func (p *PluralProvider) PropertyValuesEqual(name, previous, current string) bool { + return p.BaseProvider.PropertyValuesEqual(name, previous, current) +} + +func (p *PluralProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint { + return endpoints +} + +func (p *PluralProvider) ApplyChanges(_ context.Context, diffs *plan.Changes) error { + var changes []*RecordChange + for _, endpoint := range diffs.Create { + changes = append(changes, makeChange(CreateAction, endpoint.Targets, endpoint)) + } + + for _, desired := range diffs.UpdateNew { + changes = append(changes, makeChange(CreateAction, desired.Targets, desired)) + } + + for _, deleted := range diffs.Delete { + changes = append(changes, makeChange(DeleteAction, []string{}, deleted)) + } + + return p.applyChanges(changes) +} + +func makeChange(change string, target []string, endpoint *endpoint.Endpoint) *RecordChange { + return &RecordChange{ + Action: change, + Record: &DnsRecord{ + Name: endpoint.DNSName, + Type: endpoint.RecordType, + Records: target, + }, + } +} + +func (p *PluralProvider) applyChanges(changes []*RecordChange) error { + for _, change := range changes { + logFields := log.Fields{ + "name": change.Record.Name, + "type": change.Record.Type, + "action": change.Action, + } + log.WithFields(logFields).Info("Changing record.") + + if change.Action == CreateAction { + _, err := p.Client.CreateRecord(change.Record) + if err != nil { + return err + } + } + if change.Action == DeleteAction { + if err := p.Client.DeleteRecord(change.Record.Name, change.Record.Type); err != nil { + return err + } + } + } + + return nil +} diff --git a/provider/plural/plural_test.go b/provider/plural/plural_test.go new file mode 100644 index 0000000000..cc693d9cb9 --- /dev/null +++ b/provider/plural/plural_test.go @@ -0,0 +1,377 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plural + +import ( + "context" + "testing" + + "sigs.k8s.io/external-dns/plan" + + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/internal/testutils" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/external-dns/provider" +) + +type ClientStub struct { + mockDnsRecords []*DnsRecord +} + +// CreateRecord provides a mock function with given fields: record +func (c *ClientStub) CreateRecord(record *DnsRecord) (*DnsRecord, error) { + c.mockDnsRecords = append(c.mockDnsRecords, record) + return record, nil +} + +// DeleteRecord provides a mock function with given fields: name, ttype +func (c *ClientStub) DeleteRecord(name string, ttype string) error { + newRecords := make([]*DnsRecord, 0) + for _, record := range c.mockDnsRecords { + if record.Name == name && record.Type == ttype { + continue + } + newRecords = append(newRecords, record) + } + c.mockDnsRecords = newRecords + return nil +} + +// DnsRecords provides a mock function with given fields: +func (c *ClientStub) DnsRecords() ([]*DnsRecord, error) { + return c.mockDnsRecords, nil +} + +func newPluralProvider(pluralDNSRecord []*DnsRecord) *PluralProvider { + if pluralDNSRecord == nil { + pluralDNSRecord = make([]*DnsRecord, 0) + } + return &PluralProvider{ + BaseProvider: provider.BaseProvider{}, + Client: &ClientStub{ + mockDnsRecords: pluralDNSRecord, + }, + } +} + +func TestPluralRecords(t *testing.T) { + tests := []struct { + name string + expectedEndpoints []*endpoint.Endpoint + records []*DnsRecord + }{ + { + name: "check records", + records: []*DnsRecord{ + { + Type: endpoint.RecordTypeA, + Name: "example.com", + Records: []string{"123.123.123.122"}, + }, + { + Type: endpoint.RecordTypeA, + Name: "nginx.example.com", + Records: []string{"123.123.123.123"}, + }, + { + Type: endpoint.RecordTypeCNAME, + Name: "hack.example.com", + Records: []string{"bluecatnetworks.com"}, + }, + { + Type: endpoint.RecordTypeTXT, + Name: "kdb.example.com", + Records: []string{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + expectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.122"}, + }, + { + DNSName: "nginx.example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.123"}, + }, + { + DNSName: "hack.example.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"bluecatnetworks.com"}, + }, + { + DNSName: "kdb.example.com", + RecordType: endpoint.RecordTypeTXT, + Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + provider := newPluralProvider(test.records) + + actual, err := provider.Records(context.Background()) + if err != nil { + t.Fatal(err) + } + validateEndpoints(t, actual, test.expectedEndpoints) + }) + } +} + +func TestPluralApplyChangesCreate(t *testing.T) { + tests := []struct { + name string + expectedEndpoints []*endpoint.Endpoint + }{ + { + name: "create new endpoints", + expectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.122"}, + }, + { + DNSName: "nginx.example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.123"}, + }, + { + DNSName: "hack.example.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"bluecatnetworks.com"}, + }, + { + DNSName: "kdb.example.com", + RecordType: endpoint.RecordTypeTXT, + Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + provider := newPluralProvider(nil) + + // no records + actual, err := provider.Records(context.Background()) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, len(actual), 0, "expected no entries") + + err = provider.ApplyChanges(context.Background(), &plan.Changes{Create: test.expectedEndpoints}) + if err != nil { + t.Fatal(err) + } + + actual, err = provider.Records(context.Background()) + if err != nil { + t.Fatal(err) + } + validateEndpoints(t, actual, test.expectedEndpoints) + }) + } +} + +func TestPluralApplyChangesDelete(t *testing.T) { + tests := []struct { + name string + records []*DnsRecord + deleteEndpoints []*endpoint.Endpoint + expectedEndpoints []*endpoint.Endpoint + }{ + { + name: "delete not existing record", + records: []*DnsRecord{ + { + Type: endpoint.RecordTypeA, + Name: "example.com", + Records: []string{"123.123.123.122"}, + }, + { + Type: endpoint.RecordTypeA, + Name: "nginx.example.com", + Records: []string{"123.123.123.123"}, + }, + { + Type: endpoint.RecordTypeCNAME, + Name: "hack.example.com", + Records: []string{"bluecatnetworks.com"}, + }, + { + Type: endpoint.RecordTypeTXT, + Name: "kdb.example.com", + Records: []string{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + deleteEndpoints: []*endpoint.Endpoint{ + { + DNSName: "fake.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"1.2.3.4"}, + }, + }, + expectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.122"}, + }, + { + DNSName: "nginx.example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.123"}, + }, + { + DNSName: "hack.example.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"bluecatnetworks.com"}, + }, + { + DNSName: "kdb.example.com", + RecordType: endpoint.RecordTypeTXT, + Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + }, + { + name: "delete one record", + records: []*DnsRecord{ + { + Type: endpoint.RecordTypeA, + Name: "example.com", + Records: []string{"123.123.123.122"}, + }, + { + Type: endpoint.RecordTypeA, + Name: "nginx.example.com", + Records: []string{"123.123.123.123"}, + }, + { + Type: endpoint.RecordTypeCNAME, + Name: "hack.example.com", + Records: []string{"bluecatnetworks.com"}, + }, + { + Type: endpoint.RecordTypeTXT, + Name: "kdb.example.com", + Records: []string{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + deleteEndpoints: []*endpoint.Endpoint{ + { + DNSName: "kdb.example.com", + RecordType: endpoint.RecordTypeTXT, + Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + expectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.122"}, + }, + { + DNSName: "nginx.example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.123"}, + }, + { + DNSName: "hack.example.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"bluecatnetworks.com"}, + }, + }, + }, + { + name: "delete all records", + records: []*DnsRecord{ + { + Type: endpoint.RecordTypeA, + Name: "example.com", + Records: []string{"123.123.123.122"}, + }, + { + Type: endpoint.RecordTypeA, + Name: "nginx.example.com", + Records: []string{"123.123.123.123"}, + }, + { + Type: endpoint.RecordTypeCNAME, + Name: "hack.example.com", + Records: []string{"bluecatnetworks.com"}, + }, + { + Type: endpoint.RecordTypeTXT, + Name: "kdb.example.com", + Records: []string{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + }, + deleteEndpoints: []*endpoint.Endpoint{ + { + DNSName: "kdb.example.com", + RecordType: endpoint.RecordTypeTXT, + Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/openshift-ingress/router-default"}, + }, + { + DNSName: "example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.122"}, + }, + { + DNSName: "nginx.example.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"123.123.123.123"}, + }, + { + DNSName: "hack.example.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"bluecatnetworks.com"}, + }, + }, + expectedEndpoints: []*endpoint.Endpoint{}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + provider := newPluralProvider(test.records) + + err := provider.ApplyChanges(context.Background(), &plan.Changes{Delete: test.deleteEndpoints}) + if err != nil { + t.Fatal(err) + } + + actual, err := provider.Records(context.Background()) + if err != nil { + t.Fatal(err) + } + validateEndpoints(t, actual, test.expectedEndpoints) + }) + } +} + +func validateEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) { + assert.True(t, testutils.SameEndpoints(endpoints, expected), "expected and actual endpoints don't match. %s:%s", endpoints, expected) +} diff --git a/provider/provider.go b/provider/provider.go index ff9c402101..06791204dc 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -34,8 +34,7 @@ type Provider interface { GetDomainFilter() endpoint.DomainFilterInterface } -type BaseProvider struct { -} +type BaseProvider struct{} func (b BaseProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint { return endpoints diff --git a/provider/rcode0/rcode0.go b/provider/rcode0/rcode0.go index 571857c21c..03edc73c3b 100644 --- a/provider/rcode0/rcode0.go +++ b/provider/rcode0/rcode0.go @@ -47,7 +47,6 @@ type RcodeZeroProvider struct { // Returns the provider or an error if a provider could not be created. func NewRcodeZeroProvider(domainFilter endpoint.DomainFilter, dryRun bool, txtEnc bool) (*RcodeZeroProvider, error) { client, err := rc0.NewClient(os.Getenv("RC0_API_KEY")) - if err != nil { return nil, err } @@ -106,7 +105,6 @@ func (p *RcodeZeroProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, for _, zone := range zones { rrset, err := p.fetchRecords(zone.Domain) - if err != nil { return nil, err } @@ -172,7 +170,6 @@ func (p *RcodeZeroProvider) fetchRecords(zoneName string) ([]*rc0.RRType, error) for { records, page, err := p.Client.RRSet.List(zoneName, listOptions) - if err != nil { return nil, err } @@ -249,7 +246,6 @@ func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error { switch change.ChangeType { case rc0.ChangeTypeADD: sr, err := p.Client.RRSet.Create(zoneName, []*rc0.RRSetChange{change}) - if err != nil { return err } @@ -260,7 +256,6 @@ func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error { case rc0.ChangeTypeUPDATE: sr, err := p.Client.RRSet.Edit(zoneName, []*rc0.RRSetChange{change}) - if err != nil { return err } @@ -271,7 +266,6 @@ func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error { case rc0.ChangeTypeDELETE: sr, err := p.Client.RRSet.Delete(zoneName, []*rc0.RRSetChange{change}) - if err != nil { return err } diff --git a/provider/rcode0/rcode0_test.go b/provider/rcode0/rcode0_test.go index 597c53163c..2887c89803 100644 --- a/provider/rcode0/rcode0_test.go +++ b/provider/rcode0/rcode0_test.go @@ -53,7 +53,6 @@ func (m *mockZoneManagementService) resetTestConditions() { } func TestRcodeZeroProvider_Records(t *testing.T) { - mockRRSetService := &mockRRSetService{} mockZoneManagementService := &mockZoneManagementService{} @@ -67,7 +66,6 @@ func TestRcodeZeroProvider_Records(t *testing.T) { ctx := context.Background() endpoints, err := provider.Records(ctx) // should return 6 rrs - if err != nil { t.Errorf("should not fail, %s", err) } @@ -79,11 +77,9 @@ func TestRcodeZeroProvider_Records(t *testing.T) { if err == nil { t.Errorf("expected to fail, %s", err) } - } func TestRcodeZeroProvider_ApplyChanges(t *testing.T) { - mockRRSetService := &mockRRSetService{} mockZoneManagementService := &mockZoneManagementService{} @@ -98,15 +94,12 @@ func TestRcodeZeroProvider_ApplyChanges(t *testing.T) { changes := mockChanges() err := provider.ApplyChanges(context.Background(), changes) - if err != nil { t.Errorf("should not fail, %s", err) } - } func TestRcodeZeroProvider_NewRcodezeroChanges(t *testing.T) { - provider := &RcodeZeroProvider{} changes := mockChanges() @@ -125,7 +118,6 @@ func TestRcodeZeroProvider_NewRcodezeroChanges(t *testing.T) { } func TestRcodeZeroProvider_NewRcodezeroChange(t *testing.T) { - _endpoint := &endpoint.Endpoint{ RecordType: "A", DNSName: "app." + testZoneOne, @@ -140,12 +132,10 @@ func TestRcodeZeroProvider_NewRcodezeroChange(t *testing.T) { require.Equal(t, _endpoint.RecordType, rrsetChange.Type) require.Equal(t, _endpoint.DNSName, rrsetChange.Name) require.Equal(t, _endpoint.Targets[0], rrsetChange.Records[0].Content) - //require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL) - + // require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL) } func Test_submitChanges(t *testing.T) { - mockRRSetService := &mockRRSetService{} mockZoneManagementService := &mockZoneManagementService{} @@ -164,11 +154,9 @@ func Test_submitChanges(t *testing.T) { if err == nil { t.Errorf("expected to fail, %s", err) } - } func mockRRSetChanges(condition int) []*rc0.RRSetChange { - switch condition { case rrsetChangesUnsupportedChangeType: return []*rc0.RRSetChange{ @@ -185,7 +173,6 @@ func mockRRSetChanges(condition int) []*rc0.RRSetChange { } func mockChanges() *plan.Changes { - changes := &plan.Changes{} changes.Create = []*endpoint.Endpoint{ @@ -202,7 +189,6 @@ func mockChanges() *plan.Changes { } func TestRcodeZeroProvider_Zones(t *testing.T) { - mockRRSetService := &mockRRSetService{} mockZoneManagementService := &mockZoneManagementService{} @@ -216,7 +202,6 @@ func TestRcodeZeroProvider_Zones(t *testing.T) { mockZoneManagementService.TestNilZonesReturned = true zones, err := provider.Zones() - if err != nil { t.Fatal(err) } @@ -229,14 +214,11 @@ func TestRcodeZeroProvider_Zones(t *testing.T) { if err == nil { t.Errorf("expected to fail, %s", err) } - } func TestNewRcodeZeroProvider(t *testing.T) { - _ = os.Setenv("RC0_API_KEY", "123") p, err := NewRcodeZeroProvider(endpoint.NewDomainFilter([]string{"ext-dns-test." + testZoneOne + "."}), true, true) - if err != nil { t.Errorf("should not fail, %s", err) } @@ -262,13 +244,11 @@ func TestNewRcodeZeroProvider(t *testing.T) { if err == nil { t.Errorf("expected to fail") } - } /* mocking mockRRSetServiceInterface */ func (m *mockRRSetService) List(zone string, options *rc0.ListOptions) ([]*rc0.RRType, *rc0.Page, error) { - if m.TestErrorReturned { return nil, nil, fmt.Errorf("operation RRSet.List failed") } @@ -341,20 +321,18 @@ func mockRRSet(zone string) []*rc0.RRType { } func (m *mockRRSetService) Create(zone string, rrsetCreate []*rc0.RRSetChange) (*rc0.StatusResponse, error) { - return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil - } -func (m *mockRRSetService) Edit(zone string, rrsetEdit []*rc0.RRSetChange) (*rc0.StatusResponse, error) { +func (m *mockRRSetService) Edit(zone string, rrsetEdit []*rc0.RRSetChange) (*rc0.StatusResponse, error) { return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } -func (m *mockRRSetService) Delete(zone string, rrsetDelete []*rc0.RRSetChange) (*rc0.StatusResponse, error) { +func (m *mockRRSetService) Delete(zone string, rrsetDelete []*rc0.RRSetChange) (*rc0.StatusResponse, error) { return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } -func (m *mockRRSetService) SubmitChangeSet(zone string, changeSet []*rc0.RRSetChange) (*rc0.StatusResponse, error) { +func (m *mockRRSetService) SubmitChangeSet(zone string, changeSet []*rc0.RRSetChange) (*rc0.StatusResponse, error) { return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } @@ -365,7 +343,6 @@ func (m *mockRRSetService) DecryptTXT(key []byte, rrType *rc0.RRType) {} /* mocking ZoneManagementServiceInterface */ func (m *mockZoneManagementService) List(options *rc0.ListOptions) ([]*rc0.Zone, *rc0.Page, error) { - if m.TestNilZonesReturned { return nil, nil, nil } @@ -407,6 +384,7 @@ func (m *mockZoneManagementService) Get(zone string) (*rc0.Zone, error) { return func (m *mockZoneManagementService) Create(zoneCreate *rc0.ZoneCreate) (*rc0.StatusResponse, error) { return nil, nil } + func (m *mockZoneManagementService) Edit(zone string, zoneEdit *rc0.ZoneEdit) (*rc0.StatusResponse, error) { return nil, nil } diff --git a/provider/rdns/rdns_test.go b/provider/rdns/rdns_test.go index 4bfed1ab95..99358f4ce3 100644 --- a/provider/rdns/rdns_test.go +++ b/provider/rdns/rdns_test.go @@ -282,7 +282,6 @@ func TestRDNSApplyChanges(t *testing.T) { } client.validateRecords(client.rs, expectedRecords3, t) - } func (c fakeEtcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRecord, error) { diff --git a/provider/recordfilter_test.go b/provider/recordfilter_test.go index fc88358191..d55e07deb1 100644 --- a/provider/recordfilter_test.go +++ b/provider/recordfilter_test.go @@ -19,7 +19,7 @@ package provider import "testing" func TestRecordTypeFilter(t *testing.T) { - var records = []struct { + records := []struct { rtype string expect bool }{ diff --git a/provider/rfc2136/rfc2136.go b/provider/rfc2136/rfc2136.go index 3f8a591dd4..c7d5684a0e 100644 --- a/provider/rfc2136/rfc2136.go +++ b/provider/rfc2136/rfc2136.go @@ -69,17 +69,15 @@ type rfc2136Provider struct { actions rfc2136Actions } -var ( - // Map of supported TSIG algorithms - tsigAlgs = map[string]string{ - "hmac-md5": dns.HmacMD5, - "hmac-sha1": dns.HmacSHA1, - "hmac-sha224": dns.HmacSHA224, - "hmac-sha256": dns.HmacSHA256, - "hmac-sha384": dns.HmacSHA384, - "hmac-sha512": dns.HmacSHA512, - } -) +// Map of supported TSIG algorithms +var tsigAlgs = map[string]string{ + "hmac-md5": dns.HmacMD5, + "hmac-sha1": dns.HmacSHA1, + "hmac-sha224": dns.HmacSHA224, + "hmac-sha256": dns.HmacSHA256, + "hmac-sha384": dns.HmacSHA384, + "hmac-sha512": dns.HmacSHA512, +} type rfc2136Actions interface { SendMessage(msg *dns.Msg) error @@ -347,7 +345,7 @@ func (r rfc2136Provider) UpdateRecord(m *dns.Msg, oldEp *endpoint.Endpoint, newE func (r rfc2136Provider) AddRecord(m *dns.Msg, ep *endpoint.Endpoint) error { log.Debugf("AddRecord.ep=%s", ep) - var ttl = int64(r.minTTL.Seconds()) + ttl := int64(r.minTTL.Seconds()) if ep.RecordTTL.IsConfigured() && int64(ep.RecordTTL) > ttl { ttl = int64(ep.RecordTTL) } diff --git a/provider/rfc2136/rfc2136_test.go b/provider/rfc2136/rfc2136_test.go index bc8a7106f6..38220e042f 100644 --- a/provider/rfc2136/rfc2136_test.go +++ b/provider/rfc2136/rfc2136_test.go @@ -208,7 +208,6 @@ func TestRfc2136ApplyChanges(t *testing.T) { assert.Equal(t, 2, len(stub.updateMsgs)) assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "v2.foo.com")) assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v2.foobar.com")) - } func TestRfc2136ApplyChangesWithDifferentTTLs(t *testing.T) { @@ -253,7 +252,6 @@ func TestRfc2136ApplyChangesWithDifferentTTLs(t *testing.T) { assert.True(t, strings.Contains(createRecords[2], "v3.foo.com")) assert.True(t, strings.Contains(createRecords[2], "4.3.3.3")) assert.True(t, strings.Contains(createRecords[2], "300")) - } func TestRfc2136ApplyChangesWithUpdate(t *testing.T) { @@ -331,7 +329,6 @@ func TestRfc2136ApplyChangesWithUpdate(t *testing.T) { assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v1.foobar.com")) assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "boom")) - } func TestChunkBy(t *testing.T) { diff --git a/provider/scaleway/scaleway.go b/provider/scaleway/scaleway.go index 614c877c80..29969fb2cb 100644 --- a/provider/scaleway/scaleway.go +++ b/provider/scaleway/scaleway.go @@ -272,11 +272,11 @@ func getCompleteZoneName(zone *domain.DNSZone) string { func endpointToScalewayRecords(zoneName string, ep *endpoint.Endpoint) []*domain.Record { // no annotation results in a TTL of 0, default to 300 for consistency with other providers - var ttl = scalewyRecordTTL + ttl := scalewyRecordTTL if ep.RecordTTL.IsConfigured() { ttl = uint32(ep.RecordTTL) } - var priority = scalewayDefaultPriority + priority := scalewayDefaultPriority if prop, ok := ep.GetProviderSpecificProperty(scalewayPriorityKey); ok { prio, err := strconv.ParseUint(prop.Value, 10, 32) if err != nil { diff --git a/provider/tencentcloud/cloudapi/api.go b/provider/tencentcloud/cloudapi/api.go new file mode 100644 index 0000000000..0294f6f15a --- /dev/null +++ b/provider/tencentcloud/cloudapi/api.go @@ -0,0 +1,60 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudapi + +import ( + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" +) + +type Action struct { + Service string `json:"service"` + Name string `json:"name"` + ReadOnly bool `json:"readOnly"` +} + +var ( + /* PrivateDNS */ + CreatePrivateZoneRecord = Action{Service: "PrivateDns", Name: "CreatePrivateZoneRecord", ReadOnly: false} + DeletePrivateZoneRecord = Action{Service: "PrivateDns", Name: "DeletePrivateZoneRecord", ReadOnly: false} + ModifyPrivateZoneRecord = Action{Service: "PrivateDns", Name: "ModifyPrivateZoneRecord", ReadOnly: false} + DescribePrivateZoneList = Action{Service: "PrivateDns", Name: "DescribePrivateZoneList", ReadOnly: true} + DescribePrivateZoneRecordList = Action{Service: "PrivateDns", Name: "DescribePrivateZoneRecordList", ReadOnly: true} + + /* DNSPod */ + DescribeDomainList = Action{Service: "DnsPod", Name: "DescribeDomainList", ReadOnly: true} + DescribeRecordList = Action{Service: "DnsPod", Name: "DescribeRecordList", ReadOnly: true} + CreateRecord = Action{Service: "DnsPod", Name: "CreateRecord", ReadOnly: false} + DeleteRecord = Action{Service: "DnsPod", Name: "DeleteRecord", ReadOnly: false} + ModifyRecord = Action{Service: "DnsPod", Name: "ModifyRecord", ReadOnly: false} +) + +type TencentAPIService interface { + // PrivateDNS + CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) + DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) + ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) + DescribePrivateZoneList(request *privatedns.DescribePrivateZoneListRequest) (response *privatedns.DescribePrivateZoneListResponse, err error) + DescribePrivateZoneRecordList(request *privatedns.DescribePrivateZoneRecordListRequest) (response *privatedns.DescribePrivateZoneRecordListResponse, err error) + + // DNSPod + DescribeDomainList(request *dnspod.DescribeDomainListRequest) (response *dnspod.DescribeDomainListResponse, err error) + DescribeRecordList(request *dnspod.DescribeRecordListRequest) (response *dnspod.DescribeRecordListResponse, err error) + CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) + DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) + ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) +} diff --git a/provider/tencentcloud/cloudapi/clientset.go b/provider/tencentcloud/cloudapi/clientset.go new file mode 100644 index 0000000000..c7f3078758 --- /dev/null +++ b/provider/tencentcloud/cloudapi/clientset.go @@ -0,0 +1,81 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudapi + +import ( + "fmt" + "sync" + + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" + "go.uber.org/ratelimit" +) + +type TencentClientSetService interface { + PrivateDnsCli(action string) *privatedns.Client + DnsPodCli(action string) *dnspod.Client +} + +func NewTencentClientSetService(region string, rate int, secretId string, secretKey string, internetEndpoint bool) *defaultTencentClientSetService { + p := &defaultTencentClientSetService{ + Region: region, + RateLimit: rate, + } + cred := common.NewCredential(secretId, secretKey) + + privatednsProf := profile.NewClientProfile() + if !internetEndpoint { + privatednsProf.HttpProfile.Endpoint = "privatedns.internal.tencentcloudapi.com" + } + p.privateDnsClient, _ = privatedns.NewClient(cred, region, privatednsProf) + + dnsPodProf := profile.NewClientProfile() + if !internetEndpoint { + dnsPodProf.HttpProfile.Endpoint = "dnspod.internal.tencentcloudapi.com" + } + p.dnsPodClient, _ = dnspod.NewClient(cred, region, dnsPodProf) + + return p +} + +type defaultTencentClientSetService struct { + Region string + RateLimit int + RateLimitSyncMap sync.Map + + privateDnsClient *privatedns.Client + dnsPodClient *dnspod.Client +} + +func (p *defaultTencentClientSetService) checkRateLimit(request, method string) { + action := fmt.Sprintf("%s_%s", request, method) + if rl, ok := p.RateLimitSyncMap.LoadOrStore(action, ratelimit.New(p.RateLimit, ratelimit.WithoutSlack)); ok { + rl.(ratelimit.Limiter).Take() + } +} + +func (p *defaultTencentClientSetService) PrivateDnsCli(action string) *privatedns.Client { + p.checkRateLimit("privateDns", action) + return p.privateDnsClient +} + +func (p *defaultTencentClientSetService) DnsPodCli(action string) *dnspod.Client { + p.checkRateLimit("dnsPod", action) + return p.dnsPodClient +} diff --git a/provider/tencentcloud/cloudapi/mockapi.go b/provider/tencentcloud/cloudapi/mockapi.go new file mode 100644 index 0000000000..87a43bf68f --- /dev/null +++ b/provider/tencentcloud/cloudapi/mockapi.go @@ -0,0 +1,247 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudapi + +import ( + "math/rand" + "time" + + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" +) + +type mockAPIService struct { + privateZones []*privatedns.PrivateZone + privateZoneRecords map[string][]*privatedns.PrivateZoneRecord + + dnspodDomains []*dnspod.DomainListItem + dnspodRecords map[string][]*dnspod.RecordListItem +} + +func NewMockService(privateZones []*privatedns.PrivateZone, privateZoneRecords map[string][]*privatedns.PrivateZoneRecord, dnspodDomains []*dnspod.DomainListItem, dnspodRecords map[string][]*dnspod.RecordListItem) *mockAPIService { + rand.Seed(time.Now().Unix()) + return &mockAPIService{ + privateZones: privateZones, + privateZoneRecords: privateZoneRecords, + dnspodDomains: dnspodDomains, + dnspodRecords: dnspodRecords, + } +} + +//////////////////////////////////////////////////////////////// +// PrivateDns API +//////////////////////////////////////////////////////////////// + +func (api *mockAPIService) CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) { + randomRecordId := RandStringRunes(8) + if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist { + api.privateZoneRecords[*request.ZoneId] = make([]*privatedns.PrivateZoneRecord, 0) + } + if request.TTL == nil { + request.TTL = common.Int64Ptr(300) + } + api.privateZoneRecords[*request.ZoneId] = append(api.privateZoneRecords[*request.ZoneId], &privatedns.PrivateZoneRecord{ + RecordId: common.StringPtr(randomRecordId), + ZoneId: request.ZoneId, + SubDomain: request.SubDomain, + RecordType: request.RecordType, + RecordValue: request.RecordValue, + TTL: request.TTL, + }) + return response, nil +} + +func (api *mockAPIService) DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) { + result := make([]*privatedns.PrivateZoneRecord, 0) + if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist { + return response, nil + } + for _, privateZoneRecord := range api.privateZoneRecords[*request.ZoneId] { + deleteflag := false + if request.RecordIdSet != nil && len(request.RecordIdSet) != 0 { + for _, recordId := range request.RecordIdSet { + if *privateZoneRecord.RecordId == *recordId { + deleteflag = true + break + } + } + } + if request.RecordId != nil && *request.RecordId == *privateZoneRecord.RecordId { + deleteflag = true + } + if !deleteflag { + result = append(result, privateZoneRecord) + } + } + api.privateZoneRecords[*request.ZoneId] = result + return response, nil +} + +func (api *mockAPIService) ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) { + if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist { + return response, nil + } + for _, privateZoneRecord := range api.privateZoneRecords[*request.ZoneId] { + if *privateZoneRecord.RecordId != *request.RecordId { + continue + } + privateZoneRecord.ZoneId = request.ZoneId + privateZoneRecord.SubDomain = request.SubDomain + privateZoneRecord.RecordType = request.RecordType + privateZoneRecord.RecordValue = request.RecordValue + privateZoneRecord.TTL = request.TTL + } + return response, nil +} + +func (api *mockAPIService) DescribePrivateZoneList(request *privatedns.DescribePrivateZoneListRequest) (response *privatedns.DescribePrivateZoneListResponse, err error) { + response = privatedns.NewDescribePrivateZoneListResponse() + response.Response = &struct { + TotalCount *int64 `json:"TotalCount,omitempty" name:"TotalCount"` + PrivateZoneSet []*privatedns.PrivateZone `json:"PrivateZoneSet,omitempty" name:"PrivateZoneSet"` + RequestId *string `json:"RequestId,omitempty" name:"RequestId"` + }{ + TotalCount: common.Int64Ptr(int64(len(api.privateZones))), + PrivateZoneSet: api.privateZones, + } + return response, nil +} + +func (api *mockAPIService) DescribePrivateZoneRecordList(request *privatedns.DescribePrivateZoneRecordListRequest) (response *privatedns.DescribePrivateZoneRecordListResponse, err error) { + response = privatedns.NewDescribePrivateZoneRecordListResponse() + response.Response = &struct { + TotalCount *int64 `json:"TotalCount,omitempty" name:"TotalCount"` + RecordSet []*privatedns.PrivateZoneRecord `json:"RecordSet,omitempty" name:"RecordSet"` + RequestId *string `json:"RequestId,omitempty" name:"RequestId"` + }{} + if _, exist := api.privateZoneRecords[*request.ZoneId]; !exist { + response.Response.TotalCount = common.Int64Ptr(0) + response.Response.RecordSet = make([]*privatedns.PrivateZoneRecord, 0) + return response, nil + } + response.Response.TotalCount = common.Int64Ptr(int64(len(api.privateZoneRecords[*request.ZoneId]))) + response.Response.RecordSet = api.privateZoneRecords[*request.ZoneId] + return response, nil +} + +//////////////////////////////////////////////////////////////// +// DnsPod API +//////////////////////////////////////////////////////////////// + +func (api *mockAPIService) DescribeDomainList(request *dnspod.DescribeDomainListRequest) (response *dnspod.DescribeDomainListResponse, err error) { + response = dnspod.NewDescribeDomainListResponse() + response.Response = &struct { + DomainCountInfo *dnspod.DomainCountInfo `json:"DomainCountInfo,omitempty" name:"DomainCountInfo"` + DomainList []*dnspod.DomainListItem `json:"DomainList,omitempty" name:"DomainList"` + RequestId *string `json:"RequestId,omitempty" name:"RequestId"` + }{} + response.Response.DomainList = api.dnspodDomains + response.Response.DomainCountInfo = &dnspod.DomainCountInfo{ + AllTotal: common.Uint64Ptr(uint64(len(api.dnspodDomains))), + } + return response, nil +} + +func (api *mockAPIService) DescribeRecordList(request *dnspod.DescribeRecordListRequest) (response *dnspod.DescribeRecordListResponse, err error) { + response = dnspod.NewDescribeRecordListResponse() + response.Response = &struct { + RecordCountInfo *dnspod.RecordCountInfo `json:"RecordCountInfo,omitempty" name:"RecordCountInfo"` + RecordList []*dnspod.RecordListItem `json:"RecordList,omitempty" name:"RecordList"` + RequestId *string `json:"RequestId,omitempty" name:"RequestId"` + }{} + if _, exist := api.dnspodRecords[*request.Domain]; !exist { + response.Response.RecordList = make([]*dnspod.RecordListItem, 0) + response.Response.RecordCountInfo = &dnspod.RecordCountInfo{ + TotalCount: common.Uint64Ptr(uint64(0)), + } + return response, nil + } + response.Response.RecordList = api.dnspodRecords[*request.Domain] + response.Response.RecordCountInfo = &dnspod.RecordCountInfo{ + TotalCount: common.Uint64Ptr(uint64(len(api.dnspodRecords[*request.Domain]))), + } + return response, nil +} + +func (api *mockAPIService) CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) { + randomRecordId := RandUint64() + if _, exist := api.dnspodRecords[*request.Domain]; !exist { + api.dnspodRecords[*request.Domain] = make([]*dnspod.RecordListItem, 0) + } + if request.TTL == nil { + request.TTL = common.Uint64Ptr(300) + } + api.dnspodRecords[*request.Domain] = append(api.dnspodRecords[*request.Domain], &dnspod.RecordListItem{ + RecordId: common.Uint64Ptr(randomRecordId), + Value: request.Value, + TTL: request.TTL, + Name: request.SubDomain, + Line: request.RecordLine, + LineId: request.RecordLineId, + Type: request.RecordType, + }) + return response, nil +} + +func (api *mockAPIService) DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) { + result := make([]*dnspod.RecordListItem, 0) + if _, exist := api.dnspodRecords[*request.Domain]; !exist { + return response, nil + } + for _, zoneRecord := range api.dnspodRecords[*request.Domain] { + deleteflag := false + if request.RecordId != nil && *request.RecordId == *zoneRecord.RecordId { + deleteflag = true + } + if !deleteflag { + result = append(result, zoneRecord) + } + } + api.dnspodRecords[*request.Domain] = result + return response, nil +} + +func (api *mockAPIService) ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) { + if _, exist := api.dnspodRecords[*request.Domain]; !exist { + return response, nil + } + for _, zoneRecord := range api.dnspodRecords[*request.Domain] { + if *zoneRecord.RecordId != *request.RecordId { + continue + } + zoneRecord.Type = request.RecordType + zoneRecord.Name = request.SubDomain + zoneRecord.Value = request.Value + zoneRecord.TTL = request.TTL + } + return response, nil +} + +var letterRunes = []byte("abcdefghijklmnopqrstuvwxyz") + +func RandStringRunes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} + +func RandUint64() uint64 { + return rand.Uint64() +} diff --git a/provider/tencentcloud/cloudapi/readonlyapi.go b/provider/tencentcloud/cloudapi/readonlyapi.go new file mode 100644 index 0000000000..c86662deb9 --- /dev/null +++ b/provider/tencentcloud/cloudapi/readonlyapi.go @@ -0,0 +1,78 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudapi + +import ( + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" +) + +type readonlyAPIService struct { + defaultTencentAPIService +} + +func NewReadOnlyAPIService(region string, rate int, secretId string, secretKey string, internetEndpoint bool) *readonlyAPIService { + apiService := NewTencentAPIService(region, rate, secretId, secretKey, internetEndpoint) + tencentAPIService := &readonlyAPIService{ + *apiService, + } + return tencentAPIService +} + +//////////////////////////////////////////////////////////////// +// PrivateDns API +//////////////////////////////////////////////////////////////// + +func (api *readonlyAPIService) CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) { + apiAction := CreatePrivateZoneRecord + APIRecord(apiAction, JsonWrapper(request), "dryRun") + return response, nil +} + +func (api *readonlyAPIService) DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) { + apiAction := DeletePrivateZoneRecord + APIRecord(apiAction, JsonWrapper(request), "dryRun") + return response, nil +} + +func (api *readonlyAPIService) ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) { + apiAction := ModifyPrivateZoneRecord + APIRecord(apiAction, JsonWrapper(request), "dryRun") + return response, nil +} + +//////////////////////////////////////////////////////////////// +// DnsPod API +//////////////////////////////////////////////////////////////// + +func (api *readonlyAPIService) CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) { + apiAction := CreateRecord + APIRecord(apiAction, JsonWrapper(request), "dryRun") + return response, nil +} + +func (api *readonlyAPIService) DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) { + apiAction := DeleteRecord + APIRecord(apiAction, JsonWrapper(request), "dryRun") + return response, nil +} + +func (api *readonlyAPIService) ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) { + apiAction := ModifyRecord + APIRecord(apiAction, JsonWrapper(request), "dryRun") + return response, nil +} diff --git a/provider/tencentcloud/cloudapi/tencentapi.go b/provider/tencentcloud/cloudapi/tencentapi.go new file mode 100644 index 0000000000..95f119c7c9 --- /dev/null +++ b/provider/tencentcloud/cloudapi/tencentapi.go @@ -0,0 +1,279 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudapi + +import ( + "encoding/json" + "fmt" + "net" + "time" + + log "github.com/sirupsen/logrus" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" +) + +type defaultTencentAPIService struct { + RetryDefault int + TaskCheckInterval time.Duration + ClientSetService TencentClientSetService +} + +func NewTencentAPIService(region string, rate int, secretId string, secretKey string, internetEndpoint bool) *defaultTencentAPIService { + tencentAPIService := &defaultTencentAPIService{ + RetryDefault: 3, + TaskCheckInterval: 3 * time.Second, + ClientSetService: NewTencentClientSetService(region, rate, secretId, secretKey, internetEndpoint), + } + return tencentAPIService +} + +//////////////////////////////////////////////////////////////// +// PrivateDns API +//////////////////////////////////////////////////////////////// + +func (api *defaultTencentAPIService) CreatePrivateZoneRecord(request *privatedns.CreatePrivateZoneRecordRequest) (response *privatedns.CreatePrivateZoneRecordResponse, err error) { + apiAction := CreatePrivateZoneRecord + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.PrivateDnsCli(apiAction.Name) + if response, err = client.CreatePrivateZoneRecord(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) DeletePrivateZoneRecord(request *privatedns.DeletePrivateZoneRecordRequest) (response *privatedns.DeletePrivateZoneRecordResponse, err error) { + apiAction := DeletePrivateZoneRecord + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.PrivateDnsCli(apiAction.Name) + if response, err = client.DeletePrivateZoneRecord(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) ModifyPrivateZoneRecord(request *privatedns.ModifyPrivateZoneRecordRequest) (response *privatedns.ModifyPrivateZoneRecordResponse, err error) { + apiAction := ModifyPrivateZoneRecord + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.PrivateDnsCli(apiAction.Name) + if response, err = client.ModifyPrivateZoneRecord(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) DescribePrivateZoneList(request *privatedns.DescribePrivateZoneListRequest) (response *privatedns.DescribePrivateZoneListResponse, err error) { + apiAction := DescribePrivateZoneList + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.PrivateDnsCli(apiAction.Name) + if response, err = client.DescribePrivateZoneList(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) DescribePrivateZoneRecordList(request *privatedns.DescribePrivateZoneRecordListRequest) (response *privatedns.DescribePrivateZoneRecordListResponse, err error) { + apiAction := DescribePrivateZoneRecordList + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.PrivateDnsCli(apiAction.Name) + if response, err = client.DescribePrivateZoneRecordList(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +//////////////////////////////////////////////////////////////// +// DnsPod API +//////////////////////////////////////////////////////////////// + +func (api *defaultTencentAPIService) DescribeDomainList(request *dnspod.DescribeDomainListRequest) (response *dnspod.DescribeDomainListResponse, err error) { + apiAction := DescribeDomainList + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.DnsPodCli(apiAction.Name) + if response, err = client.DescribeDomainList(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) DescribeRecordList(request *dnspod.DescribeRecordListRequest) (response *dnspod.DescribeRecordListResponse, err error) { + apiAction := DescribeRecordList + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.DnsPodCli(apiAction.Name) + if response, err = client.DescribeRecordList(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) CreateRecord(request *dnspod.CreateRecordRequest) (response *dnspod.CreateRecordResponse, err error) { + apiAction := CreateRecord + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.DnsPodCli(apiAction.Name) + if response, err = client.CreateRecord(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) DeleteRecord(request *dnspod.DeleteRecordRequest) (response *dnspod.DeleteRecordResponse, err error) { + apiAction := DeleteRecord + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.DnsPodCli(apiAction.Name) + if response, err = client.DeleteRecord(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +func (api *defaultTencentAPIService) ModifyRecord(request *dnspod.ModifyRecordRequest) (response *dnspod.ModifyRecordResponse, err error) { + apiAction := ModifyRecord + for times := 1; times <= api.RetryDefault; times++ { + client := api.ClientSetService.DnsPodCli(apiAction.Name) + if response, err = client.ModifyRecord(request); err != nil { + requestJson := JsonWrapper(request) + if retry := dealWithError(apiAction, requestJson, err); retry == false || times == api.RetryDefault { + APIErrorRecord(apiAction, requestJson, JsonWrapper(response), err) + return nil, err + } + continue + } + break + } + APIRecord(apiAction, JsonWrapper(request), JsonWrapper(response)) + return response, nil +} + +//////////////////////////////////////////////////////////////// +// API Error Report +//////////////////////////////////////////////////////////////// + +func dealWithError(action Action, request string, err error) bool { + log.Errorf("dealWithError %s/%s request: %s, error: %s.", action.Service, action.Name, request, err.Error()) + if sdkError, ok := err.(*errors.TencentCloudSDKError); ok { + if sdkError.Code == "RequestLimitExceeded" { + return true + } else if sdkError.Code == "InternalError" || sdkError.Code == "ClientError.HttpStatusCodeError" { + return false + } else if sdkError.Code == "ClientError.NetworkError" { + return false + } else if sdkError.Code == "AuthFailure.UnauthorizedOperation" || sdkError.Code == "UnauthorizedOperation.CamNoAuth" { + return false + } + return false + } + + if _, ok := err.(net.Error); ok { + return true + } + + return false +} + +func APIErrorRecord(apiAction Action, request string, response string, err error) { + log.Infof(fmt.Sprintf("APIError API: %s/%s Request: %s, Response: %s, Error: %s", apiAction.Service, apiAction.Name, request, response, err.Error())) +} + +func APIRecord(apiAction Action, request string, response string) { + message := fmt.Sprintf("APIRecord API: %s/%s Request: %s, Response: %s", apiAction.Service, apiAction.Name, request, response) + + if apiAction.ReadOnly { + // log.Infof(message) + } else { + log.Infof(message) + } +} + +func JsonWrapper(obj interface{}) string { + if jsonStr, jsonErr := json.Marshal(obj); jsonErr == nil { + return string(jsonStr) + } + return "json_format_error" +} diff --git a/provider/tencentcloud/dnspod.go b/provider/tencentcloud/dnspod.go new file mode 100644 index 0000000000..f2e714cf89 --- /dev/null +++ b/provider/tencentcloud/dnspod.go @@ -0,0 +1,281 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tencentcloud + +import ( + "fmt" + "strconv" + "strings" + + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/plan" + "sigs.k8s.io/external-dns/provider" +) + +// DnsPod For Public Dns + +func (p *TencentCloudProvider) dnsRecords() ([]*endpoint.Endpoint, error) { + recordsList, err := p.recordsForDNS() + if err != nil { + return nil, err + } + + endpoints := make([]*endpoint.Endpoint, 0) + recordMap := groupDomainRecordList(recordsList) + for _, recordList := range recordMap { + name := getDnsDomain(*recordList.RecordList[0].Name, *recordList.Domain.Name) + recordType := *recordList.RecordList[0].Type + ttl := *recordList.RecordList[0].TTL + var targets []string + for _, record := range recordList.RecordList { + targets = append(targets, *record.Value) + } + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...)) + } + return endpoints, nil +} + +func (p *TencentCloudProvider) recordsForDNS() (map[uint64]*RecordListGroup, error) { + domainList, err := p.getDomainList() + if err != nil { + return nil, err + } + + recordListGroup := make(map[uint64]*RecordListGroup, 0) + for _, domain := range domainList { + records, err := p.getDomainRecordList(*domain.Name) + if err != nil { + return nil, err + } + for _, record := range records { + if *record.Type == "TXT" && strings.HasPrefix(*record.Value, "heritage=") { + record.Value = common.StringPtr(fmt.Sprintf(`"%s"`, *record.Value)) + } + } + recordListGroup[*domain.DomainId] = &RecordListGroup{ + Domain: domain, + RecordList: records, + } + } + return recordListGroup, nil +} + +func (p *TencentCloudProvider) getDomainList() ([]*dnspod.DomainListItem, error) { + request := dnspod.NewDescribeDomainListRequest() + request.Offset = common.Int64Ptr(0) + request.Limit = common.Int64Ptr(3000) + + domainList := make([]*dnspod.DomainListItem, 0) + totalCount := int64(100) + for *request.Offset < totalCount { + response, err := p.apiService.DescribeDomainList(request) + if err != nil { + return nil, err + } + if response.Response.DomainList != nil && len(response.Response.DomainList) > 0 { + if !p.domainFilter.IsConfigured() { + domainList = append(domainList, response.Response.DomainList...) + } else { + for _, domain := range response.Response.DomainList { + if p.domainFilter.Match(*domain.Name) { + domainList = append(domainList, domain) + } + } + } + } + totalCount = int64(*response.Response.DomainCountInfo.AllTotal) + request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.DomainList))) + } + return domainList, nil +} + +func (p *TencentCloudProvider) getDomainRecordList(domain string) ([]*dnspod.RecordListItem, error) { + request := dnspod.NewDescribeRecordListRequest() + request.Domain = common.StringPtr(domain) + request.Offset = common.Uint64Ptr(0) + request.Limit = common.Uint64Ptr(3000) + + domainList := make([]*dnspod.RecordListItem, 0) + totalCount := uint64(100) + for *request.Offset < totalCount { + response, err := p.apiService.DescribeRecordList(request) + if err != nil { + return nil, err + } + if response.Response.RecordList != nil && len(response.Response.RecordList) > 0 { + for _, record := range response.Response.RecordList { + if *record.Name == "@" && *record.Type == "NS" { // Special Record, Skip it. + continue + } + domainList = append(domainList, record) + } + } + totalCount = *response.Response.RecordCountInfo.TotalCount + request.Offset = common.Uint64Ptr(*request.Offset + uint64(len(response.Response.RecordList))) + } + return domainList, nil +} + +type RecordListGroup struct { + Domain *dnspod.DomainListItem + RecordList []*dnspod.RecordListItem +} + +func (p *TencentCloudProvider) applyChangesForDNS(changes *plan.Changes) error { + recordsGroupMap, err := p.recordsForDNS() + if err != nil { + return err + } + + zoneNameIDMapper := provider.ZoneIDName{} + for _, recordsGroup := range recordsGroupMap { + if recordsGroup.Domain.DomainId != nil { + zoneNameIDMapper.Add(strconv.FormatUint(*recordsGroup.Domain.DomainId, 10), *recordsGroup.Domain.Name) + } + } + + // Apply Change Delete + deleteEndpoints := make(map[string][]uint64) + for _, change := range [][]*endpoint.Endpoint{changes.Delete, changes.UpdateOld} { + for _, deleteChange := range change { + if zoneId, _ := zoneNameIDMapper.FindZone(deleteChange.DNSName); zoneId != "" { + zoneIdString, _ := strconv.ParseUint(zoneId, 10, 64) + recordListGroup := recordsGroupMap[zoneIdString] + for _, domainRecord := range recordListGroup.RecordList { + subDomain := getSubDomain(*recordListGroup.Domain.Name, deleteChange) + if *domainRecord.Name == subDomain && *domainRecord.Type == deleteChange.RecordType { + for _, target := range deleteChange.Targets { + if *domainRecord.Value == target { + if _, exist := deleteEndpoints[*recordListGroup.Domain.Name]; !exist { + deleteEndpoints[*recordListGroup.Domain.Name] = make([]uint64, 0) + } + deleteEndpoints[*recordListGroup.Domain.Name] = append(deleteEndpoints[*recordListGroup.Domain.Name], *domainRecord.RecordId) + } + } + } + } + } + } + } + + if err := p.deleteRecords(deleteEndpoints); err != nil { + return err + } + + // Apply Change Create + createEndpoints := make(map[string][]*endpoint.Endpoint) + for zoneId := range zoneNameIDMapper { + createEndpoints[zoneId] = make([]*endpoint.Endpoint, 0) + } + for _, change := range [][]*endpoint.Endpoint{changes.Create, changes.UpdateNew} { + for _, createChange := range change { + if zoneId, _ := zoneNameIDMapper.FindZone(createChange.DNSName); zoneId != "" { + createEndpoints[zoneId] = append(createEndpoints[zoneId], createChange) + } + } + } + if err := p.createRecord(recordsGroupMap, createEndpoints); err != nil { + return err + } + return nil +} + +func (p *TencentCloudProvider) createRecord(zoneMap map[uint64]*RecordListGroup, endpointsMap map[string][]*endpoint.Endpoint) error { + for zoneId, endpoints := range endpointsMap { + zoneIdString, _ := strconv.ParseUint(zoneId, 10, 64) + domain := zoneMap[zoneIdString] + for _, endpoint := range endpoints { + for _, target := range endpoint.Targets { + if endpoint.RecordType == "TXT" && strings.HasPrefix(target, `"heritage=`) { + target = strings.Trim(target, `"`) + } + if err := p.createRecords(domain.Domain, endpoint, target); err != nil { + return err + } + } + } + } + return nil +} + +func (p *TencentCloudProvider) createRecords(domain *dnspod.DomainListItem, endpoint *endpoint.Endpoint, target string) error { + request := dnspod.NewCreateRecordRequest() + + request.Domain = common.StringPtr(*domain.Name) + request.RecordType = common.StringPtr(endpoint.RecordType) + request.Value = common.StringPtr(target) + request.SubDomain = common.StringPtr(getSubDomain(*domain.Name, endpoint)) + if endpoint.RecordTTL.IsConfigured() { + request.TTL = common.Uint64Ptr(uint64(endpoint.RecordTTL)) + } + request.RecordLine = common.StringPtr("默认") + + if _, err := p.apiService.CreateRecord(request); err != nil { + return err + } + return nil +} + +func (p *TencentCloudProvider) deleteRecords(RecordIdsMap map[string][]uint64) error { + for domain, recordIds := range RecordIdsMap { + if len(recordIds) == 0 { + continue + } + if err := p.deleteRecord(domain, recordIds); err != nil { + return err + } + } + return nil +} + +func (p *TencentCloudProvider) deleteRecord(domain string, recordIds []uint64) error { + request := dnspod.NewDeleteRecordRequest() + request.Domain = common.StringPtr(domain) + + for _, recordId := range recordIds { + request.RecordId = common.Uint64Ptr(recordId) + if _, err := p.apiService.DeleteRecord(request); err != nil { + return err + } + } + return nil +} + +func groupDomainRecordList(recordListGroup map[uint64]*RecordListGroup) (endpointMap map[string]*RecordListGroup) { + endpointMap = make(map[string]*RecordListGroup) + + for _, recordGroup := range recordListGroup { + for _, record := range recordGroup.RecordList { + key := fmt.Sprintf("%s:%s.%s", *record.Type, *record.Name, *recordGroup.Domain.Name) + if *record.Name == TencentCloudEmptyPrefix { + key = fmt.Sprintf("%s:%s", *record.Type, *recordGroup.Domain.Name) + } + if _, exist := endpointMap[key]; !exist { + endpointMap[key] = &RecordListGroup{ + Domain: recordGroup.Domain, + RecordList: make([]*dnspod.RecordListItem, 0), + } + } + endpointMap[key].RecordList = append(endpointMap[key].RecordList, record) + } + } + + return endpointMap +} diff --git a/provider/tencentcloud/privatedns.go b/provider/tencentcloud/privatedns.go new file mode 100644 index 0000000000..ad29418331 --- /dev/null +++ b/provider/tencentcloud/privatedns.go @@ -0,0 +1,319 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tencentcloud + +import ( + "fmt" + "strings" + + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" + + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/plan" + "sigs.k8s.io/external-dns/provider" +) + +// PrivateZone For Internal Dns + +func (p *TencentCloudProvider) privateZoneRecords() ([]*endpoint.Endpoint, error) { + privateZones, err := p.recordForPrivateZone() + if err != nil { + return nil, err + } + + endpoints := make([]*endpoint.Endpoint, 0) + recordMap := groupPrivateZoneRecords(privateZones) + for _, recordList := range recordMap { + name := getDnsDomain(*recordList.RecordList[0].SubDomain, *recordList.Zone.Domain) + recordType := *recordList.RecordList[0].RecordType + ttl := *recordList.RecordList[0].TTL + var targets []string + for _, record := range recordList.RecordList { + targets = append(targets, *record.RecordValue) + } + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...)) + } + return endpoints, nil +} + +func (p *TencentCloudProvider) recordForPrivateZone() (map[string]*PrivateZoneRecordListGroup, error) { + privateZones, err := p.getPrivateZones() + if err != nil { + return nil, err + } + + recordListGroup := make(map[string]*PrivateZoneRecordListGroup, 0) + for _, zone := range privateZones { + records, err := p.getPrivateZoneRecords(*zone.ZoneId) + if err != nil { + return nil, err + } + + for _, record := range records { + if *record.RecordType == "TXT" && strings.HasPrefix(*record.RecordValue, "heritage=") { + record.RecordValue = common.StringPtr(fmt.Sprintf("\"%s\"", *record.RecordValue)) + } + } + recordListGroup[*zone.ZoneId] = &PrivateZoneRecordListGroup{ + Zone: zone, + RecordList: records, + } + } + + return recordListGroup, nil +} + +func (p *TencentCloudProvider) getPrivateZones() ([]*privatedns.PrivateZone, error) { + filters := make([]*privatedns.Filter, 1) + filters[0] = &privatedns.Filter{ + Name: common.StringPtr("Vpc"), + Values: []*string{ + common.StringPtr(p.vpcID), + }, + } + + if p.zoneIDFilter.IsConfigured() { + zoneIDs := make([]*string, len(p.zoneIDFilter.ZoneIDs)) + for index, zoneId := range p.zoneIDFilter.ZoneIDs { + zoneIDs[index] = common.StringPtr(zoneId) + } + filters = append(filters, &privatedns.Filter{ + Name: common.StringPtr("ZoneId"), + Values: zoneIDs, + }) + } + + request := privatedns.NewDescribePrivateZoneListRequest() + request.Filters = filters + request.Offset = common.Int64Ptr(0) + request.Limit = common.Int64Ptr(100) + + privateZones := make([]*privatedns.PrivateZone, 0) + totalCount := int64(100) + for *request.Offset < totalCount { + response, err := p.apiService.DescribePrivateZoneList(request) + if err != nil { + return nil, err + } + if response.Response.PrivateZoneSet != nil && len(response.Response.PrivateZoneSet) > 0 { + privateZones = append(privateZones, response.Response.PrivateZoneSet...) + } + totalCount = *response.Response.TotalCount + request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.PrivateZoneSet))) + } + + privateZonesFilter := make([]*privatedns.PrivateZone, 0) + for _, privateZone := range privateZones { + if p.domainFilter.IsConfigured() && !p.domainFilter.Match(*privateZone.Domain) { + continue + } + privateZonesFilter = append(privateZonesFilter, privateZone) + } + return privateZonesFilter, nil +} + +func (p *TencentCloudProvider) getPrivateZoneRecords(zoneId string) ([]*privatedns.PrivateZoneRecord, error) { + request := privatedns.NewDescribePrivateZoneRecordListRequest() + request.ZoneId = common.StringPtr(zoneId) + request.Offset = common.Int64Ptr(0) + request.Limit = common.Int64Ptr(100) + + privateZoneRecords := make([]*privatedns.PrivateZoneRecord, 0) + totalCount := int64(100) + for *request.Offset < totalCount { + response, err := p.apiService.DescribePrivateZoneRecordList(request) + if err != nil { + return nil, err + } + if response.Response.RecordSet != nil && len(response.Response.RecordSet) > 0 { + privateZoneRecords = append(privateZoneRecords, response.Response.RecordSet...) + } + totalCount = *response.Response.TotalCount + request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.RecordSet))) + } + return privateZoneRecords, nil +} + +type PrivateZoneRecordListGroup struct { + Zone *privatedns.PrivateZone + RecordList []*privatedns.PrivateZoneRecord +} + +// Returns nil if the operation was successful or an error if the operation failed. +func (p *TencentCloudProvider) applyChangesForPrivateZone(changes *plan.Changes) error { + zoneGroups, err := p.recordForPrivateZone() + if err != nil { + return err + } + + // In PrivateDns Service. A Zone has at least one record. The last rule cannot be deleted. + for _, zoneGroup := range zoneGroups { + if !containsBaseRecord(zoneGroup.RecordList) { + err := p.createPrivateZoneRecord(zoneGroup.Zone, &endpoint.Endpoint{ + DNSName: *zoneGroup.Zone.Domain, + RecordType: "TXT", + }, "tencent_provider_record") + if err != nil { + return err + } + } + } + + zoneNameIDMapper := provider.ZoneIDName{} + for _, zoneGroup := range zoneGroups { + if zoneGroup.Zone.ZoneId != nil { + zoneNameIDMapper.Add(*zoneGroup.Zone.ZoneId, *zoneGroup.Zone.Domain) + } + } + + // Apply Change Delete + deleteEndpoints := make(map[string][]string) + for _, change := range [][]*endpoint.Endpoint{changes.Delete, changes.UpdateOld} { + for _, deleteChange := range change { + if zoneId, _ := zoneNameIDMapper.FindZone(deleteChange.DNSName); zoneId != "" { + zoneGroup := zoneGroups[zoneId] + for _, zoneRecord := range zoneGroup.RecordList { + subDomain := getSubDomain(*zoneGroup.Zone.Domain, deleteChange) + if *zoneRecord.SubDomain == subDomain && *zoneRecord.RecordType == deleteChange.RecordType { + for _, target := range deleteChange.Targets { + if *zoneRecord.RecordValue == target { + if _, exist := deleteEndpoints[zoneId]; !exist { + deleteEndpoints[zoneId] = make([]string, 0) + } + deleteEndpoints[zoneId] = append(deleteEndpoints[zoneId], *zoneRecord.RecordId) + } + } + } + } + } + } + } + + if err := p.deletePrivateZoneRecords(deleteEndpoints); err != nil { + return err + } + + // Apply Change Create + createEndpoints := make(map[string][]*endpoint.Endpoint) + for _, change := range [][]*endpoint.Endpoint{changes.Create, changes.UpdateNew} { + for _, createChange := range change { + if zoneId, _ := zoneNameIDMapper.FindZone(createChange.DNSName); zoneId != "" { + if _, exist := createEndpoints[zoneId]; !exist { + createEndpoints[zoneId] = make([]*endpoint.Endpoint, 0) + } + createEndpoints[zoneId] = append(createEndpoints[zoneId], createChange) + } + } + } + if err := p.createPrivateZoneRecords(zoneGroups, createEndpoints); err != nil { + return err + } + return nil +} + +func containsBaseRecord(records []*privatedns.PrivateZoneRecord) bool { + for _, record := range records { + if *record.SubDomain == TencentCloudEmptyPrefix && *record.RecordType == "TXT" && *record.RecordValue == "tencent_provider_record" { + return true + } + } + return false +} + +func (p *TencentCloudProvider) createPrivateZoneRecords(zoneGroups map[string]*PrivateZoneRecordListGroup, endpointsMap map[string][]*endpoint.Endpoint) error { + for zoneId, endpoints := range endpointsMap { + zoneGroup := zoneGroups[zoneId] + for _, endpoint := range endpoints { + for _, target := range endpoint.Targets { + if endpoint.RecordType == "TXT" && strings.HasPrefix(target, "\"heritage=") { + target = strings.Trim(target, "\"") + } + if err := p.createPrivateZoneRecord(zoneGroup.Zone, endpoint, target); err != nil { + return err + } + } + } + } + return nil +} + +func (p *TencentCloudProvider) deletePrivateZoneRecords(zoneRecordIdsMap map[string][]string) error { + for zoneId, zoneRecordIds := range zoneRecordIdsMap { + if len(zoneRecordIds) == 0 { + continue + } + if err := p.deletePrivateZoneRecord(zoneId, zoneRecordIds); err != nil { + return err + } + } + return nil +} + +func (p *TencentCloudProvider) createPrivateZoneRecord(zone *privatedns.PrivateZone, endpoint *endpoint.Endpoint, target string) error { + request := privatedns.NewCreatePrivateZoneRecordRequest() + request.ZoneId = common.StringPtr(*zone.ZoneId) + request.RecordType = common.StringPtr(endpoint.RecordType) + request.RecordValue = common.StringPtr(target) + request.SubDomain = common.StringPtr(getSubDomain(*zone.Domain, endpoint)) + if endpoint.RecordTTL.IsConfigured() { + request.TTL = common.Int64Ptr(int64(endpoint.RecordTTL)) + } + + if _, err := p.apiService.CreatePrivateZoneRecord(request); err != nil { + return err + } + return nil +} + +func (p *TencentCloudProvider) deletePrivateZoneRecord(zoneId string, zoneRecordIds []string) error { + recordIds := make([]*string, len(zoneRecordIds)) + for index, recordId := range zoneRecordIds { + recordIds[index] = common.StringPtr(recordId) + } + + request := privatedns.NewDeletePrivateZoneRecordRequest() + request.ZoneId = common.StringPtr(zoneId) + request.RecordIdSet = recordIds + + if _, err := p.apiService.DeletePrivateZoneRecord(request); err != nil { + return err + } + return nil +} + +func groupPrivateZoneRecords(zoneRecords map[string]*PrivateZoneRecordListGroup) (endpointMap map[string]*PrivateZoneRecordListGroup) { + endpointMap = make(map[string]*PrivateZoneRecordListGroup) + + for _, recordGroup := range zoneRecords { + for _, record := range recordGroup.RecordList { + key := fmt.Sprintf("%s:%s.%s", *record.RecordType, *record.SubDomain, *recordGroup.Zone.Domain) + if *record.SubDomain == TencentCloudEmptyPrefix { + key = fmt.Sprintf("%s:%s", *record.RecordType, *recordGroup.Zone.Domain) + } + if _, exist := endpointMap[key]; !exist { + endpointMap[key] = &PrivateZoneRecordListGroup{ + Zone: recordGroup.Zone, + RecordList: make([]*privatedns.PrivateZoneRecord, 0), + } + } + endpointMap[key].RecordList = append(endpointMap[key].RecordList, record) + } + } + + return endpointMap +} diff --git a/provider/tencentcloud/tencent_cloud.go b/provider/tencentcloud/tencent_cloud.go new file mode 100644 index 0000000000..e551af6634 --- /dev/null +++ b/provider/tencentcloud/tencent_cloud.go @@ -0,0 +1,122 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tencentcloud + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strings" + + log "github.com/sirupsen/logrus" + + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/plan" + "sigs.k8s.io/external-dns/provider" + "sigs.k8s.io/external-dns/provider/tencentcloud/cloudapi" +) + +const ( + TencentCloudEmptyPrefix = "@" + DefaultAPIRate = 9 +) + +func NewTencentCloudProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, configFile string, zoneType string, dryRun bool) (*TencentCloudProvider, error) { + cfg := tencentCloudConfig{} + if configFile != "" { + contents, err := os.ReadFile(configFile) + if err != nil { + return nil, fmt.Errorf("failed to read Tencent Cloud config file '%s': %w", configFile, err) + } + err = json.Unmarshal(contents, &cfg) + if err != nil { + return nil, fmt.Errorf("failed to parse Tencent Cloud config file '%s': %w", configFile, err) + } + } + + var apiService cloudapi.TencentAPIService = cloudapi.NewTencentAPIService(cfg.RegionId, DefaultAPIRate, cfg.SecretId, cfg.SecretKey, cfg.InternetEndpoint) + if dryRun { + apiService = cloudapi.NewReadOnlyAPIService(cfg.RegionId, DefaultAPIRate, cfg.SecretId, cfg.SecretKey, cfg.InternetEndpoint) + } + + tencentCloudProvider := &TencentCloudProvider{ + domainFilter: domainFilter, + zoneIDFilter: zoneIDFilter, + apiService: apiService, + vpcID: cfg.VPCId, + privateZone: zoneType == "private", + } + + return tencentCloudProvider, nil +} + +type TencentCloudProvider struct { + provider.BaseProvider + logger *log.Logger + apiService cloudapi.TencentAPIService + domainFilter endpoint.DomainFilter + zoneIDFilter provider.ZoneIDFilter // Private Zone only + vpcID string // Private Zone only + privateZone bool +} + +type tencentCloudConfig struct { + RegionId string `json:"regionId" yaml:"regionId"` + SecretId string `json:"secretId" yaml:"secretId"` + SecretKey string `json:"secretKey" yaml:"secretKey"` + VPCId string `json:"vpcId" yaml:"vpcId"` + InternetEndpoint bool `json:"internetEndpoint" yaml:"internetEndpoint"` +} + +func (p *TencentCloudProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { + if p.privateZone { + return p.privateZoneRecords() + } + return p.dnsRecords() +} + +func (p *TencentCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { + if !changes.HasChanges() { + return nil + } + + log.Infof("apply changes. %s", cloudapi.JsonWrapper(changes)) + + if p.privateZone { + return p.applyChangesForPrivateZone(changes) + } + return p.applyChangesForDNS(changes) +} + +func getSubDomain(domain string, endpoint *endpoint.Endpoint) string { + name := endpoint.DNSName + name = name[:len(name)-len(domain)] + name = strings.TrimSuffix(name, ".") + + if name == "" { + return TencentCloudEmptyPrefix + } + return name +} + +func getDnsDomain(subDomain string, domain string) string { + if subDomain == TencentCloudEmptyPrefix { + return domain + } + return subDomain + "." + domain +} diff --git a/provider/tencentcloud/tencent_cloud_test.go b/provider/tencentcloud/tencent_cloud_test.go new file mode 100644 index 0000000000..b9ee48579b --- /dev/null +++ b/provider/tencentcloud/tencent_cloud_test.go @@ -0,0 +1,405 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tencentcloud + +import ( + "context" + "testing" + + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/plan" + "sigs.k8s.io/external-dns/provider" + "sigs.k8s.io/external-dns/provider/tencentcloud/cloudapi" + + privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" +) + +func NewMockTencentCloudProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneType string) *TencentCloudProvider { + cfg := tencentCloudConfig{ + // SecretId: "", + // SecretKey: "", + RegionId: "ap-shanghai", + VPCId: "vpc-abcdefg", + } + + zoneId1 := common.StringPtr(cloudapi.RandStringRunes(8)) + + privateZones := []*privatedns.PrivateZone{ + { + ZoneId: zoneId1, + Domain: common.StringPtr("external-dns-test.com"), + VpcSet: []*privatedns.VpcInfo{ + { + UniqVpcId: common.StringPtr("vpc-abcdefg"), + Region: common.StringPtr("ap-shanghai"), + }, + }, + }, + } + + zoneRecordId1 := common.StringPtr(cloudapi.RandStringRunes(8)) + zoneRecordId2 := common.StringPtr(cloudapi.RandStringRunes(8)) + privateZoneRecords := map[string][]*privatedns.PrivateZoneRecord{ + *zoneId1: { + { + ZoneId: zoneId1, + RecordId: zoneRecordId1, + SubDomain: common.StringPtr("nginx"), + RecordType: common.StringPtr("TXT"), + RecordValue: common.StringPtr("heritage=external-dns,external-dns/owner=default"), + TTL: common.Int64Ptr(300), + }, + { + ZoneId: zoneId1, + RecordId: zoneRecordId2, + SubDomain: common.StringPtr("nginx"), + RecordType: common.StringPtr("A"), + RecordValue: common.StringPtr("10.10.10.10"), + TTL: common.Int64Ptr(300), + }, + }, + } + + dnsDomainId1 := common.Uint64Ptr(cloudapi.RandUint64()) + dnsPodDomains := []*dnspod.DomainListItem{ + { + DomainId: dnsDomainId1, + Name: common.StringPtr("external-dns-test.com"), + }, + } + dnsDomainRecordId1 := common.Uint64Ptr(cloudapi.RandUint64()) + dnsDomainRecordId2 := common.Uint64Ptr(cloudapi.RandUint64()) + dnspodRecords := map[string][]*dnspod.RecordListItem{ + "external-dns-test.com": { + { + RecordId: dnsDomainRecordId1, + Value: common.StringPtr("heritage=external-dns,external-dns/owner=default"), + Name: common.StringPtr("nginx"), + Type: common.StringPtr("TXT"), + TTL: common.Uint64Ptr(300), + }, + { + RecordId: dnsDomainRecordId2, + Name: common.StringPtr("nginx"), + Type: common.StringPtr("A"), + Value: common.StringPtr("10.10.10.10"), + TTL: common.Uint64Ptr(300), + }, + }, + } + + var apiService cloudapi.TencentAPIService = cloudapi.NewMockService(privateZones, privateZoneRecords, dnsPodDomains, dnspodRecords) + + tencentCloudProvider := &TencentCloudProvider{ + domainFilter: domainFilter, + zoneIDFilter: zoneIDFilter, + apiService: apiService, + vpcID: cfg.VPCId, + privateZone: zoneType == "private", + } + + return tencentCloudProvider +} + +func TestTencentPrivateProvider_Records(t *testing.T) { + p := NewMockTencentCloudProvider(endpoint.NewDomainFilter([]string{"external-dns-test.com"}), provider.NewZoneIDFilter([]string{}), "private") + endpoints, err := p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 2 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Create、UpdateOld、UpdateNew、Delete + // The base record will be created. + changes := &plan.Changes{ + Create: []*endpoint.Endpoint{ + { + DNSName: "redis.external-dns-test.com", + RecordType: "A", + RecordTTL: 300, + Targets: endpoint.NewTargets("4.3.2.1"), + }, + }, + UpdateOld: []*endpoint.Endpoint{ + { + DNSName: "nginx.external-dns-test.com", + RecordType: "A", + RecordTTL: 300, + Targets: endpoint.NewTargets("10.10.10.10"), + }, + }, + UpdateNew: []*endpoint.Endpoint{ + { + DNSName: "tencent.external-dns-test.com", + RecordType: "A", + RecordTTL: 600, + Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"), + }, + }, + Delete: []*endpoint.Endpoint{ + { + DNSName: "nginx.external-dns-test.com", + RecordType: "TXT", + RecordTTL: 300, + Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=default\""), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 3 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Delete one target + changes = &plan.Changes{ + Delete: []*endpoint.Endpoint{ + { + DNSName: "tencent.external-dns-test.com", + RecordType: "A", + RecordTTL: 600, + Targets: endpoint.NewTargets("5.6.7.8"), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 3 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Delete another target + changes = &plan.Changes{ + Create: []*endpoint.Endpoint{ + { + DNSName: "redis.external-dns-test.com", + RecordType: "A", + RecordTTL: 300, + Targets: endpoint.NewTargets("5.6.7.8"), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 3 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Delete another target + changes = &plan.Changes{ + Delete: []*endpoint.Endpoint{ + { + DNSName: "tencent.external-dns-test.com", + RecordType: "A", + RecordTTL: 600, + Targets: endpoint.NewTargets("1.2.3.4"), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 2 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } +} + +func TestTencentPublicProvider_Records(t *testing.T) { + p := NewMockTencentCloudProvider(endpoint.NewDomainFilter([]string{"external-dns-test.com"}), provider.NewZoneIDFilter([]string{}), "public") + endpoints, err := p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 2 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Create、UpdateOld、UpdateNew、Delete + changes := &plan.Changes{ + Create: []*endpoint.Endpoint{ + { + DNSName: "redis.external-dns-test.com", + RecordType: "A", + RecordTTL: 300, + Targets: endpoint.NewTargets("4.3.2.1"), + }, + }, + UpdateOld: []*endpoint.Endpoint{ + { + DNSName: "nginx.external-dns-test.com", + RecordType: "A", + RecordTTL: 300, + Targets: endpoint.NewTargets("10.10.10.10"), + }, + }, + UpdateNew: []*endpoint.Endpoint{ + { + DNSName: "tencent.external-dns-test.com", + RecordType: "A", + RecordTTL: 600, + Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"), + }, + }, + Delete: []*endpoint.Endpoint{ + { + DNSName: "nginx.external-dns-test.com", + RecordType: "TXT", + RecordTTL: 300, + Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=default\""), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 2 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Delete one target + changes = &plan.Changes{ + Delete: []*endpoint.Endpoint{ + { + DNSName: "tencent.external-dns-test.com", + RecordType: "A", + RecordTTL: 600, + Targets: endpoint.NewTargets("5.6.7.8"), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 2 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Delete another target + changes = &plan.Changes{ + Create: []*endpoint.Endpoint{ + { + DNSName: "redis.external-dns-test.com", + RecordType: "A", + RecordTTL: 300, + Targets: endpoint.NewTargets("5.6.7.8"), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 2 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } + + // Test for Delete another target + changes = &plan.Changes{ + Delete: []*endpoint.Endpoint{ + { + DNSName: "tencent.external-dns-test.com", + RecordType: "A", + RecordTTL: 600, + Targets: endpoint.NewTargets("1.2.3.4"), + }, + }, + } + if err := p.ApplyChanges(context.Background(), changes); err != nil { + t.Errorf("Failed to get records: %v", err) + } + endpoints, err = p.Records(context.Background()) + if err != nil { + t.Errorf("Failed to get records: %v", err) + } else { + if len(endpoints) != 1 { + t.Errorf("Incorrect number of records: %d", len(endpoints)) + } + for _, endpoint := range endpoints { + t.Logf("Endpoint for %+v", *endpoint) + } + } +} diff --git a/provider/ultradns/ultradns.go b/provider/ultradns/ultradns.go index d18c636ffe..6208f7a695 100644 --- a/provider/ultradns/ultradns.go +++ b/provider/ultradns/ultradns.go @@ -41,11 +41,14 @@ const ( // global variables var sbPoolRunProbes = true -var sbPoolActOnProbes = true -var ultradnsPoolType = "rdpool" -var accountName string -//Setting custom headers for ultradns api calls +var ( + sbPoolActOnProbes = true + ultradnsPoolType = "rdpool" + accountName string +) + +// Setting custom headers for ultradns api calls var customHeader = []udnssdk.CustomHeader{ { Key: "UltraClient", @@ -145,7 +148,6 @@ func (p *UltraDNSProvider) Zones(ctx context.Context) ([]udnssdk.Zone, error) { zoneKey.Zone = zone zoneKey.AccountName = accountName zones, err := p.fetchZones(ctx, zoneKey) - if err != nil { return nil, err } @@ -489,7 +491,7 @@ func (p *UltraDNSProvider) newSBPoolObjectCreation(ctx context.Context, change * return sbPoolObject, nil } -//Creation of RDPoolObject +// Creation of RDPoolObject func (p *UltraDNSProvider) newRDPoolObjectCreation(ctx context.Context, change *UltraDNSChanges) (rdPool udnssdk.RDPoolProfile, err error) { rdPoolObject := udnssdk.RDPoolProfile{ Context: udnssdk.RDPoolSchema, diff --git a/provider/ultradns/ultradns_test.go b/provider/ultradns/ultradns_test.go index 587d5f4c6b..2d027ccccf 100644 --- a/provider/ultradns/ultradns_test.go +++ b/provider/ultradns/ultradns_test.go @@ -76,7 +76,6 @@ func (m *mockUltraDNSRecord) Select(k udnssdk.RRSetKey) ([]udnssdk.RRSet, error) RData: []string{"1.1.1.1"}, TTL: 86400, }}, nil - } func (m *mockUltraDNSRecord) SelectWithOffset(k udnssdk.RRSetKey, offset int) ([]udnssdk.RRSet, udnssdk.ResultInfo, *http.Response, error) { @@ -117,7 +116,7 @@ func TestNewUltraDNSProvider(t *testing.T) { assert.NotNilf(t, err, "Expected to fail %s", "formatted") } -//zones function test scenario +// zones function test scenario func TestUltraDNSProvider_Zones(t *testing.T) { mocked := mockUltraDNSZone{} provider := &UltraDNSProvider{ @@ -136,10 +135,9 @@ func TestUltraDNSProvider_Zones(t *testing.T) { zones, err := provider.Zones(context.Background()) assert.Nil(t, err) assert.Equal(t, reflect.DeepEqual(expected, zones), true) - } -//Records function test case +// Records function test case func TestUltraDNSProvider_Records(t *testing.T) { mocked := mockUltraDNSRecord{} mockedDomain := mockUltraDNSZone{} @@ -159,10 +157,9 @@ func TestUltraDNSProvider_Records(t *testing.T) { assert.Equal(t, v.RecordType, expected[0].RRType) assert.Equal(t, int(v.RecordTTL), expected[0].TTL) } - } -//ApplyChanges function testcase +// ApplyChanges function testcase func TestUltraDNSProvider_ApplyChanges(t *testing.T) { changes := &plan.Changes{} mocked := mockUltraDNSRecord{nil} @@ -209,7 +206,7 @@ func TestUltraDNSProvider_getSpecificRecord(t *testing.T) { assert.Nil(t, err) } -//Fail case scenario testing where CNAME and TXT Record name are same +// Fail case scenario testing where CNAME and TXT Record name are same func TestUltraDNSProvider_ApplyChangesCNAME(t *testing.T) { changes := &plan.Changes{} mocked := mockUltraDNSRecord{nil} @@ -233,11 +230,9 @@ func TestUltraDNSProvider_ApplyChangesCNAME(t *testing.T) { // This will work if you would set the environment variables such as "ULTRADNS_INTEGRATION" and zone should be available "kubernetes-ultradns-provider-test.com" func TestUltraDNSProvider_ApplyChanges_Integration(t *testing.T) { - _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { providerUltradns, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) @@ -271,7 +266,8 @@ func TestUltraDNSProvider_ApplyChanges_Integration(t *testing.T) { changes = &plan.Changes{} changes.UpdateNew = []*endpoint.Endpoint{ {DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}, - {DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100}} + {DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100}, + } err = providerUltradns.ApplyChanges(context.Background(), changes) assert.Nil(t, err) @@ -296,7 +292,8 @@ func TestUltraDNSProvider_ApplyChanges_Integration(t *testing.T) { changes = &plan.Changes{} changes.Delete = []*endpoint.Endpoint{ {DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"2001:0db8:85a3:0000:0000:8a2e:0370:7335"}, RecordType: "AAAA", RecordTTL: 100}, - {DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}} + {DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.2.2"}, RecordType: "A", RecordTTL: 100}, + } err = providerUltradns.ApplyChanges(context.Background(), changes) assert.Nil(t, err) @@ -308,7 +305,6 @@ func TestUltraDNSProvider_ApplyChanges_Integration(t *testing.T) { assert.Equal(t, resp.Status, "404 Not Found") } - } // This will work if you would set the environment variables such as "ULTRADNS_INTEGRATION" and zone should be available "kubernetes-ultradns-provider-test.com" for multiple target @@ -316,13 +312,13 @@ func TestUltraDNSProvider_ApplyChanges_MultipleTarget_integeration(t *testing.T) _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { provider, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) changes := &plan.Changes{} changes.Create = []*endpoint.Endpoint{ - {DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.1.2.2"}, RecordType: "A"}} + {DNSName: "kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.1.2.2"}, RecordType: "A"}, + } err = provider.ApplyChanges(context.Background(), changes) assert.Nil(t, err) @@ -404,7 +400,7 @@ func TestUltraDNSProvider_newSBPoolObjectCreation(t *testing.T) { }, } - for _, _ = range changesList.ResourceRecordSetUltraDNS.RData { + for range changesList.ResourceRecordSetUltraDNS.RData { rrdataInfo := udnssdk.SBRDataInfo{ RunProbes: true, @@ -428,15 +424,13 @@ func TestUltraDNSProvider_newSBPoolObjectCreation(t *testing.T) { actualSBPoolObject, _ := provider.newSBPoolObjectCreation(context.Background(), changesList) assert.Equal(t, sbPoolObject, actualSBPoolObject) - } -//Testcase to check fail scenario for multiple AAAA targets +// Testcase to check fail scenario for multiple AAAA targets func TestUltraDNSProvider_MultipleTargetAAAA(t *testing.T) { _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { _ = os.Setenv("ULTRADNS_POOL_TYPE", "sbpool") @@ -454,12 +448,11 @@ func TestUltraDNSProvider_MultipleTargetAAAA(t *testing.T) { } } -//Testcase to check fail scenario for multiple AAAA targets +// Testcase to check fail scenario for multiple AAAA targets func TestUltraDNSProvider_MultipleTargetAAAARDPool(t *testing.T) { _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { _ = os.Setenv("ULTRADNS_POOL_TYPE", "rdpool") provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) @@ -491,7 +484,6 @@ func TestUltraDNSProvider_MultipleTargetCNAME(t *testing.T) { _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { provider, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) changes := &plan.Changes{} @@ -508,7 +500,7 @@ func TestUltraDNSProvider_MultipleTargetCNAME(t *testing.T) { } } -//Testing creation of RD Pool +// Testing creation of RD Pool func TestUltraDNSProvider_newRDPoolObjectCreation(t *testing.T) { mocked := mockUltraDNSRecord{nil} mockedDomain := mockUltraDNSZone{nil} @@ -538,10 +530,9 @@ func TestUltraDNSProvider_newRDPoolObjectCreation(t *testing.T) { actualRDPoolObject, _ := provider.newRDPoolObjectCreation(context.Background(), changesList) assert.Equal(t, rdPoolObject, actualRDPoolObject) - } -//Testing Failure scenarios over NewUltraDNS Provider +// Testing Failure scenarios over NewUltraDNS Provider func TestNewUltraDNSProvider_FailCases(t *testing.T) { _ = os.Setenv("ULTRADNS_USERNAME", "") _ = os.Setenv("ULTRADNS_PASSWORD", "") @@ -590,7 +581,6 @@ func TestNewUltraDNSProvider_FailCases(t *testing.T) { _ = os.Unsetenv("ULTRADNS_POOL_TYPE") _, accounterr := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true) assert.Nil(t, accounterr) - } // Testing success scenarios for newly introduced environment variables @@ -618,12 +608,10 @@ func TestNewUltraDNSProvider_NewEnvVariableSuccessCases(t *testing.T) { _ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "true") _, err2 := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true) assert.Nilf(t, err2, "ActOnProbe given value is not working %s", "formatted") - } // Base64 Bad string decoding scenario func TestNewUltraDNSProvider_Base64DecodeFailcase(t *testing.T) { - _ = os.Setenv("ULTRADNS_USERNAME", "") _ = os.Setenv("ULTRADNS_PASSWORD", "12345") _ = os.Setenv("ULTRADNS_BASEURL", "") @@ -631,17 +619,14 @@ func TestNewUltraDNSProvider_Base64DecodeFailcase(t *testing.T) { _ = os.Setenv("ULTRADNS_ENABLE_ACTONPROBE", "true") _, err := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"test-ultradns-provider.com"}), true) assert.NotNilf(t, err, "Base64 decode should fail in this case %s", "formatted") - } func TestUltraDNSProvider_PoolConversionCase(t *testing.T) { - _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { - //Creating SBPool Record + // Creating SBPool Record _ = os.Setenv("ULTRADNS_POOL_TYPE", "sbpool") provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) changes := &plan.Changes{} @@ -652,7 +637,7 @@ func TestUltraDNSProvider_PoolConversionCase(t *testing.T) { resp, _ := provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{}) assert.Equal(t, resp.Status, "200 OK") - //Converting to RD Pool + // Converting to RD Pool _ = os.Setenv("ULTRADNS_POOL_TYPE", "rdpool") provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) changes = &plan.Changes{} @@ -662,7 +647,7 @@ func TestUltraDNSProvider_PoolConversionCase(t *testing.T) { resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{}) assert.Equal(t, resp.Status, "200 OK") - //Converting back to SB Pool + // Converting back to SB Pool _ = os.Setenv("ULTRADNS_POOL_TYPE", "sbpool") provider, _ = NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com"}), false) changes = &plan.Changes{} @@ -672,7 +657,7 @@ func TestUltraDNSProvider_PoolConversionCase(t *testing.T) { resp, _ = provider.client.Do("GET", "zones/kubernetes-ultradns-provider-test.com./rrsets/A/ttl.kubernetes-ultradns-provider-test.com.", nil, udnssdk.RRSetListDTO{}) assert.Equal(t, resp.Status, "200 OK") - //Deleting Record + // Deleting Record changes = &plan.Changes{} changes.Delete = []*endpoint.Endpoint{{DNSName: "ttl.kubernetes-ultradns-provider-test.com", Targets: endpoint.Targets{"1.1.1.1", "1.2.3.4"}, RecordType: "A"}} err = provider.ApplyChanges(context.Background(), changes) @@ -683,11 +668,9 @@ func TestUltraDNSProvider_PoolConversionCase(t *testing.T) { } func TestUltraDNSProvider_DomainFilter(t *testing.T) { - _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com", "kubernetes-ultradns-provider-test.com"}), true) zones, err := provider.Zones(context.Background()) @@ -703,11 +686,9 @@ func TestUltraDNSProvider_DomainFilter(t *testing.T) { } func TestUltraDNSProvider_DomainFiltersZonesFailCase(t *testing.T) { - _, ok := os.LookupEnv("ULTRADNS_INTEGRATION") if !ok { log.Printf("Skipping test") - } else { provider, _ := NewUltraDNSProvider(endpoint.NewDomainFilter([]string{"kubernetes-ultradns-provider-test.com", "kubernetes-uldsvdsvadvvdsvadvstradns-provider-test.com"}), true) _, err := provider.Zones(context.Background()) @@ -715,7 +696,7 @@ func TestUltraDNSProvider_DomainFiltersZonesFailCase(t *testing.T) { } } -//zones function with domain filter test scenario +// zones function with domain filter test scenario func TestUltraDNSProvider_DomainFilterZonesMocked(t *testing.T) { mocked := mockUltraDNSZone{} provider := &UltraDNSProvider{ @@ -756,7 +737,7 @@ func TestUltraDNSProvider_DomainFilterZonesMocked(t *testing.T) { assert.Nil(t, err) assert.Equal(t, reflect.DeepEqual(expected, zones), true) - //When zone is not given but account is provided + // When zone is not given but account is provided provider = &UltraDNSProvider{ client: udnssdk.Client{ Zone: &mocked, @@ -772,5 +753,4 @@ func TestUltraDNSProvider_DomainFilterZonesMocked(t *testing.T) { zones, err = provider.Zones(context.Background()) assert.Nil(t, err) assert.Equal(t, reflect.DeepEqual(expected, zones), true) - } diff --git a/provider/vinyldns/vinyldns.go b/provider/vinyldns/vinyldns.go index e0cbaef90f..7dc98c2d73 100644 --- a/provider/vinyldns/vinyldns.go +++ b/provider/vinyldns/vinyldns.go @@ -103,7 +103,7 @@ func (p *vinyldnsProvider) Records(ctx context.Context) (endpoints []*endpoint.E recordsCount := len(r.Records) log.Debugf(fmt.Sprintf("%s.%s.%d.%s", r.Name, r.Type, recordsCount, zone.Name)) - //TODO: AAAA Records + // TODO: AAAA Records if len(r.Records) > 0 { targets := make([]string, len(r.Records)) for idx, rr := range r.Records { @@ -231,7 +231,7 @@ func newVinylDNSChanges(action string, endpoints []*endpoint.Endpoint) []*vinyld } func newVinylDNSChange(action string, endpoint *endpoint.Endpoint) *vinyldnsChange { - var ttl = vinyldnsRecordTTL + ttl := vinyldnsRecordTTL if endpoint.RecordTTL.IsConfigured() { ttl = int(endpoint.RecordTTL) } diff --git a/provider/vinyldns/vinyldns_test.go b/provider/vinyldns/vinyldns_test.go index 5388425f24..1afd831b8b 100644 --- a/provider/vinyldns/vinyldns_test.go +++ b/provider/vinyldns/vinyldns_test.go @@ -37,9 +37,11 @@ type mockVinyldnsZoneInterface struct { var mockVinylDNSProvider vinyldnsProvider -var vinylDNSZones []vinyldns.Zone -var vinylDNSRecords []vinyldns.RecordSet -var vinylDNSRecordSetUpdateResponse *vinyldns.RecordSetUpdateResponse +var ( + vinylDNSZones []vinyldns.Zone + vinylDNSRecords []vinyldns.RecordSet + vinylDNSRecordSetUpdateResponse *vinyldns.RecordSetUpdateResponse +) func TestVinylDNSServices(t *testing.T) { firstZone := vinyldns.Zone{ diff --git a/provider/vultr/vultr_test.go b/provider/vultr/vultr_test.go index 38bf05ba7e..e3beb44ea8 100644 --- a/provider/vultr/vultr_test.go +++ b/provider/vultr/vultr_test.go @@ -72,7 +72,6 @@ func (m mockVultrDomain) GetDNSSec(ctx context.Context, domain string) ([]string return nil, nil } - type mockVultrRecord struct { client *govultr.Client } diff --git a/provider/zone_id_filter.go b/provider/zone_id_filter.go index e15581f27e..50a016b2d8 100644 --- a/provider/zone_id_filter.go +++ b/provider/zone_id_filter.go @@ -43,3 +43,11 @@ func (f ZoneIDFilter) Match(zoneID string) bool { return false } + +// IsConfigured returns true if DomainFilter is configured, false otherwise +func (f ZoneIDFilter) IsConfigured() bool { + if len(f.ZoneIDs) == 1 { + return f.ZoneIDs[0] != "" + } + return len(f.ZoneIDs) > 0 +} diff --git a/provider/zone_type_filter_test.go b/provider/zone_type_filter_test.go index 129fef036f..8677e6fb03 100644 --- a/provider/zone_type_filter_test.go +++ b/provider/zone_type_filter_test.go @@ -37,22 +37,22 @@ func TestZoneTypeFilterMatch(t *testing.T) { zones []interface{} }{ { - "", true, []interface{}{ publicZoneStr, privateZoneStr, &route53.HostedZone{} }, + "", true, []interface{}{publicZoneStr, privateZoneStr, &route53.HostedZone{}}, }, { - "public", true, []interface{}{ publicZoneStr, publicZoneAWS, &route53.HostedZone{} }, + "public", true, []interface{}{publicZoneStr, publicZoneAWS, &route53.HostedZone{}}, }, { - "public", false, []interface{}{ privateZoneStr, privateZoneAWS }, + "public", false, []interface{}{privateZoneStr, privateZoneAWS}, }, { - "private", true, []interface{}{ privateZoneStr, privateZoneAWS }, + "private", true, []interface{}{privateZoneStr, privateZoneAWS}, }, { - "private", false, []interface{}{ publicZoneStr, publicZoneAWS, &route53.HostedZone{} }, + "private", false, []interface{}{publicZoneStr, publicZoneAWS, &route53.HostedZone{}}, }, { - "unknown", false, []interface{}{ publicZoneStr }, + "unknown", false, []interface{}{publicZoneStr}, }, } { zoneTypeFilter := NewZoneTypeFilter(tc.zoneTypeFilter) diff --git a/registry/noop_test.go b/registry/noop_test.go index 70d7dac580..8d644ece62 100644 --- a/registry/noop_test.go +++ b/registry/noop_test.go @@ -108,7 +108,7 @@ func testNoopApplyChanges(t *testing.T) { }) assert.EqualError(t, err, inmemory.ErrRecordAlreadyExists.Error()) - //correct changes + // correct changes require.NoError(t, r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ { diff --git a/registry/registry.go b/registry/registry.go index 59b2991403..7f219a8463 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -38,7 +38,7 @@ type Registry interface { MissingRecords() []*endpoint.Endpoint } -//TODO(ideahitme): consider moving this to Plan +// TODO(ideahitme): consider moving this to Plan func filterOwnedRecords(ownerID string, eps []*endpoint.Endpoint) []*endpoint.Endpoint { filtered := []*endpoint.Endpoint{} for _, ep := range eps { diff --git a/registry/txt.go b/registry/txt.go index 2e0c33a3c2..b68447ec4a 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -35,7 +35,7 @@ const recordTemplate = "%{record_type}" // TXTRegistry implements registry interface with ownership implemented via associated TXT records type TXTRegistry struct { provider provider.Provider - ownerID string //refers to the owner id of the current instance + ownerID string // refers to the owner id of the current instance mapper nameMapper // cache the records in memory and update on an interval instead. @@ -114,9 +114,9 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error // We simply assume that TXT records for the registry will always have only one target. labels, err := endpoint.NewLabelsFromString(record.Targets[0]) if err == endpoint.ErrInvalidHeritage { - //if no heritage is found or it is invalid - //case when value of txt record cannot be identified - //record will not be removed as it will have empty owner + // if no heritage is found or it is invalid + // case when value of txt record cannot be identified + // record will not be removed as it will have empty owner endpoints = append(endpoints, record) continue } @@ -195,8 +195,13 @@ func (im *TXTRegistry) generateTXTRecord(r *endpoint.Endpoint) []*endpoint.Endpo txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)).WithSetIdentifier(r.SetIdentifier) txt.ProviderSpecific = r.ProviderSpecific // new TXT record format (containing record type) - txtNew := endpoint.NewEndpoint(im.mapper.toNewTXTName(r.DNSName, r.RecordType), endpoint.RecordTypeTXT, r.Labels.Serialize(true)).WithSetIdentifier(r.SetIdentifier) - txtNew.ProviderSpecific = r.ProviderSpecific + txtNew := endpoint.NewEndpoint(im.mapper.toNewTXTName(r.DNSName, r.RecordType), endpoint.RecordTypeTXT, r.Labels.Serialize(true)) + if txtNew != nil { + txtNew.WithSetIdentifier(r.SetIdentifier) + txtNew.ProviderSpecific = r.ProviderSpecific + } else { + return []*endpoint.Endpoint{txt} + } return []*endpoint.Endpoint{txt, txtNew} } @@ -343,6 +348,7 @@ func (pr affixNameMapper) dropAffixTemplate(name string) string { func (pr affixNameMapper) isPrefix() bool { return len(pr.suffix) == 0 } + func (pr affixNameMapper) isSuffix() bool { return len(pr.prefix) == 0 && len(pr.suffix) > 0 } @@ -395,6 +401,7 @@ func (pr affixNameMapper) normalizeAffixTemplate(afix, recordType string) string } return afix } + func (pr affixNameMapper) toNewTXTName(endpointDNSName, recordType string) string { DNSName := strings.SplitN(endpointDNSName, ".", 2) recordType = strings.ToLower(recordType) diff --git a/registry/txt_test.go b/registry/txt_test.go index 27d7d66a5e..932d1ab2d9 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -536,6 +536,7 @@ func testTXTRegistryApplyChangesWithTemplatedPrefix(t *testing.T) { err := r.ApplyChanges(ctx, changes) require.NoError(t, err) } + func testTXTRegistryApplyChangesWithTemplatedSuffix(t *testing.T) { p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) @@ -579,6 +580,7 @@ func testTXTRegistryApplyChangesWithTemplatedSuffix(t *testing.T) { err := r.ApplyChanges(ctx, changes) require.NoError(t, err) } + func testTXTRegistryApplyChangesWithSuffix(t *testing.T) { p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) @@ -790,6 +792,8 @@ func testTXTRegistryMissingRecordsNoPrefix(t *testing.T) { newEndpointWithOwner("oldformat-otherowner.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=otherowner\"", endpoint.RecordTypeTXT, ""), endpoint.NewEndpoint("unmanaged1.test-zone.example.org", endpoint.RecordTypeA, "unmanaged1.loadbalancer.com"), endpoint.NewEndpoint("unmanaged2.test-zone.example.org", endpoint.RecordTypeCNAME, "unmanaged2.loadbalancer.com"), + newEndpointWithOwner("this-is-a-63-characters-long-label-that-we-do-expect-will-work.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), + newEndpointWithOwner("this-is-a-63-characters-long-label-that-we-do-expect-will-work.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) expectedRecords := []*endpoint.Endpoint{ @@ -847,6 +851,14 @@ func testTXTRegistryMissingRecordsNoPrefix(t *testing.T) { Targets: endpoint.Targets{"unmanaged2.loadbalancer.com"}, RecordType: endpoint.RecordTypeCNAME, }, + { + DNSName: "this-is-a-63-characters-long-label-that-we-do-expect-will-work.test-zone.example.org", + Targets: endpoint.Targets{"foo.loadbalancer.com"}, + RecordType: endpoint.RecordTypeCNAME, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner", + }, + }, } expectedMissingRecords := []*endpoint.Endpoint{ diff --git a/source/ambassador_host.go b/source/ambassador_host.go index a2214dcd3a..4d8659c29c 100644 --- a/source/ambassador_host.go +++ b/source/ambassador_host.go @@ -66,7 +66,8 @@ func NewAmbassadorHostSource( ctx context.Context, dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, - namespace string) (Source, error) { + namespace string, +) (Source, error) { var err error // Use shared informer to listen for add/update/delete of Host in the specified namespace. @@ -134,12 +135,14 @@ func (sc *ambassadorHostSource) Endpoints(ctx context.Context) ([]*endpoint.Endp targets, err := sc.targetsFromAmbassadorLoadBalancer(ctx, service) if err != nil { - return nil, err + log.Warningf("Could not find targets for service %s for Host %s: %v", service, fullname, err) + continue } hostEndpoints, err := sc.endpointsFromHost(ctx, host, targets) if err != nil { - return nil, err + log.Warningf("Could not get endpoints for Host %s", err) + continue } if len(hostEndpoints) == 0 { log.Debugf("No endpoints could be generated from Host %s", fullname) diff --git a/source/ambassador_host_test.go b/source/ambassador_host_test.go index bb960600e0..9702eb1196 100644 --- a/source/ambassador_host_test.go +++ b/source/ambassador_host_test.go @@ -19,8 +19,15 @@ package source import ( "testing" + ambassador "github.com/datawire/ambassador/pkg/api/getambassador.io/v2" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "golang.org/x/net/context" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + fakeDynamic "k8s.io/client-go/dynamic/fake" + fakeKube "k8s.io/client-go/kubernetes/fake" ) type AmbassadorSuite struct { @@ -37,6 +44,63 @@ func testAmbassadorSourceImplementsSource(t *testing.T) { require.Implements(t, (*Source)(nil), new(ambassadorHostSource)) } +func TestAmbassadorHostSource(t *testing.T) { + fakeKubernetesClient := fakeKube.NewSimpleClientset() + + ambassadorScheme := runtime.NewScheme() + + ambassador.AddToScheme(ambassadorScheme) + + fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(ambassadorScheme) + + ctx := context.Background() + + namespace := "test" + + host, err := createAmbassadorHost("test-host", "test-service") + if err != nil { + t.Fatalf("could not create host resource: %v", err) + } + + { + _, err := fakeDynamicClient.Resource(ambHostGVR).Namespace(namespace).Create(ctx, host, v1.CreateOptions{}) + if err != nil { + t.Fatalf("could not create host: %v", err) + } + } + + ambassadorSource, err := NewAmbassadorHostSource(ctx, fakeDynamicClient, fakeKubernetesClient, namespace) + if err != nil { + t.Fatalf("could not create ambassador source: %v", err) + } + + { + _, err := ambassadorSource.Endpoints(ctx) + if err != nil { + t.Fatalf("could not collect ambassador source endpoints: %v", err) + } + } +} + +func createAmbassadorHost(name, ambassadorService string) (*unstructured.Unstructured, error) { + host := &ambassador.Host{ + ObjectMeta: v1.ObjectMeta{ + Name: name, + Annotations: map[string]string{ + ambHostAnnotation: ambassadorService, + }, + }, + } + obj := &unstructured.Unstructured{} + uc, _ := newUnstructuredConverter() + err := uc.scheme.Convert(host, obj, nil) + if err != nil { + return nil, err + } + + return obj, nil +} + // TestParseAmbLoadBalancerService tests our parsing of Ambassador service info. func TestParseAmbLoadBalancerService(t *testing.T) { vectors := []struct { diff --git a/source/connector_test.go b/source/connector_test.go index 3feba38008..9177d7b6bd 100644 --- a/source/connector_test.go +++ b/source/connector_test.go @@ -33,7 +33,6 @@ type ConnectorSuite struct { } func (suite *ConnectorSuite) SetupTest() { - } func startServerToServeTargets(t *testing.T, endpoints []*endpoint.Endpoint) net.Listener { @@ -91,7 +90,8 @@ func testConnectorSourceEndpoints(t *testing.T) { title: "valid remote server", server: true, expected: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -103,12 +103,14 @@ func testConnectorSourceEndpoints(t *testing.T) { title: "valid remote server with multiple endpoints", server: true, expected: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, }, - {DNSName: "xyz.example.org", + { + DNSName: "xyz.example.org", Targets: endpoint.Targets{"abc.example.org"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 180, diff --git a/source/crd_test.go b/source/crd_test.go index a88fb46165..fe9d4e6606 100644 --- a/source/crd_test.go +++ b/source/crd_test.go @@ -45,7 +45,6 @@ type CRDSuite struct { } func (suite *CRDSuite) SetupTest() { - } func defaultHeader() http.Header { @@ -152,7 +151,8 @@ func testCRDSourceEndpoints(t *testing.T) { registeredKind: "DNSEndpoint", kind: "DNSEndpoint", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -168,7 +168,8 @@ func testCRDSourceEndpoints(t *testing.T) { registeredKind: "DNSEndpoint", kind: "JustEndpoint", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -186,7 +187,8 @@ func testCRDSourceEndpoints(t *testing.T) { namespace: "foo", registeredNamespace: "foo", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -204,7 +206,8 @@ func testCRDSourceEndpoints(t *testing.T) { namespace: "foo", registeredNamespace: "bar", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -222,7 +225,8 @@ func testCRDSourceEndpoints(t *testing.T) { namespace: "foo", registeredNamespace: "foo", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -240,7 +244,8 @@ func testCRDSourceEndpoints(t *testing.T) { namespace: "foo", registeredNamespace: "foo", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -258,12 +263,14 @@ func testCRDSourceEndpoints(t *testing.T) { namespace: "foo", registeredNamespace: "foo", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, }, - {DNSName: "xyz.example.org", + { + DNSName: "xyz.example.org", Targets: endpoint.Targets{"abc.example.org"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 180, @@ -283,7 +290,8 @@ func testCRDSourceEndpoints(t *testing.T) { annotations: map[string]string{"test": "that"}, annotationFilter: "test=filter_something_else", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -303,7 +311,8 @@ func testCRDSourceEndpoints(t *testing.T) { annotations: map[string]string{"test": "that"}, annotationFilter: "test=that", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -323,7 +332,8 @@ func testCRDSourceEndpoints(t *testing.T) { labels: map[string]string{"test": "that"}, labelFilter: "test=filter_something_else", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -343,7 +353,8 @@ func testCRDSourceEndpoints(t *testing.T) { labels: map[string]string{"test": "that"}, labelFilter: "test=that", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordType: endpoint.RecordTypeA, RecordTTL: 180, @@ -363,7 +374,8 @@ func testCRDSourceEndpoints(t *testing.T) { labels: map[string]string{"test": "that"}, labelFilter: "test=that", endpoints: []*endpoint.Endpoint{ - {DNSName: "abc.example.org", + { + DNSName: "abc.example.org", Targets: endpoint.Targets{"ns1.k8s.io", "ns2.k8s.io"}, RecordType: endpoint.RecordTypeNS, RecordTTL: 180, diff --git a/source/gateway.go b/source/gateway.go index 500962e673..6ca9b0bfcc 100644 --- a/source/gateway.go +++ b/source/gateway.go @@ -32,10 +32,10 @@ import ( kubeinformers "k8s.io/client-go/informers" coreinformers "k8s.io/client-go/informers/core/v1" cache "k8s.io/client-go/tools/cache" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned" - informers "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions" - informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" + informers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" + informers_v1b1 "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1beta1" "sigs.k8s.io/external-dns/endpoint" ) @@ -51,11 +51,11 @@ type gatewayRoute interface { // Metadata returns the route's metadata. Metadata() *metav1.ObjectMeta // Hostnames returns the route's specified hostnames. - Hostnames() []v1alpha2.Hostname + Hostnames() []v1beta1.Hostname // Protocol returns the route's protocol type. - Protocol() v1alpha2.ProtocolType + Protocol() v1beta1.ProtocolType // RouteStatus returns the route's common status. - RouteStatus() v1alpha2.RouteStatus + RouteStatus() v1beta1.RouteStatus } type newGatewayRouteInformerFunc func(informers.SharedInformerFactory) gatewayRouteInformer @@ -82,7 +82,7 @@ func newGatewayInformerFactory(client gateway.Interface, namespace string, label type gatewayRouteSource struct { gwNamespace string gwLabels labels.Selector - gwInformer informers_v1a2.GatewayInformer + gwInformer informers_v1b1.GatewayInformer rtKind string rtNamespace string @@ -123,8 +123,8 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string, } informerFactory := newGatewayInformerFactory(client, config.GatewayNamespace, gwLabels) - gwInformer := informerFactory.Gateway().V1alpha2().Gateways() // TODO: Gateway informer should be shared across gateway sources. - gwInformer.Informer() // Register with factory before starting. + gwInformer := informerFactory.Gateway().V1beta1().Gateways() // TODO: Gateway informer should be shared across gateway sources. + gwInformer.Informer() // Register with factory before starting. rtInformerFactory := informerFactory if config.Namespace != config.GatewayNamespace || !selectorsEqual(rtLabels, gwLabels) { @@ -257,15 +257,15 @@ type gatewayRouteResolver struct { } type gatewayListeners struct { - gateway *v1alpha2.Gateway - listeners map[v1alpha2.SectionName][]v1alpha2.Listener + gateway *v1beta1.Gateway + listeners map[v1beta1.SectionName][]v1beta1.Listener } -func newGatewayRouteResolver(src *gatewayRouteSource, gateways []*v1alpha2.Gateway, namespaces []*corev1.Namespace) *gatewayRouteResolver { +func newGatewayRouteResolver(src *gatewayRouteSource, gateways []*v1beta1.Gateway, namespaces []*corev1.Namespace) *gatewayRouteResolver { // Create Gateway Listener lookup table. gws := make(map[types.NamespacedName]gatewayListeners, len(gateways)) for _, gw := range gateways { - lss := make(map[v1alpha2.SectionName][]v1alpha2.Listener, len(gw.Spec.Listeners)+1) + lss := make(map[v1beta1.SectionName][]v1beta1.Listener, len(gw.Spec.Listeners)+1) for i, lis := range gw.Spec.Listeners { lss[lis.Name] = gw.Spec.Listeners[i : i+1] } @@ -321,9 +321,18 @@ func (c *gatewayRouteResolver) resolve(rt gatewayRoute) (map[string]endpoint.Tar section := sectionVal(ref.SectionName, "") listeners := gw.listeners[section] for i := range listeners { - // Confirm that the protocols match and the Listener allows the Route (based on namespace and kind). lis := &listeners[i] - if !gwProtocolMatches(rt.Protocol(), lis.Protocol) || !c.routeIsAllowed(gw.gateway, lis, rt) { + // Confirm that the Listener and Route protocols match. + if !gwProtocolMatches(rt.Protocol(), lis.Protocol) { + continue + } + // Confirm that the Listener and Route ports match, if specified. + // EXPERIMENTAL: https://gateway-api.sigs.k8s.io/geps/gep-957/ + if ref.Port != nil && *ref.Port != lis.Port { + continue + } + // Confirm that the Listener allows the Route (based on namespace and kind). + if !c.routeIsAllowed(gw.gateway, lis, rt) { continue } // Find all overlapping hostnames between the Route and Listener. @@ -388,23 +397,23 @@ func (c *gatewayRouteResolver) hosts(rt gatewayRoute) ([]string, error) { return hostnames, nil } -func (c *gatewayRouteResolver) routeIsAllowed(gw *v1alpha2.Gateway, lis *v1alpha2.Listener, rt gatewayRoute) bool { +func (c *gatewayRouteResolver) routeIsAllowed(gw *v1beta1.Gateway, lis *v1beta1.Listener, rt gatewayRoute) bool { meta := rt.Metadata() allow := lis.AllowedRoutes // Check the route's namespace. - from := v1alpha2.NamespacesFromSame + from := v1beta1.NamespacesFromSame if allow != nil && allow.Namespaces != nil && allow.Namespaces.From != nil { from = *allow.Namespaces.From } switch from { - case v1alpha2.NamespacesFromAll: + case v1beta1.NamespacesFromAll: // OK - case v1alpha2.NamespacesFromSame: + case v1beta1.NamespacesFromSame: if gw.Namespace != meta.Namespace { return false } - case v1alpha2.NamespacesFromSelector: + case v1beta1.NamespacesFromSelector: selector, err := metav1.LabelSelectorAsSelector(allow.Namespaces.Selector) if err != nil { log.Debugf("Gateway %s/%s section %q has invalid namespace selector: %v", gw.Namespace, gw.Name, lis.Name, err) @@ -442,7 +451,7 @@ func (c *gatewayRouteResolver) routeIsAllowed(gw *v1alpha2.Gateway, lis *v1alpha func gwRouteIsAccepted(conds []metav1.Condition) bool { for _, c := range conds { - if v1alpha2.RouteConditionType(c.Type) == v1alpha2.ConditionRouteAccepted { + if v1beta1.RouteConditionType(c.Type) == v1beta1.RouteConditionAccepted { return c.Status == metav1.ConditionTrue } } @@ -469,12 +478,12 @@ func uniqueTargets(targets endpoint.Targets) endpoint.Targets { // gwProtocolMatches returns whether a and b are the same protocol, // where HTTP and HTTPS are considered the same. -func gwProtocolMatches(a, b v1alpha2.ProtocolType) bool { - if a == v1alpha2.HTTPSProtocolType { - a = v1alpha2.HTTPProtocolType +func gwProtocolMatches(a, b v1beta1.ProtocolType) bool { + if a == v1beta1.HTTPSProtocolType { + a = v1beta1.HTTPProtocolType } - if b == v1alpha2.HTTPSProtocolType { - b = v1alpha2.HTTPProtocolType + if b == v1beta1.HTTPSProtocolType { + b = v1beta1.HTTPProtocolType } return a == b } @@ -522,7 +531,7 @@ func strVal(ptr *string, def string) string { return *ptr } -func sectionVal(ptr *v1alpha2.SectionName, def v1alpha2.SectionName) v1alpha2.SectionName { +func sectionVal(ptr *v1beta1.SectionName, def v1beta1.SectionName) v1beta1.SectionName { if ptr == nil || *ptr == "" { return def } diff --git a/source/gateway_httproute.go b/source/gateway_httproute.go index a0650ac64d..eca0d690e9 100644 --- a/source/gateway_httproute.go +++ b/source/gateway_httproute.go @@ -19,28 +19,28 @@ package source import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - informers "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions" - informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + informers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" + informers_v1b1 "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1beta1" ) // NewGatewayHTTPRouteSource creates a new Gateway HTTPRoute source with the given config. func NewGatewayHTTPRouteSource(clients ClientGenerator, config *Config) (Source, error) { return newGatewayRouteSource(clients, config, "HTTPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInformer { - return &gatewayHTTPRouteInformer{factory.Gateway().V1alpha2().HTTPRoutes()} + return &gatewayHTTPRouteInformer{factory.Gateway().V1beta1().HTTPRoutes()} }) } -type gatewayHTTPRoute struct{ route *v1alpha2.HTTPRoute } +type gatewayHTTPRoute struct{ route *v1beta1.HTTPRoute } -func (rt *gatewayHTTPRoute) Object() kubeObject { return rt.route } -func (rt *gatewayHTTPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } -func (rt *gatewayHTTPRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames } -func (rt *gatewayHTTPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.HTTPProtocolType } -func (rt *gatewayHTTPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus } +func (rt *gatewayHTTPRoute) Object() kubeObject { return rt.route } +func (rt *gatewayHTTPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } +func (rt *gatewayHTTPRoute) Hostnames() []v1beta1.Hostname { return rt.route.Spec.Hostnames } +func (rt *gatewayHTTPRoute) Protocol() v1beta1.ProtocolType { return v1beta1.HTTPProtocolType } +func (rt *gatewayHTTPRoute) RouteStatus() v1beta1.RouteStatus { return rt.route.Status.RouteStatus } type gatewayHTTPRouteInformer struct { - informers_v1a2.HTTPRouteInformer + informers_v1b1.HTTPRouteInformer } func (inf gatewayHTTPRouteInformer) List(namespace string, selector labels.Selector) ([]gatewayRoute, error) { diff --git a/source/gateway_httproute_test.go b/source/gateway_httproute_test.go index b1290ba6fc..28926705db 100644 --- a/source/gateway_httproute_test.go +++ b/source/gateway_httproute_test.go @@ -26,8 +26,8 @@ import ( "k8s.io/apimachinery/pkg/labels" kubefake "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/external-dns/endpoint" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake" + "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" ) func mustGetLabelSelector(s string) labels.Selector { @@ -38,23 +38,23 @@ func mustGetLabelSelector(s string) labels.Selector { return v } -func gatewayStatus(ips ...string) v1alpha2.GatewayStatus { - typ := v1alpha2.IPAddressType - addrs := make([]v1alpha2.GatewayAddress, len(ips)) +func gatewayStatus(ips ...string) v1beta1.GatewayStatus { + typ := v1beta1.IPAddressType + addrs := make([]v1beta1.GatewayAddress, len(ips)) for i, ip := range ips { - addrs[i] = v1alpha2.GatewayAddress{Type: &typ, Value: ip} + addrs[i] = v1beta1.GatewayAddress{Type: &typ, Value: ip} } - return v1alpha2.GatewayStatus{Addresses: addrs} + return v1beta1.GatewayStatus{Addresses: addrs} } -func routeStatus(refs ...v1alpha2.ParentRef) v1alpha2.RouteStatus { - var v v1alpha2.RouteStatus +func routeStatus(refs ...v1beta1.ParentReference) v1beta1.RouteStatus { + var v v1beta1.RouteStatus for _, ref := range refs { - v.Parents = append(v.Parents, v1alpha2.RouteParentStatus{ + v.Parents = append(v.Parents, v1beta1.RouteParentStatus{ ParentRef: ref, Conditions: []metav1.Condition{ { - Type: string(v1alpha2.ConditionRouteAccepted), + Type: string(v1beta1.RouteConditionAccepted), Status: metav1.ConditionTrue, }, }, @@ -63,19 +63,33 @@ func routeStatus(refs ...v1alpha2.ParentRef) v1alpha2.RouteStatus { return v } -func httpRouteStatus(refs ...v1alpha2.ParentRef) v1alpha2.HTTPRouteStatus { - return v1alpha2.HTTPRouteStatus{RouteStatus: routeStatus(refs...)} +func httpRouteStatus(refs ...v1beta1.ParentReference) v1beta1.HTTPRouteStatus { + return v1beta1.HTTPRouteStatus{RouteStatus: routeStatus(refs...)} } -func gatewayParentRef(namespace, name string) v1alpha2.ParentRef { - group := v1alpha2.Group("gateway.networking.k8s.io") - kind := v1alpha2.Kind("Gateway") - return v1alpha2.ParentRef{ +type parentRefOption func(*v1beta1.ParentReference) + +func withSectionName(name v1beta1.SectionName) parentRefOption { + return func(ref *v1beta1.ParentReference) { ref.SectionName = &name } +} + +func withPortNumber(port v1beta1.PortNumber) parentRefOption { + return func(ref *v1beta1.ParentReference) { ref.Port = &port } +} + +func gatewayParentRef(namespace, name string, options ...parentRefOption) v1beta1.ParentReference { + group := v1beta1.Group("gateway.networking.k8s.io") + kind := v1beta1.Kind("Gateway") + ref := v1beta1.ParentReference{ Group: &group, Kind: &kind, - Name: v1alpha2.ObjectName(name), - Namespace: (*v1alpha2.Namespace)(&namespace), + Name: v1beta1.ObjectName(name), + Namespace: (*v1beta1.Namespace)(&namespace), } + for _, opt := range options { + opt(&ref) + } + return ref } func newTestEndpoint(dnsName, recordType string, targets ...string) *endpoint.Endpoint { @@ -94,11 +108,11 @@ func newTestEndpointWithTTL(dnsName, recordType string, ttl int64, targets ...st func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { t.Parallel() - fromAll := v1alpha2.NamespacesFromAll - fromSame := v1alpha2.NamespacesFromSame - fromSelector := v1alpha2.NamespacesFromSelector - allowAllNamespaces := &v1alpha2.AllowedRoutes{ - Namespaces: &v1alpha2.RouteNamespaces{ + fromAll := v1beta1.NamespacesFromAll + fromSame := v1beta1.NamespacesFromSame + fromSelector := v1beta1.NamespacesFromSelector + allowAllNamespaces := &v1beta1.AllowedRoutes{ + Namespaces: &v1beta1.RouteNamespaces{ From: &fromAll, }, } @@ -115,14 +129,14 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { } return v } - hostnames := func(names ...v1alpha2.Hostname) []v1alpha2.Hostname { return names } + hostnames := func(names ...v1beta1.Hostname) []v1beta1.Hostname { return names } tests := []struct { title string config Config namespaces []*corev1.Namespace - gateways []*v1alpha2.Gateway - routes []*v1alpha2.HTTPRoute + gateways []*v1beta1.Gateway + routes []*v1beta1.HTTPRoute endpoints []*endpoint.Endpoint }{ { @@ -131,12 +145,12 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { GatewayNamespace: "gateway-namespace", }, namespaces: namespaces("gateway-namespace", "not-gateway-namespace", "route-namespace"), - gateways: []*v1alpha2.Gateway{ + gateways: []*v1beta1.Gateway{ { ObjectMeta: objectMeta("gateway-namespace", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, AllowedRoutes: allowAllNamespaces, }}, }, @@ -144,15 +158,15 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { }, { ObjectMeta: objectMeta("not-gateway-namespace", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("2.3.4.5"), }, }, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("route-namespace", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("test.example.internal"), }, Status: httpRouteStatus( // The route is attached to both gateways. @@ -170,27 +184,27 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { Namespace: "route-namespace", }, namespaces: namespaces("gateway-namespace", "route-namespace", "not-route-namespace"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("gateway-namespace", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, AllowedRoutes: allowAllNamespaces, }}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: objectMeta("route-namespace", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("route-namespace.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("gateway-namespace", "test")), }, { ObjectMeta: objectMeta("not-route-namespace", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("not-route-namespace.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("gateway-namespace", "test")), @@ -206,15 +220,15 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { GatewayLabelFilter: "foo=bar", }, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{ + gateways: []*v1beta1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Name: "labels-match", Namespace: "default", Labels: map[string]string{"foo": "bar"}, }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }, @@ -224,15 +238,15 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { Namespace: "default", Labels: map[string]string{"foo": "qux"}, }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("2.3.4.5"), }, }, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("test.example.internal"), }, Status: httpRouteStatus( // The route is attached to both gateways. @@ -250,21 +264,21 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { LabelFilter: mustGetLabelSelector("foo=bar"), }, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: metav1.ObjectMeta{ Name: "labels-match", Namespace: "default", Labels: map[string]string{"foo": "bar"}, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("labels-match.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -275,7 +289,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { Namespace: "default", Labels: map[string]string{"foo": "qux"}, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("labels-dont-match.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -291,21 +305,21 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { AnnotationFilter: "foo=bar", }, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: metav1.ObjectMeta{ Name: "annotations-match", Namespace: "default", Annotations: map[string]string{"foo": "bar"}, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("annotations-match.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -316,7 +330,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { Namespace: "default", Annotations: map[string]string{"foo": "qux"}, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("annotations-dont-match.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -330,14 +344,14 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "SkipControllerAnnotation", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: metav1.ObjectMeta{ Name: "api", Namespace: "default", @@ -345,7 +359,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { controllerAnnotationKey: "something-else", }, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("api.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -356,25 +370,25 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "MultipleGateways", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{ + gateways: []*v1beta1.Gateway{ { ObjectMeta: objectMeta("default", "one"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }, { ObjectMeta: objectMeta("default", "two"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("2.3.4.5"), }, }, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("test.example.internal"), }, Status: httpRouteStatus( @@ -390,27 +404,27 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "MultipleListeners", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "one"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{ + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{ { Name: "foo", - Protocol: v1alpha2.HTTPProtocolType, + Protocol: v1beta1.HTTPProtocolType, Hostname: hostnamePtr("foo.example.internal"), }, { Name: "bar", - Protocol: v1alpha2.HTTPProtocolType, + Protocol: v1beta1.HTTPProtocolType, Hostname: hostnamePtr("bar.example.internal"), }, }, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("*.example.internal"), }, Status: httpRouteStatus( @@ -422,116 +436,200 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { newTestEndpoint("bar.example.internal", "A", "1.2.3.4"), }, }, + { + title: "SectionNameMatch", + config: Config{}, + namespaces: namespaces("default"), + gateways: []*v1beta1.Gateway{{ + ObjectMeta: objectMeta("default", "test"), + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{ + { + Name: "foo", + Protocol: v1beta1.HTTPProtocolType, + Hostname: hostnamePtr("foo.example.internal"), + }, + { + Name: "bar", + Protocol: v1beta1.HTTPProtocolType, + Hostname: hostnamePtr("bar.example.internal"), + }, + }, + }, + Status: gatewayStatus("1.2.3.4"), + }}, + routes: []*v1beta1.HTTPRoute{{ + ObjectMeta: objectMeta("default", "test"), + Spec: v1beta1.HTTPRouteSpec{ + Hostnames: hostnames("*.example.internal"), + }, + Status: httpRouteStatus( + gatewayParentRef("default", "test", withSectionName("foo")), + ), + }}, + endpoints: []*endpoint.Endpoint{ + newTestEndpoint("foo.example.internal", "A", "1.2.3.4"), + }, + }, + { + // EXPERIMENTAL: https://gateway-api.sigs.k8s.io/geps/gep-957/ + title: "PortNumberMatch", + config: Config{}, + namespaces: namespaces("default"), + gateways: []*v1beta1.Gateway{{ + ObjectMeta: objectMeta("default", "test"), + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{ + { + Name: "foo", + Protocol: v1beta1.HTTPProtocolType, + Hostname: hostnamePtr("foo.example.internal"), + Port: 80, + }, + { + Name: "bar", + Protocol: v1beta1.HTTPProtocolType, + Hostname: hostnamePtr("bar.example.internal"), + Port: 80, + }, + { + Name: "qux", + Protocol: v1beta1.HTTPProtocolType, + Hostname: hostnamePtr("qux.example.internal"), + Port: 8080, + }, + }, + }, + Status: gatewayStatus("1.2.3.4"), + }}, + routes: []*v1beta1.HTTPRoute{{ + ObjectMeta: objectMeta("default", "test"), + Spec: v1beta1.HTTPRouteSpec{ + Hostnames: hostnames("*.example.internal"), + }, + Status: httpRouteStatus( + gatewayParentRef("default", "test", withPortNumber(80)), + ), + }}, + endpoints: []*endpoint.Endpoint{ + newTestEndpoint("foo.example.internal", "A", "1.2.3.4"), + newTestEndpoint("bar.example.internal", "A", "1.2.3.4"), + }, + }, { title: "WildcardInGateway", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, Hostname: hostnamePtr("*.example.internal"), }}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "no-hostname"), - Spec: v1alpha2.HTTPRouteSpec{ - Hostnames: []v1alpha2.Hostname{ + Spec: v1beta1.HTTPRouteSpec{ + Hostnames: []v1beta1.Hostname{ "foo.example.internal", }, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), }}, endpoints: []*endpoint.Endpoint{ - newTestEndpoint("foo.example.internal", "A", "1.2.3.4")}, + newTestEndpoint("foo.example.internal", "A", "1.2.3.4"), + }, }, { title: "WildcardInRoute", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, Hostname: hostnamePtr("foo.example.internal"), }}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "no-hostname"), - Spec: v1alpha2.HTTPRouteSpec{ - Hostnames: []v1alpha2.Hostname{ + Spec: v1beta1.HTTPRouteSpec{ + Hostnames: []v1beta1.Hostname{ "*.example.internal", }, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), }}, endpoints: []*endpoint.Endpoint{ - newTestEndpoint("foo.example.internal", "A", "1.2.3.4")}, + newTestEndpoint("foo.example.internal", "A", "1.2.3.4"), + }, }, { title: "WildcardInRouteAndGateway", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, Hostname: hostnamePtr("*.example.internal"), }}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "no-hostname"), - Spec: v1alpha2.HTTPRouteSpec{ - Hostnames: []v1alpha2.Hostname{ + Spec: v1beta1.HTTPRouteSpec{ + Hostnames: []v1beta1.Hostname{ "*.example.internal", }, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), }}, endpoints: []*endpoint.Endpoint{ - newTestEndpoint("*.example.internal", "A", "1.2.3.4")}, + newTestEndpoint("*.example.internal", "A", "1.2.3.4"), + }, }, { title: "NoRouteHostname", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, Hostname: hostnamePtr("foo.example.internal"), }}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "no-hostname"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: nil, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), }}, endpoints: []*endpoint.Endpoint{ - newTestEndpoint("foo.example.internal", "A", "1.2.3.4")}, + newTestEndpoint("foo.example.internal", "A", "1.2.3.4"), + }, }, { title: "NoGateways", config: Config{}, namespaces: namespaces("default"), gateways: nil, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("example.internal"), }, Status: httpRouteStatus(), @@ -542,16 +640,16 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "NoHostnames", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "no-hostname"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: nil, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -562,14 +660,14 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "HostnameAnnotation", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: metav1.ObjectMeta{ Name: "without-hostame", @@ -578,7 +676,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "annotation.without-hostname.internal", }, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: nil, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -591,7 +689,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "annotation.with-hostname.internal", }, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("with-hostname.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -609,14 +707,14 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { IgnoreHostnameAnnotation: true, }, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: metav1.ObjectMeta{ Name: "with-hostame", Namespace: "default", @@ -624,7 +722,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "annotation.with-hostname.internal", }, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("with-hostname.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -639,24 +737,24 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { FQDNTemplate: "{{.Name}}.zero.internal, {{.Name}}.one.internal. , {{.Name}}.two.internal ", }, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: objectMeta("default", "fqdn-with-hostnames"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("fqdn-with-hostnames.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), }, { ObjectMeta: objectMeta("default", "fqdn-without-hostnames"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: nil, }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -676,16 +774,16 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { CombineFQDNAndAnnotation: true, }, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "fqdn-with-hostnames"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("fqdn-with-hostnames.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -699,21 +797,21 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "TTL", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: metav1.ObjectMeta{ Name: "valid-ttl", Namespace: "default", Annotations: map[string]string{ttlAnnotationKey: "15s"}, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("valid-ttl.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -724,7 +822,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { Namespace: "default", Annotations: map[string]string{ttlAnnotationKey: "abc"}, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("invalid-ttl.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -739,14 +837,14 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "ProviderAnnotations", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}}, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{Protocol: v1beta1.HTTPProtocolType}}, }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: metav1.ObjectMeta{ Name: "provider-annotations", Namespace: "default", @@ -755,7 +853,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { aliasAnnotationKey: "true", }, }, - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("provider-annotations.com"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -770,31 +868,31 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "DifferentHostnameDifferentGateway", config: Config{}, namespaces: namespaces("default"), - gateways: []*v1alpha2.Gateway{ + gateways: []*v1beta1.Gateway{ { ObjectMeta: objectMeta("default", "one"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ Hostname: hostnamePtr("*.one.internal"), - Protocol: v1alpha2.HTTPProtocolType, + Protocol: v1beta1.HTTPProtocolType, }}, }, Status: gatewayStatus("1.2.3.4"), }, { ObjectMeta: objectMeta("default", "two"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ Hostname: hostnamePtr("*.two.internal"), - Protocol: v1alpha2.HTTPProtocolType, + Protocol: v1beta1.HTTPProtocolType, }}, }, Status: gatewayStatus("2.3.4.5"), }, }, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("test.one.internal", "test.two.internal"), }, Status: httpRouteStatus( @@ -811,13 +909,13 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "AllowedRoutesSameNamespace", config: Config{}, namespaces: namespaces("same-namespace", "other-namespace"), - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("same-namespace", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, - AllowedRoutes: &v1alpha2.AllowedRoutes{ - Namespaces: &v1alpha2.RouteNamespaces{ + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, + AllowedRoutes: &v1beta1.AllowedRoutes{ + Namespaces: &v1beta1.RouteNamespaces{ From: &fromSame, }, }, @@ -825,17 +923,17 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: objectMeta("same-namespace", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("same-namespace.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("same-namespace", "test")), }, { ObjectMeta: objectMeta("other-namespace", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("other-namespace.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("same-namespace", "test")), @@ -867,13 +965,13 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { }, }, }, - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, - AllowedRoutes: &v1alpha2.AllowedRoutes{ - Namespaces: &v1alpha2.RouteNamespaces{ + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, + AllowedRoutes: &v1beta1.AllowedRoutes{ + Namespaces: &v1beta1.RouteNamespaces{ From: &fromSelector, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"team": "foo"}, @@ -884,17 +982,17 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{ + routes: []*v1beta1.HTTPRoute{ { ObjectMeta: objectMeta("foo", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("foo.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), }, { ObjectMeta: objectMeta("bar", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("bar.example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -908,13 +1006,13 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { title: "MissingNamespace", config: Config{}, namespaces: nil, - gateways: []*v1alpha2.Gateway{{ + gateways: []*v1beta1.Gateway{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.HTTPProtocolType, - AllowedRoutes: &v1alpha2.AllowedRoutes{ - Namespaces: &v1alpha2.RouteNamespaces{ + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.HTTPProtocolType, + AllowedRoutes: &v1beta1.AllowedRoutes{ + Namespaces: &v1beta1.RouteNamespaces{ // Namespace selector triggers namespace lookup. From: &fromSelector, Selector: &metav1.LabelSelector{ @@ -926,9 +1024,9 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { }, Status: gatewayStatus("1.2.3.4"), }}, - routes: []*v1alpha2.HTTPRoute{{ + routes: []*v1beta1.HTTPRoute{{ ObjectMeta: objectMeta("default", "test"), - Spec: v1alpha2.HTTPRouteSpec{ + Spec: v1beta1.HTTPRouteSpec{ Hostnames: hostnames("example.internal"), }, Status: httpRouteStatus(gatewayParentRef("default", "test")), @@ -944,12 +1042,12 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { ctx := context.Background() gwClient := gatewayfake.NewSimpleClientset() for _, gw := range tt.gateways { - _, err := gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) + _, err := gwClient.GatewayV1beta1().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) require.NoError(t, err, "failed to create Gateway") } for _, rt := range tt.routes { - _, err := gwClient.GatewayV1alpha2().HTTPRoutes(rt.Namespace).Create(ctx, rt, metav1.CreateOptions{}) + _, err := gwClient.GatewayV1beta1().HTTPRoutes(rt.Namespace).Create(ctx, rt, metav1.CreateOptions{}) require.NoError(t, err, "failed to create HTTPRoute") } kubeClient := kubefake.NewSimpleClientset() @@ -972,4 +1070,4 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) { } } -func hostnamePtr(val v1alpha2.Hostname) *v1alpha2.Hostname { return &val } +func hostnamePtr(val v1beta1.Hostname) *v1beta1.Hostname { return &val } diff --git a/source/gateway_tcproute.go b/source/gateway_tcproute.go index 809072c1d7..5badfaff12 100644 --- a/source/gateway_tcproute.go +++ b/source/gateway_tcproute.go @@ -20,8 +20,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/gateway-api/apis/v1alpha2" - informers "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions" - informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + informers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" + informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1alpha2" ) // NewGatewayTCPRouteSource creates a new Gateway TCPRoute source with the given config. @@ -33,11 +34,13 @@ func NewGatewayTCPRouteSource(clients ClientGenerator, config *Config) (Source, type gatewayTCPRoute struct{ route *v1alpha2.TCPRoute } -func (rt *gatewayTCPRoute) Object() kubeObject { return rt.route } -func (rt *gatewayTCPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } -func (rt *gatewayTCPRoute) Hostnames() []v1alpha2.Hostname { return nil } -func (rt *gatewayTCPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.TCPProtocolType } -func (rt *gatewayTCPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus } +func (rt *gatewayTCPRoute) Object() kubeObject { return rt.route } +func (rt *gatewayTCPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } +func (rt *gatewayTCPRoute) Hostnames() []v1beta1.Hostname { return nil } +func (rt *gatewayTCPRoute) Protocol() v1beta1.ProtocolType { return v1beta1.TCPProtocolType } +func (rt *gatewayTCPRoute) RouteStatus() v1beta1.RouteStatus { + return v1b1RouteStatus(rt.route.Status.RouteStatus) +} type gatewayTCPRouteInformer struct { informers_v1a2.TCPRouteInformer @@ -54,3 +57,50 @@ func (inf gatewayTCPRouteInformer) List(namespace string, selector labels.Select } return routes, nil } + +func v1b1Hostnames(hostnames []v1alpha2.Hostname) []v1beta1.Hostname { + if len(hostnames) == 0 { + return nil + } + list := make([]v1beta1.Hostname, len(hostnames)) + for i, s := range hostnames { + list[i] = v1beta1.Hostname(s) + } + return list +} + +func v1b1RouteStatus(s v1alpha2.RouteStatus) v1beta1.RouteStatus { + return v1beta1.RouteStatus{ + Parents: v1b1RouteParentStatuses(s.Parents), + } +} + +func v1b1RouteParentStatuses(statuses []v1alpha2.RouteParentStatus) []v1beta1.RouteParentStatus { + if len(statuses) == 0 { + return nil + } + list := make([]v1beta1.RouteParentStatus, len(statuses)) + for i, s := range statuses { + list[i] = v1b1RouteParentStatus(s) + } + return list +} + +func v1b1RouteParentStatus(s v1alpha2.RouteParentStatus) v1beta1.RouteParentStatus { + return v1beta1.RouteParentStatus{ + ParentRef: v1b1ParentReference(s.ParentRef), + ControllerName: v1beta1.GatewayController(s.ControllerName), + Conditions: s.Conditions, + } +} + +func v1b1ParentReference(s v1alpha2.ParentReference) v1beta1.ParentReference { + return v1beta1.ParentReference{ + Group: (*v1beta1.Group)(s.Group), + Kind: (*v1beta1.Kind)(s.Kind), + Namespace: (*v1beta1.Namespace)(s.Namespace), + Name: v1beta1.ObjectName(s.Name), + SectionName: (*v1beta1.SectionName)(s.SectionName), + Port: (*v1beta1.PortNumber)(s.Port), + } +} diff --git a/source/gateway_tcproute_test.go b/source/gateway_tcproute_test.go index 1f8b59931a..678c4d3422 100644 --- a/source/gateway_tcproute_test.go +++ b/source/gateway_tcproute_test.go @@ -26,7 +26,8 @@ import ( kubefake "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake" + "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" ) func TestGatewayTCPRouteSourceEndpoints(t *testing.T) { @@ -48,19 +49,19 @@ func TestGatewayTCPRouteSourceEndpoints(t *testing.T) { require.NoError(t, err, "failed to create Namespace") ips := []string{"10.64.0.1", "10.64.0.2"} - gw := &v1alpha2.Gateway{ + gw := &v1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "internal", Namespace: "default", }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.TCPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.TCPProtocolType, }}, }, Status: gatewayStatus(ips...), } - _, err = gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) + _, err = gwClient.GatewayV1beta1().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) require.NoError(t, err, "failed to create Gateway") rt := &v1alpha2.TCPRoute{ @@ -73,7 +74,7 @@ func TestGatewayTCPRouteSourceEndpoints(t *testing.T) { }, Spec: v1alpha2.TCPRouteSpec{}, Status: v1alpha2.TCPRouteStatus{ - RouteStatus: routeStatus(gatewayParentRef("default", "internal")), + RouteStatus: v1a2RouteStatus(v1a2ParentRef("default", "internal")), }, } _, err = gwClient.GatewayV1alpha2().TCPRoutes(rt.Namespace).Create(ctx, rt, metav1.CreateOptions{}) @@ -92,3 +93,31 @@ func TestGatewayTCPRouteSourceEndpoints(t *testing.T) { newTestEndpoint("api-template.foobar.internal", "A", ips...), }) } + +func v1a2ParentRef(namespace, name string) v1alpha2.ParentReference { + group := v1alpha2.Group("gateway.networking.k8s.io") + kind := v1alpha2.Kind("Gateway") + ref := v1alpha2.ParentReference{ + Group: &group, + Kind: &kind, + Name: v1alpha2.ObjectName(name), + Namespace: (*v1alpha2.Namespace)(&namespace), + } + return ref +} + +func v1a2RouteStatus(refs ...v1alpha2.ParentReference) v1alpha2.RouteStatus { + var v v1alpha2.RouteStatus + for _, ref := range refs { + v.Parents = append(v.Parents, v1alpha2.RouteParentStatus{ + ParentRef: ref, + Conditions: []metav1.Condition{ + { + Type: string(v1alpha2.RouteConditionAccepted), + Status: metav1.ConditionTrue, + }, + }, + }) + } + return v +} diff --git a/source/gateway_tlsroute.go b/source/gateway_tlsroute.go index 5fc4c12f11..ec5de040e1 100644 --- a/source/gateway_tlsroute.go +++ b/source/gateway_tlsroute.go @@ -20,8 +20,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/gateway-api/apis/v1alpha2" - informers "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions" - informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + informers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" + informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1alpha2" ) // NewGatewayTLSRouteSource creates a new Gateway TLSRoute source with the given config. @@ -33,11 +34,15 @@ func NewGatewayTLSRouteSource(clients ClientGenerator, config *Config) (Source, type gatewayTLSRoute struct{ route *v1alpha2.TLSRoute } -func (rt *gatewayTLSRoute) Object() kubeObject { return rt.route } -func (rt *gatewayTLSRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } -func (rt *gatewayTLSRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames } -func (rt *gatewayTLSRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.TLSProtocolType } -func (rt *gatewayTLSRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus } +func (rt *gatewayTLSRoute) Object() kubeObject { return rt.route } +func (rt *gatewayTLSRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } +func (rt *gatewayTLSRoute) Hostnames() []v1beta1.Hostname { + return v1b1Hostnames(rt.route.Spec.Hostnames) +} +func (rt *gatewayTLSRoute) Protocol() v1beta1.ProtocolType { return v1beta1.TLSProtocolType } +func (rt *gatewayTLSRoute) RouteStatus() v1beta1.RouteStatus { + return v1b1RouteStatus(rt.route.Status.RouteStatus) +} type gatewayTLSRouteInformer struct { informers_v1a2.TLSRouteInformer diff --git a/source/gateway_tlsroute_test.go b/source/gateway_tlsroute_test.go index 46be23b7b9..f693ba9ace 100644 --- a/source/gateway_tlsroute_test.go +++ b/source/gateway_tlsroute_test.go @@ -26,7 +26,8 @@ import ( kubefake "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake" + "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" ) func TestGatewayTLSRouteSourceEndpoints(t *testing.T) { @@ -48,19 +49,19 @@ func TestGatewayTLSRouteSourceEndpoints(t *testing.T) { require.NoError(t, err, "failed to create Namespace") ips := []string{"10.64.0.1", "10.64.0.2"} - gw := &v1alpha2.Gateway{ + gw := &v1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "internal", Namespace: "default", }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.TLSProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.TLSProtocolType, }}, }, Status: gatewayStatus(ips...), } - _, err = gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) + _, err = gwClient.GatewayV1beta1().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) require.NoError(t, err, "failed to create Gateway") rt := &v1alpha2.TLSRoute{ @@ -75,7 +76,7 @@ func TestGatewayTLSRouteSourceEndpoints(t *testing.T) { Hostnames: []v1alpha2.Hostname{"api-hostnames.foobar.internal"}, }, Status: v1alpha2.TLSRouteStatus{ - RouteStatus: routeStatus(gatewayParentRef("default", "internal")), + RouteStatus: v1a2RouteStatus(v1a2ParentRef("default", "internal")), }, } _, err = gwClient.GatewayV1alpha2().TLSRoutes(rt.Namespace).Create(ctx, rt, metav1.CreateOptions{}) diff --git a/source/gateway_udproute.go b/source/gateway_udproute.go index 08d9c987d5..20ce5ccc43 100644 --- a/source/gateway_udproute.go +++ b/source/gateway_udproute.go @@ -20,8 +20,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/gateway-api/apis/v1alpha2" - informers "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions" - informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + informers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" + informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1alpha2" ) // NewGatewayUDPRouteSource creates a new Gateway UDPRoute source with the given config. @@ -33,11 +34,13 @@ func NewGatewayUDPRouteSource(clients ClientGenerator, config *Config) (Source, type gatewayUDPRoute struct{ route *v1alpha2.UDPRoute } -func (rt *gatewayUDPRoute) Object() kubeObject { return rt.route } -func (rt *gatewayUDPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } -func (rt *gatewayUDPRoute) Hostnames() []v1alpha2.Hostname { return nil } -func (rt *gatewayUDPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.UDPProtocolType } -func (rt *gatewayUDPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus } +func (rt *gatewayUDPRoute) Object() kubeObject { return rt.route } +func (rt *gatewayUDPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta } +func (rt *gatewayUDPRoute) Hostnames() []v1beta1.Hostname { return nil } +func (rt *gatewayUDPRoute) Protocol() v1beta1.ProtocolType { return v1beta1.UDPProtocolType } +func (rt *gatewayUDPRoute) RouteStatus() v1beta1.RouteStatus { + return v1b1RouteStatus(rt.route.Status.RouteStatus) +} type gatewayUDPRouteInformer struct { informers_v1a2.UDPRouteInformer diff --git a/source/gateway_udproute_test.go b/source/gateway_udproute_test.go index c17cee4357..9f1abc7aca 100644 --- a/source/gateway_udproute_test.go +++ b/source/gateway_udproute_test.go @@ -26,7 +26,8 @@ import ( kubefake "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake" + "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" ) func TestGatewayUDPRouteSourceEndpoints(t *testing.T) { @@ -48,19 +49,19 @@ func TestGatewayUDPRouteSourceEndpoints(t *testing.T) { require.NoError(t, err, "failed to create Namespace") ips := []string{"10.64.0.1", "10.64.0.2"} - gw := &v1alpha2.Gateway{ + gw := &v1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "internal", Namespace: "default", }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Protocol: v1alpha2.UDPProtocolType, + Spec: v1beta1.GatewaySpec{ + Listeners: []v1beta1.Listener{{ + Protocol: v1beta1.UDPProtocolType, }}, }, Status: gatewayStatus(ips...), } - _, err = gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) + _, err = gwClient.GatewayV1beta1().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) require.NoError(t, err, "failed to create Gateway") rt := &v1alpha2.UDPRoute{ @@ -73,7 +74,7 @@ func TestGatewayUDPRouteSourceEndpoints(t *testing.T) { }, Spec: v1alpha2.UDPRouteSpec{}, Status: v1alpha2.UDPRouteStatus{ - RouteStatus: routeStatus(gatewayParentRef("default", "internal")), + RouteStatus: v1a2RouteStatus(v1a2ParentRef("default", "internal")), }, } _, err = gwClient.GatewayV1alpha2().UDPRoutes(rt.Namespace).Create(ctx, rt, metav1.CreateOptions{}) diff --git a/source/gloo_proxy_test.go b/source/gloo_proxy_test.go index 2666704be7..c1005fec2b 100644 --- a/source/gloo_proxy_test.go +++ b/source/gloo_proxy_test.go @@ -94,13 +94,13 @@ var internalProxySvc = corev1.Service{ Status: corev1.ServiceStatus{ LoadBalancer: corev1.LoadBalancerStatus{ Ingress: []corev1.LoadBalancerIngress{ - corev1.LoadBalancerIngress{ + { IP: "203.0.113.1", }, - corev1.LoadBalancerIngress{ + { IP: "203.0.113.2", }, - corev1.LoadBalancerIngress{ + { IP: "203.0.113.3", }, }, @@ -181,13 +181,13 @@ var externalProxySvc = corev1.Service{ Status: corev1.ServiceStatus{ LoadBalancer: corev1.LoadBalancerStatus{ Ingress: []corev1.LoadBalancerIngress{ - corev1.LoadBalancerIngress{ + { Hostname: "a.example.org", }, - corev1.LoadBalancerIngress{ + { Hostname: "b.example.org", }, - corev1.LoadBalancerIngress{ + { Hostname: "c.example.org", }, }, @@ -270,7 +270,7 @@ func TestGlooSource(t *testing.T) { assert.NoError(t, err) assert.Len(t, endpoints, 5) assert.ElementsMatch(t, endpoints, []*endpoint.Endpoint{ - &endpoint.Endpoint{ + { DNSName: "a.test", Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP}, RecordType: endpoint.RecordTypeA, @@ -278,7 +278,7 @@ func TestGlooSource(t *testing.T) { Labels: endpoint.Labels{}, ProviderSpecific: endpoint.ProviderSpecific{}, }, - &endpoint.Endpoint{ + { DNSName: "b.test", Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP}, RecordType: endpoint.RecordTypeA, @@ -286,7 +286,7 @@ func TestGlooSource(t *testing.T) { Labels: endpoint.Labels{}, ProviderSpecific: endpoint.ProviderSpecific{}, }, - &endpoint.Endpoint{ + { DNSName: "c.test", Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP}, RecordType: endpoint.RecordTypeA, @@ -300,7 +300,7 @@ func TestGlooSource(t *testing.T) { }, }, }, - &endpoint.Endpoint{ + { DNSName: "d.test", Targets: []string{externalProxySvc.Status.LoadBalancer.Ingress[0].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[1].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[2].Hostname}, RecordType: endpoint.RecordTypeCNAME, @@ -308,7 +308,7 @@ func TestGlooSource(t *testing.T) { Labels: endpoint.Labels{}, ProviderSpecific: endpoint.ProviderSpecific{}, }, - &endpoint.Endpoint{ + { DNSName: "e.test", Targets: []string{externalProxySvc.Status.LoadBalancer.Ingress[0].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[1].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[2].Hostname}, RecordType: endpoint.RecordTypeCNAME, diff --git a/source/kong_tcpingress.go b/source/kong_tcpingress.go index a0e8de1f4d..6dac0a60aa 100644 --- a/source/kong_tcpingress.go +++ b/source/kong_tcpingress.go @@ -255,11 +255,10 @@ func newKongUnstructuredConverter() (*unstructuredConverter, error) { return uc, nil } -//Kong types based on https://github.com/Kong/kubernetes-ingress-controller/blob/v1.2.0/pkg/apis/configuration/v1beta1/types.go to facilitate testing -//When trying to import them from the Kong repo as a dependency it required upgrading the k8s.io/client-go and k8s.io/apimachinery which seemed -//cause several changes in how the mock clients were working that resulted in a bunch of failures in other tests -//If that is dealt with at some point the below can be removed and replaced with an actual import - +// Kong types based on https://github.com/Kong/kubernetes-ingress-controller/blob/v1.2.0/pkg/apis/configuration/v1beta1/types.go to facilitate testing +// When trying to import them from the Kong repo as a dependency it required upgrading the k8s.io/client-go and k8s.io/apimachinery which seemed +// cause several changes in how the mock clients were working that resulted in a bunch of failures in other tests +// If that is dealt with at some point the below can be removed and replaced with an actual import type TCPIngress struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/source/openshift_route_test.go b/source/openshift_route_test.go index e60e84deb2..af6d9fc57d 100644 --- a/source/openshift_route_test.go +++ b/source/openshift_route_test.go @@ -490,7 +490,6 @@ func testOcpRouteSourceEndpoints(t *testing.T) { title: "route without matching labels", labelFilter: "app=web-external", ocpRoute: &routev1.Route{ - Spec: routev1.RouteSpec{ Host: "my-annotation-domain.com", }, diff --git a/source/pod.go b/source/pod.go index dae6a7402d..36e6ffe50e 100644 --- a/source/pod.go +++ b/source/pod.go @@ -74,7 +74,6 @@ func NewPodSource(ctx context.Context, kubeClient kubernetes.Interface, namespac } func (*podSource) AddEventHandler(ctx context.Context, handler func()) { - } func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { diff --git a/source/service_test.go b/source/service_test.go index 3fffc256cd..a16994b2bc 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -686,7 +686,8 @@ func testServiceSourceEndpoints(t *testing.T) { {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"lb.example.com"}}, }, - }, { + }, + { title: "load balancer services annotated with DNS Controller annotations return an endpoint with both annotations in compatibility mode", svcNamespace: "testing", svcName: "foo", diff --git a/source/skipper_routegroup_test.go b/source/skipper_routegroup_test.go index 75881e274a..40cf65c1b6 100644 --- a/source/skipper_routegroup_test.go +++ b/source/skipper_routegroup_test.go @@ -215,14 +215,14 @@ func TestEndpointsFromRouteGroups(t *testing.T) { Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, - }} { + }, + } { t.Run(tt.name, func(t *testing.T) { got := tt.source.endpointsFromRouteGroup(tt.rg) validateEndpoints(t, got, tt.want) }) } - } type fakeRouteGroupClient struct { @@ -709,7 +709,8 @@ func TestRouteGroupsEndpoints(t *testing.T) { Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, - }} { + }, + } { t.Run(tt.name, func(t *testing.T) { if tt.fqdnTemplate != "" { tmpl, err := parseTemplate(tt.fqdnTemplate) diff --git a/source/store.go b/source/store.go index 931d0a33ad..1f99f804df 100644 --- a/source/store.go +++ b/source/store.go @@ -35,7 +35,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned" + gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" ) // ErrSourceNotFound is returned when a requested source doesn't exist. diff --git a/source/store_test.go b/source/store_test.go index 527478f52d..30275d8c4b 100644 --- a/source/store_test.go +++ b/source/store_test.go @@ -33,7 +33,7 @@ import ( fakeDynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes" fakeKube "k8s.io/client-go/kubernetes/fake" - gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned" + gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" ) type MockClientGenerator struct {