@@ -91,6 +91,11 @@ type NS1Config struct {
9191 NS1IgnoreSSL bool
9292 DryRun bool
9393 MinTTLSeconds int
94+ // Optional: map a zone FQDN (or suffix) to the NS1 zone handle/ID to use
95+ // when looking up that zone. Keys/values are case-insensitive;
96+ // trailing dots on keys are ignored.
97+ // e.g. map[string]string{"example.com":"corp-prod-zone","dev.example.com":"dev-view-handle"}
98+ ZoneHandleOverrides map [string ]string
9499}
95100
96101// NS1Provider is the NS1 provider
@@ -101,6 +106,8 @@ type NS1Provider struct {
101106 zoneIDFilter provider.ZoneIDFilter
102107 dryRun bool
103108 minTTLSeconds int
109+ // normalized overrides: fqdn (no trailing dot, lowercased) -> handle/ID (lowercased)
110+ zoneHandleOverrides map [string ]string
104111}
105112
106113// NewNS1Provider creates a new NS1 Provider
@@ -137,10 +144,11 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr
137144 apiClient := api .NewClient (client , clientArgs ... )
138145
139146 return & NS1Provider {
140- client : NS1DomainService {apiClient },
141- domainFilter : config .DomainFilter ,
142- zoneIDFilter : config .ZoneIDFilter ,
143- minTTLSeconds : config .MinTTLSeconds ,
147+ client : NS1DomainService {apiClient },
148+ domainFilter : config .DomainFilter ,
149+ zoneIDFilter : config .ZoneIDFilter ,
150+ minTTLSeconds : config .MinTTLSeconds ,
151+ zoneHandleOverrides : normalizeOverrides (config .ZoneHandleOverrides ),
144152 }, nil
145153}
146154
@@ -155,9 +163,20 @@ func (p *NS1Provider) Records(ctx context.Context) ([]*endpoint.Endpoint, error)
155163
156164 for _ , zone := range zones {
157165 // TODO handle Header Codes
158- zoneData , _ , err := p .client .GetZone (zone .String ())
166+ // Prefer lookup via handle/ID if an override exists; fall back to FQDN.
167+ lookup := p .zoneLookupKeyFor (zone .Zone )
168+ zoneData , _ , err := p .client .GetZone (lookup )
159169 if err != nil {
160- return nil , err
170+ if lookup != strings .TrimSuffix (zone .Zone , "." ) {
171+ // fallback to FQDN lookup if override missed
172+ zoneData , _ , err = p .client .GetZone (zone .Zone )
173+ if err != nil {
174+ return nil , err
175+ }
176+ } else {
177+ return nil , err
178+ }
179+
161180 }
162181
163182 for _ , record := range zoneData .Records {
@@ -208,7 +227,7 @@ func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error {
208227 }
209228
210229 // separate into per-zone change sets to be passed to the API.
211- changesByZone := ns1ChangesByZone (zones , changes )
230+ changesByZone := p . ns1ChangesByZone (zones , changes )
212231 for zoneName , changes := range changesByZone {
213232 for _ , change := range changes {
214233 record := p .ns1BuildRecord (zoneName , change )
@@ -302,15 +321,34 @@ func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change {
302321 return changes
303322}
304323
324+ // normalizeOverrides lowercases keys/values and strips any trailing dot on keys.
325+ func normalizeOverrides (m map [string ]string ) map [string ]string {
326+ if len (m ) == 0 {
327+ return map [string ]string {}
328+ }
329+ out := make (map [string ]string , len (m ))
330+ for k , v := range m {
331+ kk := strings .TrimSuffix (strings .ToLower (strings .TrimSpace (k )), "." )
332+ vv := strings .ToLower (strings .TrimSpace (v ))
333+ if kk != "" && vv != "" {
334+ out [kk ] = vv
335+ }
336+ }
337+ return out
338+ }
339+
305340// ns1ChangesByZone separates a multi-zone change into a single change per zone.
306- func ns1ChangesByZone (zones []* dns.Zone , changeSets []* ns1Change ) map [string ][]* ns1Change {
341+ // The map key becomes the "write key": handle/ID if overridden, else FQDN.
342+ func (p * NS1Provider ) ns1ChangesByZone (zones []* dns.Zone , changeSets []* ns1Change ) map [string ][]* ns1Change {
307343 changes := make (map [string ][]* ns1Change )
308344 zoneNameIDMapper := provider.ZoneIDName {}
345+
309346 for _ , z := range zones {
310347 zoneNameIDMapper .Add (z .Zone , z .Zone )
311348 changes [z .Zone ] = []* ns1Change {}
312349 }
313350
351+ // group changes by zone FQDN
314352 for _ , c := range changeSets {
315353 zone , _ := zoneNameIDMapper .FindZone (c .Endpoint .DNSName )
316354 if zone == "" {
@@ -320,5 +358,34 @@ func ns1ChangesByZone(zones []*dns.Zone, changeSets []*ns1Change) map[string][]*
320358 changes [zone ] = append (changes [zone ], c )
321359 }
322360
361+ // replace zone FQDN with zone handle if FQDN is overridden
362+ for k , v := range changes {
363+ writeKey := p .zoneLookupKeyFor (k )
364+
365+ if writeKey != k {
366+ changes [writeKey ] = v
367+ delete (changes , k )
368+ }
369+ }
370+
323371 return changes
324372}
373+
374+ // zoneLookupKeyFor returns the preferred key to pass to GetZone:
375+ // if an override exists for fqdn (or a more specific suffix), return its mapped handle/ID;
376+ // otherwise return the normalized FQDN.
377+ func (p * NS1Provider ) zoneLookupKeyFor (fqdn string ) string {
378+ name := strings .TrimSuffix (strings .ToLower (strings .TrimSpace (fqdn )), "." )
379+ bestKey := ""
380+ for k := range p .zoneHandleOverrides {
381+ if name == k || strings .HasSuffix (name , "." + k ) {
382+ if len (k ) > len (bestKey ) {
383+ bestKey = k // longest (most specific) match wins
384+ }
385+ }
386+ }
387+ if bestKey != "" {
388+ return p .zoneHandleOverrides [bestKey ]
389+ }
390+ return name
391+ }
0 commit comments