sigs.k8s.io/external-dns@v0.14.1/provider/tencentcloud/privatedns.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 "strings" 22 23 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" 24 privatedns "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns/v20201028" 25 26 "sigs.k8s.io/external-dns/endpoint" 27 "sigs.k8s.io/external-dns/plan" 28 "sigs.k8s.io/external-dns/provider" 29 ) 30 31 // PrivateZone For Internal Dns 32 33 func (p *TencentCloudProvider) privateZoneRecords() ([]*endpoint.Endpoint, error) { 34 privateZones, err := p.recordForPrivateZone() 35 if err != nil { 36 return nil, err 37 } 38 39 endpoints := make([]*endpoint.Endpoint, 0) 40 recordMap := groupPrivateZoneRecords(privateZones) 41 for _, recordList := range recordMap { 42 name := getDnsDomain(*recordList.RecordList[0].SubDomain, *recordList.Zone.Domain) 43 recordType := *recordList.RecordList[0].RecordType 44 ttl := *recordList.RecordList[0].TTL 45 var targets []string 46 for _, record := range recordList.RecordList { 47 targets = append(targets, *record.RecordValue) 48 } 49 endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...)) 50 } 51 return endpoints, nil 52 } 53 54 func (p *TencentCloudProvider) recordForPrivateZone() (map[string]*PrivateZoneRecordListGroup, error) { 55 privateZones, err := p.getPrivateZones() 56 if err != nil { 57 return nil, err 58 } 59 60 recordListGroup := make(map[string]*PrivateZoneRecordListGroup, 0) 61 for _, zone := range privateZones { 62 records, err := p.getPrivateZoneRecords(*zone.ZoneId) 63 if err != nil { 64 return nil, err 65 } 66 67 for _, record := range records { 68 if *record.RecordType == "TXT" && strings.HasPrefix(*record.RecordValue, "heritage=") { 69 record.RecordValue = common.StringPtr(fmt.Sprintf("\"%s\"", *record.RecordValue)) 70 } 71 } 72 recordListGroup[*zone.ZoneId] = &PrivateZoneRecordListGroup{ 73 Zone: zone, 74 RecordList: records, 75 } 76 } 77 78 return recordListGroup, nil 79 } 80 81 func (p *TencentCloudProvider) getPrivateZones() ([]*privatedns.PrivateZone, error) { 82 filters := make([]*privatedns.Filter, 1) 83 filters[0] = &privatedns.Filter{ 84 Name: common.StringPtr("Vpc"), 85 Values: []*string{ 86 common.StringPtr(p.vpcID), 87 }, 88 } 89 90 if p.zoneIDFilter.IsConfigured() { 91 zoneIDs := make([]*string, len(p.zoneIDFilter.ZoneIDs)) 92 for index, zoneId := range p.zoneIDFilter.ZoneIDs { 93 zoneIDs[index] = common.StringPtr(zoneId) 94 } 95 filters = append(filters, &privatedns.Filter{ 96 Name: common.StringPtr("ZoneId"), 97 Values: zoneIDs, 98 }) 99 } 100 101 request := privatedns.NewDescribePrivateZoneListRequest() 102 request.Filters = filters 103 request.Offset = common.Int64Ptr(0) 104 request.Limit = common.Int64Ptr(100) 105 106 privateZones := make([]*privatedns.PrivateZone, 0) 107 totalCount := int64(100) 108 for *request.Offset < totalCount { 109 response, err := p.apiService.DescribePrivateZoneList(request) 110 if err != nil { 111 return nil, err 112 } 113 if response.Response.PrivateZoneSet != nil && len(response.Response.PrivateZoneSet) > 0 { 114 privateZones = append(privateZones, response.Response.PrivateZoneSet...) 115 } 116 totalCount = *response.Response.TotalCount 117 request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.PrivateZoneSet))) 118 } 119 120 privateZonesFilter := make([]*privatedns.PrivateZone, 0) 121 for _, privateZone := range privateZones { 122 if !p.domainFilter.Match(*privateZone.Domain) { 123 continue 124 } 125 privateZonesFilter = append(privateZonesFilter, privateZone) 126 } 127 return privateZonesFilter, nil 128 } 129 130 func (p *TencentCloudProvider) getPrivateZoneRecords(zoneId string) ([]*privatedns.PrivateZoneRecord, error) { 131 request := privatedns.NewDescribePrivateZoneRecordListRequest() 132 request.ZoneId = common.StringPtr(zoneId) 133 request.Offset = common.Int64Ptr(0) 134 request.Limit = common.Int64Ptr(100) 135 136 privateZoneRecords := make([]*privatedns.PrivateZoneRecord, 0) 137 totalCount := int64(100) 138 for *request.Offset < totalCount { 139 response, err := p.apiService.DescribePrivateZoneRecordList(request) 140 if err != nil { 141 return nil, err 142 } 143 if response.Response.RecordSet != nil && len(response.Response.RecordSet) > 0 { 144 privateZoneRecords = append(privateZoneRecords, response.Response.RecordSet...) 145 } 146 totalCount = *response.Response.TotalCount 147 request.Offset = common.Int64Ptr(*request.Offset + int64(len(response.Response.RecordSet))) 148 } 149 return privateZoneRecords, nil 150 } 151 152 type PrivateZoneRecordListGroup struct { 153 Zone *privatedns.PrivateZone 154 RecordList []*privatedns.PrivateZoneRecord 155 } 156 157 // Returns nil if the operation was successful or an error if the operation failed. 158 func (p *TencentCloudProvider) applyChangesForPrivateZone(changes *plan.Changes) error { 159 zoneGroups, err := p.recordForPrivateZone() 160 if err != nil { 161 return err 162 } 163 164 // In PrivateDns Service. A Zone has at least one record. The last rule cannot be deleted. 165 for _, zoneGroup := range zoneGroups { 166 if !containsBaseRecord(zoneGroup.RecordList) { 167 err := p.createPrivateZoneRecord(zoneGroup.Zone, &endpoint.Endpoint{ 168 DNSName: *zoneGroup.Zone.Domain, 169 RecordType: "TXT", 170 }, "tencent_provider_record") 171 if err != nil { 172 return err 173 } 174 } 175 } 176 177 zoneNameIDMapper := provider.ZoneIDName{} 178 for _, zoneGroup := range zoneGroups { 179 if zoneGroup.Zone.ZoneId != nil { 180 zoneNameIDMapper.Add(*zoneGroup.Zone.ZoneId, *zoneGroup.Zone.Domain) 181 } 182 } 183 184 // Apply Change Delete 185 deleteEndpoints := make(map[string][]string) 186 for _, change := range [][]*endpoint.Endpoint{changes.Delete, changes.UpdateOld} { 187 for _, deleteChange := range change { 188 if zoneId, _ := zoneNameIDMapper.FindZone(deleteChange.DNSName); zoneId != "" { 189 zoneGroup := zoneGroups[zoneId] 190 for _, zoneRecord := range zoneGroup.RecordList { 191 subDomain := getSubDomain(*zoneGroup.Zone.Domain, deleteChange) 192 if *zoneRecord.SubDomain == subDomain && *zoneRecord.RecordType == deleteChange.RecordType { 193 for _, target := range deleteChange.Targets { 194 if *zoneRecord.RecordValue == target { 195 if _, exist := deleteEndpoints[zoneId]; !exist { 196 deleteEndpoints[zoneId] = make([]string, 0) 197 } 198 deleteEndpoints[zoneId] = append(deleteEndpoints[zoneId], *zoneRecord.RecordId) 199 } 200 } 201 } 202 } 203 } 204 } 205 } 206 207 if err := p.deletePrivateZoneRecords(deleteEndpoints); err != nil { 208 return err 209 } 210 211 // Apply Change Create 212 createEndpoints := make(map[string][]*endpoint.Endpoint) 213 for _, change := range [][]*endpoint.Endpoint{changes.Create, changes.UpdateNew} { 214 for _, createChange := range change { 215 if zoneId, _ := zoneNameIDMapper.FindZone(createChange.DNSName); zoneId != "" { 216 if _, exist := createEndpoints[zoneId]; !exist { 217 createEndpoints[zoneId] = make([]*endpoint.Endpoint, 0) 218 } 219 createEndpoints[zoneId] = append(createEndpoints[zoneId], createChange) 220 } 221 } 222 } 223 if err := p.createPrivateZoneRecords(zoneGroups, createEndpoints); err != nil { 224 return err 225 } 226 return nil 227 } 228 229 func containsBaseRecord(records []*privatedns.PrivateZoneRecord) bool { 230 for _, record := range records { 231 if *record.SubDomain == TencentCloudEmptyPrefix && *record.RecordType == "TXT" && *record.RecordValue == "tencent_provider_record" { 232 return true 233 } 234 } 235 return false 236 } 237 238 func (p *TencentCloudProvider) createPrivateZoneRecords(zoneGroups map[string]*PrivateZoneRecordListGroup, endpointsMap map[string][]*endpoint.Endpoint) error { 239 for zoneId, endpoints := range endpointsMap { 240 zoneGroup := zoneGroups[zoneId] 241 for _, endpoint := range endpoints { 242 for _, target := range endpoint.Targets { 243 if endpoint.RecordType == "TXT" && strings.HasPrefix(target, "\"heritage=") { 244 target = strings.Trim(target, "\"") 245 } 246 if err := p.createPrivateZoneRecord(zoneGroup.Zone, endpoint, target); err != nil { 247 return err 248 } 249 } 250 } 251 } 252 return nil 253 } 254 255 func (p *TencentCloudProvider) deletePrivateZoneRecords(zoneRecordIdsMap map[string][]string) error { 256 for zoneId, zoneRecordIds := range zoneRecordIdsMap { 257 if len(zoneRecordIds) == 0 { 258 continue 259 } 260 if err := p.deletePrivateZoneRecord(zoneId, zoneRecordIds); err != nil { 261 return err 262 } 263 } 264 return nil 265 } 266 267 func (p *TencentCloudProvider) createPrivateZoneRecord(zone *privatedns.PrivateZone, endpoint *endpoint.Endpoint, target string) error { 268 request := privatedns.NewCreatePrivateZoneRecordRequest() 269 request.ZoneId = common.StringPtr(*zone.ZoneId) 270 request.RecordType = common.StringPtr(endpoint.RecordType) 271 request.RecordValue = common.StringPtr(target) 272 request.SubDomain = common.StringPtr(getSubDomain(*zone.Domain, endpoint)) 273 if endpoint.RecordTTL.IsConfigured() { 274 request.TTL = common.Int64Ptr(int64(endpoint.RecordTTL)) 275 } 276 277 if _, err := p.apiService.CreatePrivateZoneRecord(request); err != nil { 278 return err 279 } 280 return nil 281 } 282 283 func (p *TencentCloudProvider) deletePrivateZoneRecord(zoneId string, zoneRecordIds []string) error { 284 recordIds := make([]*string, len(zoneRecordIds)) 285 for index, recordId := range zoneRecordIds { 286 recordIds[index] = common.StringPtr(recordId) 287 } 288 289 request := privatedns.NewDeletePrivateZoneRecordRequest() 290 request.ZoneId = common.StringPtr(zoneId) 291 request.RecordIdSet = recordIds 292 293 if _, err := p.apiService.DeletePrivateZoneRecord(request); err != nil { 294 return err 295 } 296 return nil 297 } 298 299 func groupPrivateZoneRecords(zoneRecords map[string]*PrivateZoneRecordListGroup) (endpointMap map[string]*PrivateZoneRecordListGroup) { 300 endpointMap = make(map[string]*PrivateZoneRecordListGroup) 301 302 for _, recordGroup := range zoneRecords { 303 for _, record := range recordGroup.RecordList { 304 key := fmt.Sprintf("%s:%s.%s", *record.RecordType, *record.SubDomain, *recordGroup.Zone.Domain) 305 if *record.SubDomain == TencentCloudEmptyPrefix { 306 key = fmt.Sprintf("%s:%s", *record.RecordType, *recordGroup.Zone.Domain) 307 } 308 if _, exist := endpointMap[key]; !exist { 309 endpointMap[key] = &PrivateZoneRecordListGroup{ 310 Zone: recordGroup.Zone, 311 RecordList: make([]*privatedns.PrivateZoneRecord, 0), 312 } 313 } 314 endpointMap[key].RecordList = append(endpointMap[key].RecordList, record) 315 } 316 } 317 318 return endpointMap 319 }