@@ -2,6 +2,7 @@ package providers
22
33import (
44 "context"
5+ "encoding/json"
56 "errors"
67 "fmt"
78 "strings"
@@ -70,13 +71,13 @@ func NewRackspaceProvider(config *RackspaceConfig) (*RackspaceProvider, error) {
7071 }, nil
7172}
7273
73- func (p * RackspaceProvider ) getClient () ServiceClient {
74+ func (p * RackspaceProvider ) getClient (ctx context. Context ) ServiceClient {
7475 p .mu .Lock ()
7576 defer p .mu .Unlock ()
7677 if time .Now ().Before (p .tokenExpiry .Add (tokenRefreshBeforeTime )) {
7778 return p .serviceClient
7879 }
79- clientRaw , tokenExpiry , err := authenticateAndCreateClient (context . Background () , p .authProvider , p .config )
80+ clientRaw , tokenExpiry , err := authenticateAndCreateClient (ctx , p .authProvider , p .config )
8081 if err != nil {
8182 log .Error ("Failed to refresh Rackspace token" , "error" , err )
8283 return p .serviceClient
@@ -107,8 +108,13 @@ func authenticateAndCreateClient(ctx context.Context, authProvider AuthProvider,
107108 tokenExpiry := time .Now ().Add (defaultTokenLifetime )
108109 if provider .TokenID != "" {
109110 if authResult , ok := provider .GetAuthResult ().(tokens.CreateResult ); ok {
110- if token , err := authResult .ExtractToken (); err == nil && token != nil {
111+ token , err := authResult .ExtractToken ()
112+ if err != nil {
113+ log .Warn ("Failed to extract token, using default expiry" , "error" , err )
114+ } else if token != nil {
111115 tokenExpiry = token .ExpiresAt
116+ } else {
117+ log .Warn ("Extracted token is nil, using default expiry" )
112118 }
113119 }
114120 }
@@ -124,7 +130,7 @@ func authenticateAndCreateClient(ctx context.Context, authProvider AuthProvider,
124130func (p * RackspaceProvider ) Records (ctx context.Context ) ([]* endpoint.Endpoint , error ) {
125131 var endpoints []* endpoint.Endpoint
126132 opts := domains.ListOpts {}
127- pager := p .getClient ().ListDomains (ctx , opts )
133+ pager := p .getClient (ctx ).ListDomains (ctx , opts )
128134 start := time .Now ()
129135
130136 err := pager .EachPage (ctx , func (ctx context.Context , page pagination.Page ) (bool , error ) {
@@ -136,12 +142,11 @@ func (p *RackspaceProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
136142 if ! p .DomainFilter .Match (domain .Name ) {
137143 continue
138144 }
139- recordOpts := records.ListOpts {}
140- recordPager := p .getClient ().ListRecords (ctx , domain .ID , recordOpts )
145+ recordPager := p .getClient (ctx ).ListRecords (ctx , domain .ID , records.ListOpts {})
141146 err := recordPager .EachPage (ctx , func (ctx context.Context , recordPage pagination.Page ) (bool , error ) {
142147 recordList , err := records .ExtractRecords (recordPage )
143148 if err != nil {
144- return false , err
149+ return false , fmt . Errorf ( "failed to extract records: %w" , err )
145150 }
146151 for _ , record := range recordList {
147152 if ep := convertRecordToEndpoint (record , domain .Name ); ep != nil {
@@ -158,7 +163,7 @@ func (p *RackspaceProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
158163 })
159164 log .Debug ("Fetched records" , "count" , len (endpoints ), "elapsed" , time .Since (start ))
160165 if err != nil {
161- return nil , fmt .Errorf ("failed to fetch domains: %v " , err )
166+ return nil , fmt .Errorf ("failed to fetch domains: %w " , err )
162167 }
163168
164169 return endpoints , nil
@@ -167,7 +172,6 @@ func (p *RackspaceProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
167172// ApplyChanges applies DNS record changes to Rackspace Cloud DNS
168173func (p * RackspaceProvider ) ApplyChanges (ctx context.Context , changes * plan.Changes ) error {
169174 var errs []error
170-
171175 log .Info ("Applying changes" ,
172176 "create" , len (changes .Create ),
173177 "updateNew" , len (changes .UpdateNew ),
@@ -212,39 +216,19 @@ func convertRecordToEndpoint(record records.RecordList, domainName string) *endp
212216 if record .Type == "NS" || record .Type == "SOA" {
213217 return nil
214218 }
215-
216- domainName = strings .TrimSuffix (strings .ToLower (domainName ), "." )
217- recordName := strings .TrimSuffix (strings .ToLower (record .Name ), "." )
218-
219- var dnsName string
220- if recordName == "" || recordName == domainName {
221- dnsName = domainName + "."
222- } else if strings .HasSuffix (recordName , "." + domainName ) {
223- dnsName = recordName + "."
224- } else {
225- dnsName = recordName + "." + domainName + "."
226- }
227-
228- // Normalize TXT data (Rackspace often stores without quotes)
229- data := record .Data
230- if record .Type == "TXT" {
231- data = strings .Trim (data , `"` )
232- data = fmt .Sprintf (`"%s"` , data )
219+ var labels map [string ]string
220+ if record .Type == "TXT" && record .Comment != "" {
221+ if err := json .Unmarshal ([]byte (record .Comment ), & labels ); err != nil {
222+ log .Warn ("Failed to unmarshal TXT record labels" , "name" , record .Name , "comment" , record .Comment , "error" , err )
223+ }
233224 }
234-
235225 ep := & endpoint.Endpoint {
236- DNSName : dnsName ,
237- RecordType : record .Type ,
238- Targets : []string {data },
239- ProviderSpecific : nil ,
240- }
241-
242- if record .TTL != 0 {
243- ep .RecordTTL = endpoint .TTL (record .TTL )
244- } else {
245- ep .RecordTTL = endpoint .TTL (300 ) // default to 300s if API didn’t return
226+ DNSName : record .Name ,
227+ RecordType : record .Type ,
228+ Targets : []string {record .Data },
229+ RecordTTL : endpoint .TTL (record .TTL ),
230+ Labels : labels ,
246231 }
247-
248232 return ep
249233}
250234
@@ -255,19 +239,21 @@ func (p *RackspaceProvider) createRecord(ctx context.Context, ep *endpoint.Endpo
255239 }
256240 fqdn := strings .TrimSuffix (strings .ToLower (ep .DNSName ), "." )
257241 for _ , target := range ep .Targets {
258- createOpts := records.CreateOpts {
259- Name : fqdn ,
260- Type : ep .RecordType ,
261- Data : target ,
262- }
263- if ep .RecordTTL .IsConfigured () {
264- ttl := uint (ep .RecordTTL )
265- if ttl < 300 {
266- ttl = 300
242+ var labels string
243+ if ep .RecordType == "TXT" {
244+ target = strings .Trim (target , `"` )
245+ if len (ep .Labels ) > 0 {
246+ b , _ := json .Marshal (ep .Labels )
247+ labels = string (b )
267248 }
268- createOpts .TTL = ttl
269249 }
270- if _ , err := p .getClient ().CreateRecord (ctx , domain .ID , createOpts ); err != nil {
250+ createOpts := records.CreateOpts {
251+ Name : fqdn ,
252+ Type : ep .RecordType ,
253+ Data : target ,
254+ Comment : labels ,
255+ }
256+ if _ , err := p .getClient (ctx ).CreateRecord (ctx , domain .ID , createOpts ); err != nil {
271257 return fmt .Errorf ("failed to create record %s: %v" , ep .DNSName , err )
272258 }
273259 log .Info ("Created record" , "dnsName" , ep .DNSName , "type" , ep .RecordType , "target" , target )
@@ -297,8 +283,11 @@ func (p *RackspaceProvider) deleteRecord(ctx context.Context, endpoint *endpoint
297283}
298284
299285func (p * RackspaceProvider ) deleteRecordByName (ctx context.Context , domain * domains.DomainList , dnsName , recordType string ) error {
286+ if domain == nil {
287+ return fmt .Errorf ("domain cannot be nil" )
288+ }
300289 wantName := strings .TrimSuffix (strings .ToLower (dnsName ), "." )
301- pager := p .getClient ().ListRecords (ctx , domain .ID , records.ListOpts {})
290+ pager := p .getClient (ctx ).ListRecords (ctx , domain .ID , records.ListOpts {})
302291
303292 var errs []error
304293 err := pager .EachPage (ctx , func (ctx context.Context , page pagination.Page ) (bool , error ) {
@@ -310,8 +299,8 @@ func (p *RackspaceProvider) deleteRecordByName(ctx context.Context, domain *doma
310299 for _ , rec := range recordList {
311300 gotName := strings .TrimSuffix (strings .ToLower (rec .Name ), "." )
312301 if gotName == wantName && strings .EqualFold (rec .Type , recordType ) {
313- if e := p .getClient ().DeleteRecord (ctx , domain .ID , rec .ID ); e != nil {
314- errs = append (errs , fmt .Errorf ("failed to delete record %s: %v " , rec .Name , e ))
302+ if e := p .getClient (ctx ).DeleteRecord (ctx , domain .ID , rec .ID ); e != nil {
303+ errs = append (errs , fmt .Errorf ("failed to delete record %s: %w " , rec .Name , e ))
315304 } else {
316305 log .Info ("Deleted record" , "dnsName" , rec .Name , "type" , recordType )
317306 }
@@ -320,24 +309,27 @@ func (p *RackspaceProvider) deleteRecordByName(ctx context.Context, domain *doma
320309 return true , nil
321310 })
322311 if err != nil {
323- return fmt .Errorf ("failed to list records: %v " , err )
312+ return fmt .Errorf ("failed to list records: %w " , err )
324313 }
325314 if len (errs ) > 0 {
326- return fmt . Errorf ( "errors during deletion: %v" , errs )
315+ return errors . Join ( errs ... )
327316 }
328317 return nil
329318}
330319
331320func (p * RackspaceProvider ) findDomain (ctx context.Context , dnsName string ) (* domains.DomainList , error ) {
321+ if dnsName == "" {
322+ return nil , fmt .Errorf ("DNS name cannot be empty" )
323+ }
332324 dnsName = strings .TrimSuffix (strings .ToLower (dnsName ), "." )
333325 opts := domains.ListOpts {}
334- pager := p .getClient ().ListDomains (ctx , opts )
326+ pager := p .getClient (ctx ).ListDomains (ctx , opts )
335327
336328 var bestMatch * domains.DomainList
337329 err := pager .EachPage (ctx , func (ctx context.Context , page pagination.Page ) (bool , error ) {
338330 domainList , err := domains .ExtractDomains (page )
339331 if err != nil {
340- return false , err
332+ return false , fmt . Errorf ( "failed to extract domains: %w" , err )
341333 }
342334 for _ , domain := range domainList {
343335 domainName := strings .TrimSuffix (strings .ToLower (domain .Name ), "." )
@@ -351,7 +343,7 @@ func (p *RackspaceProvider) findDomain(ctx context.Context, dnsName string) (*do
351343 })
352344
353345 if err != nil {
354- return nil , fmt .Errorf ("failed to list domains: %v " , err )
346+ return nil , fmt .Errorf ("failed to list domains: %w " , err )
355347 }
356348 if bestMatch == nil {
357349 return nil , fmt .Errorf ("no matching domain found for %s" , dnsName )
0 commit comments