diff --git a/README.md b/README.md index 49edf89..d6d370d 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,20 @@ To run the provider, you must provide the following Environment Variables: **Infoblox Environment Variables**: -| Environment Variable | Default value | Required | -|-----------------------------|---------------|----------| -| INFOBLOX_HOST | localhost | true | -| INFOBLOX_PORT | 443 | true | -| INFOBLOX_WAPI_USER | | true | -| INFOBLOX_WAPI_PASSWORD | | true | -| INFOBLOX_VERSION | | true | -| INFOBLOX_SSL_VERIFY | true | false | -| INFOBLOX_DRY_RUN | false | false | -| INFOBLOX_VIEW | default | false | -| INFOBLOX_MAX_RESULTS | 1500 | false | -| INFOBLOX_CREATE_PTR | false | false | -| INFOBLOX_DEFAULT_TTL | 300 | false | +| Environment Variable | Default value | Required | +|-------------------------------------|---------------|----------| +| INFOBLOX_HOST | localhost | true | +| INFOBLOX_PORT | 443 | true | +| INFOBLOX_WAPI_USER | | true | +| INFOBLOX_WAPI_PASSWORD | | true | +| INFOBLOX_VERSION | | true | +| INFOBLOX_SSL_VERIFY | true | false | +| INFOBLOX_DRY_RUN | false | false | +| INFOBLOX_VIEW | default | false | +| INFOBLOX_MAX_RESULTS | 1500 | false | +| INFOBLOX_CREATE_PTR | false | false | +| INFOBLOX_DEFAULT_TTL | 300 | false | +| INFOBLOX_EXTENSIBLE_ATTRIBUTES_JSON | {} | false | **external-dns-infoblox-webhook Environment Variables**: diff --git a/internal/infoblox/infoblox.go b/internal/infoblox/infoblox.go index 9ca8caa..b150fe8 100644 --- a/internal/infoblox/infoblox.go +++ b/internal/infoblox/infoblox.go @@ -20,6 +20,7 @@ Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic import ( "context" + "encoding/json" "fmt" "net" "net/http" @@ -59,19 +60,20 @@ type Provider struct { // StartupConfig clarifies the method signature type StartupConfig struct { - Host string `env:"INFOBLOX_HOST,required" envDefault:"localhost"` - Port int `env:"INFOBLOX_PORT,required" envDefault:"443"` - Username string `env:"INFOBLOX_WAPI_USER,required"` - Password string `env:"INFOBLOX_WAPI_PASSWORD,required"` - Version string `env:"INFOBLOX_VERSION,required"` - SSLVerify bool `env:"INFOBLOX_SSL_VERIFY" envDefault:"true"` - DryRun bool `env:"INFOBLOX_DRY_RUN" envDefault:"false"` - View string `env:"INFOBLOX_VIEW" envDefault:"default"` - MaxResults int `env:"INFOBLOX_MAX_RESULTS" envDefault:"1500"` - CreatePTR bool `env:"INFOBLOX_CREATE_PTR" envDefault:"false"` - DefaultTTL int `env:"INFOBLOX_DEFAULT_TTL" envDefault:"300"` - FQDNRegEx string - NameRegEx string + Host string `env:"INFOBLOX_HOST,required" envDefault:"localhost"` + Port int `env:"INFOBLOX_PORT,required" envDefault:"443"` + Username string `env:"INFOBLOX_WAPI_USER,required"` + Password string `env:"INFOBLOX_WAPI_PASSWORD,required"` + Version string `env:"INFOBLOX_VERSION,required"` + SSLVerify bool `env:"INFOBLOX_SSL_VERIFY" envDefault:"true"` + DryRun bool `env:"INFOBLOX_DRY_RUN" envDefault:"false"` + View string `env:"INFOBLOX_VIEW" envDefault:"default"` + MaxResults int `env:"INFOBLOX_MAX_RESULTS" envDefault:"1500"` + CreatePTR bool `env:"INFOBLOX_CREATE_PTR" envDefault:"false"` + DefaultTTL int `env:"INFOBLOX_DEFAULT_TTL" envDefault:"300"` + ExtAttrsJSON string `env:"INFOBLOX_EXTENSIBLE_ATTRIBUTES_JSON" envDefault:"{}"` + FQDNRegEx string + NameRegEx string } type infobloxRecordSet struct { @@ -181,6 +183,10 @@ func (p *Provider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, e if err != nil { return nil, fmt.Errorf("could not fetch zones: %w", err) } + extAttrs, err := deserializeEAs(p.config.ExtAttrsJSON) + if err != nil { + return nil, err + } for _, zone := range zones { log.Debugf("fetch records from zone '%s'", zone.Fqdn) @@ -188,6 +194,7 @@ func (p *Provider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, e var resA []ibclient.RecordA objA := ibclient.NewEmptyRecordA() objA.View = p.config.View + objA.Ea = extAttrs objA.Zone = zone.Fqdn err = PagingGetObject(p.client, objA, "", searchParams, &resA) if err != nil && !isNotFoundError(err) { @@ -200,6 +207,7 @@ func (p *Provider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, e var resH []ibclient.HostRecord objH := ibclient.NewEmptyHostRecord() objH.View = &p.config.View + objH.Ea = extAttrs objH.Zone = zone.Fqdn err = PagingGetObject(p.client, objH, "", searchParams, &resH) if err != nil && !isNotFoundError(err) { @@ -211,6 +219,7 @@ func (p *Provider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, e var resC []ibclient.RecordCNAME objC := ibclient.NewEmptyRecordCNAME() objC.View = &p.config.View + objC.Ea = extAttrs objC.Zone = zone.Fqdn err = PagingGetObject(p.client, objC, "", searchParams, &resC) if err != nil && !isNotFoundError(err) { @@ -222,6 +231,7 @@ func (p *Provider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, e var resT []ibclient.RecordTXT objT := ibclient.NewEmptyRecordTXT() objT.View = &p.config.View + objT.Ea = extAttrs objT.Zone = zone.Fqdn err = PagingGetObject(p.client, objT, "", searchParams, &resT) if err != nil && !isNotFoundError(err) { @@ -236,6 +246,7 @@ func (p *Provider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, e var resP []ibclient.RecordPTR objP := ibclient.NewEmptyRecordPTR() objP.View = p.config.View + objP.Ea = extAttrs objP.Zone = arpaZone err = PagingGetObject(p.client, objP, "", map[string]string{"zone": arpaZone, "view": p.config.View}, &resP) if err != nil && !isNotFoundError(err) { @@ -660,6 +671,10 @@ func (p *Provider) recordSet(ep *endpoint.Endpoint, getObject bool) (recordSet i if ep.RecordTTL.IsConfigured() { ttl = uint32(ep.RecordTTL) } + extAttrs, err := deserializeEAs(p.config.ExtAttrsJSON) + if err != nil { + return + } ptrToBoolTrue := true switch ep.RecordType { case endpoint.RecordTypeA: @@ -668,6 +683,7 @@ func (p *Provider) recordSet(ep *endpoint.Endpoint, getObject bool) (recordSet i obj.Name = &ep.DNSName // TODO: get target index obj.Ipv4Addr = &ep.Targets[0] + obj.Ea = extAttrs obj.Ttl = &ttl obj.UseTtl = &ptrToBoolTrue if getObject { @@ -692,6 +708,7 @@ func (p *Provider) recordSet(ep *endpoint.Endpoint, getObject bool) (recordSet i obj.PtrdName = &ep.DNSName // TODO: get target index obj.Ipv4Addr = &ep.Targets[0] + obj.Ea = extAttrs obj.Ttl = &ttl obj.UseTtl = &ptrToBoolTrue if getObject { @@ -714,6 +731,7 @@ func (p *Provider) recordSet(ep *endpoint.Endpoint, getObject bool) (recordSet i obj := ibclient.NewEmptyRecordCNAME() obj.Name = &ep.DNSName obj.Canonical = &ep.Targets[0] + obj.Ea = extAttrs obj.Ttl = &ttl obj.UseTtl = &ptrToBoolTrue if getObject { @@ -741,6 +759,7 @@ func (p *Provider) recordSet(ep *endpoint.Endpoint, getObject bool) (recordSet i obj := ibclient.NewEmptyRecordTXT() obj.Text = &ep.Targets[0] obj.Name = &ep.DNSName + obj.Ea = extAttrs obj.Ttl = &ttl obj.UseTtl = &ptrToBoolTrue // TODO: Zone? @@ -784,3 +803,14 @@ func lookupEnvAtoi(key string, fallback int) (i int) { } return } + +func deserializeEAs(extAttrJSON string) (map[string]interface{}, error) { + extAttrs := make(map[string]interface{}) + if extAttrJSON == "" { + return extAttrs, nil + } + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return nil, fmt.Errorf("cannot process 'ext_attrs' field: %w", err) + } + return extAttrs, nil +}