sigs.k8s.io/external-dns@v0.14.1/provider/tencentcloud/dnspod.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tencentcloud 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 24 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" 25 dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323" 26 27 "sigs.k8s.io/external-dns/endpoint" 28 "sigs.k8s.io/external-dns/plan" 29 "sigs.k8s.io/external-dns/provider" 30 ) 31 32 // DnsPod For Public Dns 33 34 func (p *TencentCloudProvider) dnsRecords() ([]*endpoint.Endpoint, error) { 35 recordsList, err := p.recordsForDNS() 36 if err != nil { 37 return nil, err 38 } 39 40 endpoints := make([]*endpoint.Endpoint, 0) 41 recordMap := groupDomainRecordList(recordsList) 42 for _, recordList := range recordMap { 43 name := getDnsDomain(*recordList.RecordList[0].Name, *recordList.Domain.Name) 44 recordType := *recordList.RecordList[0].Type 45 ttl := *recordList.RecordList[0].TTL 46 var targets []string 47 for _, record := range recordList.RecordList { 48 targets = append(targets, *record.Value) 49 } 50 endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...)) 51 } 52 return endpoints, nil 53 } 54 55 func (p *TencentCloudProvider) recordsForDNS() (map[uint64]*RecordListGroup, error) { 56 domainList, err := p.getDomainList() 57 if err != nil { 58 return nil, err 59 } 60 61 recordListGroup := make(map[uint64]*RecordListGroup, 0) 62 for _, domain := range domainList { 63 records, err := p.getDomainRecordList(*domain.Name) 64 if err != nil { 65 return nil, err 66 } 67 for _, record := range records { 68 if *record.Type == "TXT" && strings.HasPrefix(*record.Value, "heritage=") { 69 record.Value = common.StringPtr(fmt.Sprintf(`"%s"`, *record.Value)) 70 } 71 } 72 recordListGroup[*domain.DomainId] = &RecordListGroup{ 73 Domain: domain, 74 RecordList: records, 75 } 76 } 77 return recordListGroup, nil 78 } 79 80 func (p *TencentCloudProvider) getDomainList() ([]*dnspod.DomainListItem, error) { 81 request := dnspod.NewDescribeDomainListRequest() 82 request.Offset = common.Int64Ptr(0) 83 request.Limit = common.Int64Ptr(3000) 84 85 domainList := make([]*dnspod.DomainListItem, 0) 86 totalCount := int64(100) 87 for *request.Offset < totalCount { 88 response, err := p.apiService.DescribeDomainList(request) 89 if err != nil { 90 return nil, err 91 } 92 if response.Response.DomainList != nil && len(response.Response.DomainList) > 0 { 93 if !p.domainFilter.IsConfigured() { 94 domainList = append(domainList, response.Response.DomainList...) 95 } else { 96 for _, domain := range response.Response.DomainList { 97 if p.domainFilter.Match(*domain.Name) { 98 domainList = append(domainList, domain) 99 } 100 } 101 } 102 } 103 totalCount = int64(*response.Response.DomainCountInfo.AllTotal) 104 request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.DomainList))) 105 } 106 return domainList, nil 107 } 108 109 func (p *TencentCloudProvider) getDomainRecordList(domain string) ([]*dnspod.RecordListItem, error) { 110 request := dnspod.NewDescribeRecordListRequest() 111 request.Domain = common.StringPtr(domain) 112 request.Offset = common.Uint64Ptr(0) 113 request.Limit = common.Uint64Ptr(3000) 114 115 domainList := make([]*dnspod.RecordListItem, 0) 116 totalCount := uint64(100) 117 for *request.Offset < totalCount { 118 response, err := p.apiService.DescribeRecordList(request) 119 if err != nil { 120 return nil, err 121 } 122 if response.Response.RecordList != nil && len(response.Response.RecordList) > 0 { 123 for _, record := range response.Response.RecordList { 124 if *record.Name == "@" && *record.Type == "NS" { // Special Record, Skip it. 125 continue 126 } 127 domainList = append(domainList, record) 128 } 129 } 130 totalCount = *response.Response.RecordCountInfo.TotalCount 131 request.Offset = common.Uint64Ptr(*request.Offset + uint64(len(response.Response.RecordList))) 132 } 133 return domainList, nil 134 } 135 136 type RecordListGroup struct { 137 Domain *dnspod.DomainListItem 138 RecordList []*dnspod.RecordListItem 139 } 140 141 func (p *TencentCloudProvider) applyChangesForDNS(changes *plan.Changes) error { 142 recordsGroupMap, err := p.recordsForDNS() 143 if err != nil { 144 return err 145 } 146 147 zoneNameIDMapper := provider.ZoneIDName{} 148 for _, recordsGroup := range recordsGroupMap { 149 if recordsGroup.Domain.DomainId != nil { 150 zoneNameIDMapper.Add(strconv.FormatUint(*recordsGroup.Domain.DomainId, 10), *recordsGroup.Domain.Name) 151 } 152 } 153 154 // Apply Change Delete 155 deleteEndpoints := make(map[string][]uint64) 156 for _, change := range [][]*endpoint.Endpoint{changes.Delete, changes.UpdateOld} { 157 for _, deleteChange := range change { 158 if zoneId, _ := zoneNameIDMapper.FindZone(deleteChange.DNSName); zoneId != "" { 159 zoneIdString, _ := strconv.ParseUint(zoneId, 10, 64) 160 recordListGroup := recordsGroupMap[zoneIdString] 161 for _, domainRecord := range recordListGroup.RecordList { 162 subDomain := getSubDomain(*recordListGroup.Domain.Name, deleteChange) 163 if *domainRecord.Name == subDomain && *domainRecord.Type == deleteChange.RecordType { 164 for _, target := range deleteChange.Targets { 165 if *domainRecord.Value == target { 166 if _, exist := deleteEndpoints[*recordListGroup.Domain.Name]; !exist { 167 deleteEndpoints[*recordListGroup.Domain.Name] = make([]uint64, 0) 168 } 169 deleteEndpoints[*recordListGroup.Domain.Name] = append(deleteEndpoints[*recordListGroup.Domain.Name], *domainRecord.RecordId) 170 } 171 } 172 } 173 } 174 } 175 } 176 } 177 178 if err := p.deleteRecords(deleteEndpoints); err != nil { 179 return err 180 } 181 182 // Apply Change Create 183 createEndpoints := make(map[string][]*endpoint.Endpoint) 184 for zoneId := range zoneNameIDMapper { 185 createEndpoints[zoneId] = make([]*endpoint.Endpoint, 0) 186 } 187 for _, change := range [][]*endpoint.Endpoint{changes.Create, changes.UpdateNew} { 188 for _, createChange := range change { 189 if zoneId, _ := zoneNameIDMapper.FindZone(createChange.DNSName); zoneId != "" { 190 createEndpoints[zoneId] = append(createEndpoints[zoneId], createChange) 191 } 192 } 193 } 194 if err := p.createRecord(recordsGroupMap, createEndpoints); err != nil { 195 return err 196 } 197 return nil 198 } 199 200 func (p *TencentCloudProvider) createRecord(zoneMap map[uint64]*RecordListGroup, endpointsMap map[string][]*endpoint.Endpoint) error { 201 for zoneId, endpoints := range endpointsMap { 202 zoneIdString, _ := strconv.ParseUint(zoneId, 10, 64) 203 domain := zoneMap[zoneIdString] 204 for _, endpoint := range endpoints { 205 for _, target := range endpoint.Targets { 206 if endpoint.RecordType == "TXT" && strings.HasPrefix(target, `"heritage=`) { 207 target = strings.Trim(target, `"`) 208 } 209 if err := p.createRecords(domain.Domain, endpoint, target); err != nil { 210 return err 211 } 212 } 213 } 214 } 215 return nil 216 } 217 218 func (p *TencentCloudProvider) createRecords(domain *dnspod.DomainListItem, endpoint *endpoint.Endpoint, target string) error { 219 request := dnspod.NewCreateRecordRequest() 220 221 request.Domain = common.StringPtr(*domain.Name) 222 request.RecordType = common.StringPtr(endpoint.RecordType) 223 request.Value = common.StringPtr(target) 224 request.SubDomain = common.StringPtr(getSubDomain(*domain.Name, endpoint)) 225 if endpoint.RecordTTL.IsConfigured() { 226 request.TTL = common.Uint64Ptr(uint64(endpoint.RecordTTL)) 227 } 228 request.RecordLine = common.StringPtr("默认") 229 230 if _, err := p.apiService.CreateRecord(request); err != nil { 231 return err 232 } 233 return nil 234 } 235 236 func (p *TencentCloudProvider) deleteRecords(RecordIdsMap map[string][]uint64) error { 237 for domain, recordIds := range RecordIdsMap { 238 if len(recordIds) == 0 { 239 continue 240 } 241 if err := p.deleteRecord(domain, recordIds); err != nil { 242 return err 243 } 244 } 245 return nil 246 } 247 248 func (p *TencentCloudProvider) deleteRecord(domain string, recordIds []uint64) error { 249 request := dnspod.NewDeleteRecordRequest() 250 request.Domain = common.StringPtr(domain) 251 252 for _, recordId := range recordIds { 253 request.RecordId = common.Uint64Ptr(recordId) 254 if _, err := p.apiService.DeleteRecord(request); err != nil { 255 return err 256 } 257 } 258 return nil 259 } 260 261 func groupDomainRecordList(recordListGroup map[uint64]*RecordListGroup) (endpointMap map[string]*RecordListGroup) { 262 endpointMap = make(map[string]*RecordListGroup) 263 264 for _, recordGroup := range recordListGroup { 265 for _, record := range recordGroup.RecordList { 266 key := fmt.Sprintf("%s:%s.%s", *record.Type, *record.Name, *recordGroup.Domain.Name) 267 if *record.Name == TencentCloudEmptyPrefix { 268 key = fmt.Sprintf("%s:%s", *record.Type, *recordGroup.Domain.Name) 269 } 270 if _, exist := endpointMap[key]; !exist { 271 endpointMap[key] = &RecordListGroup{ 272 Domain: recordGroup.Domain, 273 RecordList: make([]*dnspod.RecordListItem, 0), 274 } 275 } 276 endpointMap[key].RecordList = append(endpointMap[key].RecordList, record) 277 } 278 } 279 280 return endpointMap 281 }