mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-12-07 12:51:00 +01:00
add the provider for the tencent cloud.
Signed-off-by: misakazhou <misakazhou@tencent.com>
This commit is contained in:
parent
e47d56112a
commit
a2e7ffc36a
@ -57,6 +57,8 @@ 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)
|
||||
|
||||
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 +117,7 @@ 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 |
|
||||
|
||||
## Kubernetes version compatibility
|
||||
|
||||
@ -183,6 +186,7 @@ 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)
|
||||
|
||||
### Running Locally
|
||||
|
||||
|
||||
208
docs/tutorials/tencentcloud.md
Normal file
208
docs/tutorials/tencentcloud.md
Normal file
@ -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.
|
||||
|
||||
3
go.mod
3
go.mod
@ -142,6 +142,9 @@ require (
|
||||
github.com/smartystreets/gunit v1.3.4 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.4.0 // indirect
|
||||
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/terra-farm/udnssdk v1.3.5 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect
|
||||
go.mongodb.org/mongo-driver v1.5.1 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@ -1349,6 +1349,12 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
|
||||
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=
|
||||
|
||||
3
main.go
3
main.go
@ -64,6 +64,7 @@ import (
|
||||
"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"
|
||||
@ -334,6 +335,8 @@ 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 "tencentcloud":
|
||||
p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun)
|
||||
default:
|
||||
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
||||
}
|
||||
|
||||
@ -192,6 +192,8 @@ type Config struct {
|
||||
OCPRouterName string
|
||||
IBMCloudProxied bool
|
||||
IBMCloudConfigFile string
|
||||
TencentCloudConfigFile string
|
||||
TencentCloudZoneType 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,8 @@ var defaultConfig = &Config{
|
||||
GoDaddyOTE: false,
|
||||
IBMCloudProxied: false,
|
||||
IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json",
|
||||
TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json",
|
||||
TencentCloudZoneType: "",
|
||||
}
|
||||
|
||||
// NewConfig returns new Config object
|
||||
@ -415,7 +420,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")
|
||||
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 +448,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)
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
60
provider/tencentcloud/cloudapi/api.go
Normal file
60
provider/tencentcloud/cloudapi/api.go
Normal file
@ -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)
|
||||
}
|
||||
81
provider/tencentcloud/cloudapi/clientset.go
Normal file
81
provider/tencentcloud/cloudapi/clientset.go
Normal file
@ -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
|
||||
}
|
||||
247
provider/tencentcloud/cloudapi/mockapi.go
Normal file
247
provider/tencentcloud/cloudapi/mockapi.go
Normal file
@ -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()
|
||||
}
|
||||
78
provider/tencentcloud/cloudapi/readonlyapi.go
Normal file
78
provider/tencentcloud/cloudapi/readonlyapi.go
Normal file
@ -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
|
||||
}
|
||||
279
provider/tencentcloud/cloudapi/tencentapi.go
Normal file
279
provider/tencentcloud/cloudapi/tencentapi.go
Normal file
@ -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"
|
||||
}
|
||||
281
provider/tencentcloud/dnspod.go
Normal file
281
provider/tencentcloud/dnspod.go
Normal file
@ -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
|
||||
}
|
||||
319
provider/tencentcloud/privatedns.go
Normal file
319
provider/tencentcloud/privatedns.go
Normal file
@ -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
|
||||
}
|
||||
122
provider/tencentcloud/tencent_cloud.go
Normal file
122
provider/tencentcloud/tencent_cloud.go
Normal file
@ -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
|
||||
}
|
||||
404
provider/tencentcloud/tencent_cloud_test.go
Normal file
404
provider/tencentcloud/tencent_cloud_test.go
Normal file
@ -0,0 +1,404 @@
|
||||
/*
|
||||
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"
|
||||
"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"
|
||||
"testing"
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user