mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
1008 lines
37 KiB
Go
1008 lines
37 KiB
Go
/*
|
|
Copyright 2022 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package ibmcloud
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/crn"
|
|
"github.com/IBM/go-sdk-core/v5/core"
|
|
"github.com/IBM/networking-go-sdk/dnsrecordsv1"
|
|
"github.com/IBM/networking-go-sdk/dnssvcsv1"
|
|
"github.com/IBM/networking-go-sdk/zonesv1"
|
|
"gopkg.in/yaml.v2"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"sigs.k8s.io/external-dns/endpoint"
|
|
"sigs.k8s.io/external-dns/plan"
|
|
"sigs.k8s.io/external-dns/provider"
|
|
"sigs.k8s.io/external-dns/source"
|
|
)
|
|
|
|
var proxyTypeNotSupported = map[string]bool{
|
|
"LOC": true,
|
|
"MX": true,
|
|
"NS": true,
|
|
"SPF": true,
|
|
"TXT": true,
|
|
"SRV": true,
|
|
}
|
|
|
|
var privateTypeSupported = map[string]bool{
|
|
"A": true,
|
|
"CNAME": true,
|
|
"TXT": true,
|
|
}
|
|
|
|
const (
|
|
// recordCreate is a ChangeAction enum value
|
|
recordCreate = "CREATE"
|
|
// recordDelete is a ChangeAction enum value
|
|
recordDelete = "DELETE"
|
|
// recordUpdate is a ChangeAction enum value
|
|
recordUpdate = "UPDATE"
|
|
// defaultPublicRecordTTL 1 = automatic
|
|
defaultPublicRecordTTL = 1
|
|
|
|
proxyFilter = "ibmcloud-proxied"
|
|
vpcFilter = "ibmcloud-vpc"
|
|
zoneStatePendingNetwork = "PENDING_NETWORK_ADD"
|
|
zoneStateActive = "ACTIVE"
|
|
)
|
|
|
|
// Source shadow the interface source.Source. used primarily for unit testing.
|
|
type Source interface {
|
|
Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error)
|
|
AddEventHandler(context.Context, func())
|
|
}
|
|
|
|
// ibmcloudClient is a minimal implementation of DNS API that we actually use, used primarily for unit testing.
|
|
type ibmcloudClient interface {
|
|
ListAllDDNSRecordsWithContext(ctx context.Context, listAllDNSRecordsOptions *dnsrecordsv1.ListAllDnsRecordsOptions) (result *dnsrecordsv1.ListDnsrecordsResp, response *core.DetailedResponse, err error)
|
|
CreateDNSRecordWithContext(ctx context.Context, createDNSRecordOptions *dnsrecordsv1.CreateDnsRecordOptions) (result *dnsrecordsv1.DnsrecordResp, response *core.DetailedResponse, err error)
|
|
DeleteDNSRecordWithContext(ctx context.Context, deleteDNSRecordOptions *dnsrecordsv1.DeleteDnsRecordOptions) (result *dnsrecordsv1.DeleteDnsrecordResp, response *core.DetailedResponse, err error)
|
|
UpdateDNSRecordWithContext(ctx context.Context, updateDNSRecordOptions *dnsrecordsv1.UpdateDnsRecordOptions) (result *dnsrecordsv1.DnsrecordResp, response *core.DetailedResponse, err error)
|
|
ListDnszonesWithContext(ctx context.Context, listDnszonesOptions *dnssvcsv1.ListDnszonesOptions) (result *dnssvcsv1.ListDnszones, response *core.DetailedResponse, err error)
|
|
GetDnszoneWithContext(ctx context.Context, getDnszoneOptions *dnssvcsv1.GetDnszoneOptions) (result *dnssvcsv1.Dnszone, response *core.DetailedResponse, err error)
|
|
CreatePermittedNetworkWithContext(ctx context.Context, createPermittedNetworkOptions *dnssvcsv1.CreatePermittedNetworkOptions) (result *dnssvcsv1.PermittedNetwork, response *core.DetailedResponse, err error)
|
|
ListResourceRecordsWithContext(ctx context.Context, listResourceRecordsOptions *dnssvcsv1.ListResourceRecordsOptions) (result *dnssvcsv1.ListResourceRecords, response *core.DetailedResponse, err error)
|
|
CreateResourceRecordWithContext(ctx context.Context, createResourceRecordOptions *dnssvcsv1.CreateResourceRecordOptions) (result *dnssvcsv1.ResourceRecord, response *core.DetailedResponse, err error)
|
|
DeleteResourceRecordWithContext(ctx context.Context, deleteResourceRecordOptions *dnssvcsv1.DeleteResourceRecordOptions) (response *core.DetailedResponse, err error)
|
|
UpdateResourceRecordWithContext(ctx context.Context, updateResourceRecordOptions *dnssvcsv1.UpdateResourceRecordOptions) (result *dnssvcsv1.ResourceRecord, response *core.DetailedResponse, err error)
|
|
NewResourceRecordInputRdataRdataARecord(ip string) (model *dnssvcsv1.ResourceRecordInputRdataRdataARecord, err error)
|
|
NewResourceRecordInputRdataRdataCnameRecord(cname string) (model *dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord, err error)
|
|
NewResourceRecordInputRdataRdataTxtRecord(text string) (model *dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord, err error)
|
|
NewResourceRecordUpdateInputRdataRdataARecord(ip string) (model *dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord, err error)
|
|
NewResourceRecordUpdateInputRdataRdataCnameRecord(cname string) (model *dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord, err error)
|
|
NewResourceRecordUpdateInputRdataRdataTxtRecord(text string) (model *dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord, err error)
|
|
}
|
|
|
|
type ibmcloudService struct {
|
|
publicZonesService *zonesv1.ZonesV1
|
|
publicRecordsService *dnsrecordsv1.DnsRecordsV1
|
|
privateDNSService *dnssvcsv1.DnsSvcsV1
|
|
}
|
|
|
|
func (i ibmcloudService) ListAllDDNSRecordsWithContext(ctx context.Context, listAllDNSRecordsOptions *dnsrecordsv1.ListAllDnsRecordsOptions) (result *dnsrecordsv1.ListDnsrecordsResp, response *core.DetailedResponse, err error) {
|
|
return i.publicRecordsService.ListAllDnsRecordsWithContext(ctx, listAllDNSRecordsOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) CreateDNSRecordWithContext(ctx context.Context, createDNSRecordOptions *dnsrecordsv1.CreateDnsRecordOptions) (result *dnsrecordsv1.DnsrecordResp, response *core.DetailedResponse, err error) {
|
|
return i.publicRecordsService.CreateDnsRecordWithContext(ctx, createDNSRecordOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) DeleteDNSRecordWithContext(ctx context.Context, deleteDNSRecordOptions *dnsrecordsv1.DeleteDnsRecordOptions) (result *dnsrecordsv1.DeleteDnsrecordResp, response *core.DetailedResponse, err error) {
|
|
return i.publicRecordsService.DeleteDnsRecordWithContext(ctx, deleteDNSRecordOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) UpdateDNSRecordWithContext(ctx context.Context, updateDNSRecordOptions *dnsrecordsv1.UpdateDnsRecordOptions) (result *dnsrecordsv1.DnsrecordResp, response *core.DetailedResponse, err error) {
|
|
return i.publicRecordsService.UpdateDnsRecordWithContext(ctx, updateDNSRecordOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) ListDnszonesWithContext(ctx context.Context, listDnszonesOptions *dnssvcsv1.ListDnszonesOptions) (result *dnssvcsv1.ListDnszones, response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.ListDnszonesWithContext(ctx, listDnszonesOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) GetDnszoneWithContext(ctx context.Context, getDnszoneOptions *dnssvcsv1.GetDnszoneOptions) (result *dnssvcsv1.Dnszone, response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.GetDnszoneWithContext(ctx, getDnszoneOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) CreatePermittedNetworkWithContext(ctx context.Context, createPermittedNetworkOptions *dnssvcsv1.CreatePermittedNetworkOptions) (result *dnssvcsv1.PermittedNetwork, response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.CreatePermittedNetworkWithContext(ctx, createPermittedNetworkOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) ListResourceRecordsWithContext(ctx context.Context, listResourceRecordsOptions *dnssvcsv1.ListResourceRecordsOptions) (result *dnssvcsv1.ListResourceRecords, response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.ListResourceRecordsWithContext(ctx, listResourceRecordsOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) CreateResourceRecordWithContext(ctx context.Context, createResourceRecordOptions *dnssvcsv1.CreateResourceRecordOptions) (result *dnssvcsv1.ResourceRecord, response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.CreateResourceRecordWithContext(ctx, createResourceRecordOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) DeleteResourceRecordWithContext(ctx context.Context, deleteResourceRecordOptions *dnssvcsv1.DeleteResourceRecordOptions) (response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.DeleteResourceRecordWithContext(ctx, deleteResourceRecordOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) UpdateResourceRecordWithContext(ctx context.Context, updateResourceRecordOptions *dnssvcsv1.UpdateResourceRecordOptions) (result *dnssvcsv1.ResourceRecord, response *core.DetailedResponse, err error) {
|
|
return i.privateDNSService.UpdateResourceRecordWithContext(ctx, updateResourceRecordOptions)
|
|
}
|
|
|
|
func (i ibmcloudService) NewResourceRecordInputRdataRdataARecord(ip string) (model *dnssvcsv1.ResourceRecordInputRdataRdataARecord, err error) {
|
|
return i.privateDNSService.NewResourceRecordInputRdataRdataARecord(ip)
|
|
}
|
|
|
|
func (i ibmcloudService) NewResourceRecordInputRdataRdataCnameRecord(cname string) (model *dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord, err error) {
|
|
return i.privateDNSService.NewResourceRecordInputRdataRdataCnameRecord(cname)
|
|
}
|
|
|
|
func (i ibmcloudService) NewResourceRecordInputRdataRdataTxtRecord(text string) (model *dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord, err error) {
|
|
return i.privateDNSService.NewResourceRecordInputRdataRdataTxtRecord(text)
|
|
}
|
|
|
|
func (i ibmcloudService) NewResourceRecordUpdateInputRdataRdataARecord(ip string) (model *dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord, err error) {
|
|
return i.privateDNSService.NewResourceRecordUpdateInputRdataRdataARecord(ip)
|
|
}
|
|
|
|
func (i ibmcloudService) NewResourceRecordUpdateInputRdataRdataCnameRecord(cname string) (model *dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord, err error) {
|
|
return i.privateDNSService.NewResourceRecordUpdateInputRdataRdataCnameRecord(cname)
|
|
}
|
|
|
|
func (i ibmcloudService) NewResourceRecordUpdateInputRdataRdataTxtRecord(text string) (model *dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord, err error) {
|
|
return i.privateDNSService.NewResourceRecordUpdateInputRdataRdataTxtRecord(text)
|
|
}
|
|
|
|
// IBMCloudProvider is an implementation of Provider for IBM Cloud DNS.
|
|
type IBMCloudProvider struct {
|
|
provider.BaseProvider
|
|
source Source
|
|
Client ibmcloudClient
|
|
// only consider hosted zones managing domains ending in this suffix
|
|
domainFilter endpoint.DomainFilter
|
|
zoneIDFilter provider.ZoneIDFilter
|
|
instanceID string
|
|
privateZone bool
|
|
proxiedByDefault bool
|
|
DryRun bool
|
|
}
|
|
|
|
type ibmcloudConfig struct {
|
|
Endpoint string `json:"endpoint" yaml:"endpoint"`
|
|
APIKey string `json:"apiKey" yaml:"apiKey"`
|
|
CRN string `json:"instanceCrn" yaml:"instanceCrn"`
|
|
IAMURL string `json:"iamUrl" yaml:"iamUrl"`
|
|
InstanceID string `json:"-" yaml:"-"`
|
|
}
|
|
|
|
// ibmcloudChange differentiates between ChangActions
|
|
type ibmcloudChange struct {
|
|
Action string
|
|
PublicResourceRecord dnsrecordsv1.DnsrecordDetails
|
|
PrivateResourceRecord dnssvcsv1.ResourceRecord
|
|
}
|
|
|
|
func getConfig(configFile string) (*ibmcloudConfig, error) {
|
|
contents, err := os.ReadFile(configFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read IBM Cloud config file '%s': %v", configFile, err)
|
|
}
|
|
cfg := &ibmcloudConfig{}
|
|
err = yaml.Unmarshal(contents, &cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read IBM Cloud config file '%s': %v", configFile, err)
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func (c *ibmcloudConfig) Validate(authenticator core.Authenticator, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter) (ibmcloudService, bool, error) {
|
|
var service ibmcloudService
|
|
isPrivate := false
|
|
log.Debugf("filters: %v, %v", domainFilter.Filters, zoneIDFilter.ZoneIDs)
|
|
if (len(domainFilter.Filters) == 0 || domainFilter.Filters[0] == "") && zoneIDFilter.ZoneIDs[0] == "" {
|
|
return service, isPrivate, fmt.Errorf("at lease one of filters: 'domain-filter', 'zone-id-filter' needed")
|
|
}
|
|
|
|
crn, err := crn.Parse(c.CRN)
|
|
if err != nil {
|
|
return service, isPrivate, err
|
|
}
|
|
log.Infof("IBM Cloud Service: %s", crn.ServiceName)
|
|
c.InstanceID = crn.ServiceInstance
|
|
|
|
switch {
|
|
case strings.Contains(crn.ServiceName, "internet-svcs"):
|
|
if len(domainFilter.Filters) > 1 || len(zoneIDFilter.ZoneIDs) > 1 {
|
|
return service, isPrivate, fmt.Errorf("for public zone, only one domain id filter or domain name filter allowed")
|
|
}
|
|
var zoneID string
|
|
// Public DNS service
|
|
service.publicZonesService, err = zonesv1.NewZonesV1(&zonesv1.ZonesV1Options{
|
|
Authenticator: authenticator,
|
|
Crn: core.StringPtr(c.CRN),
|
|
})
|
|
if err != nil {
|
|
return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud public zones client: %v", err)
|
|
}
|
|
if c.Endpoint != "" {
|
|
service.publicZonesService.SetServiceURL(c.Endpoint)
|
|
}
|
|
|
|
zonesResp, _, err := service.publicZonesService.ListZones(&zonesv1.ListZonesOptions{})
|
|
if err != nil {
|
|
return service, isPrivate, fmt.Errorf("failed to list ibmcloud public zones: %v", err)
|
|
}
|
|
for _, zone := range zonesResp.Result {
|
|
log.Debugf("zoneName: %s, zoneID: %s", *zone.Name, *zone.ID)
|
|
if len(domainFilter.Filters) > 0 && domainFilter.Filters[0] != "" && domainFilter.Match(*zone.Name) {
|
|
log.Debugf("zone %s found.", *zone.ID)
|
|
zoneID = *zone.ID
|
|
break
|
|
}
|
|
if len(zoneIDFilter.ZoneIDs[0]) != 0 && zoneIDFilter.Match(*zone.ID) {
|
|
log.Debugf("zone %s found.", *zone.ID)
|
|
zoneID = *zone.ID
|
|
break
|
|
}
|
|
}
|
|
if len(zoneID) == 0 {
|
|
return service, isPrivate, fmt.Errorf("no matched zone found")
|
|
}
|
|
|
|
service.publicRecordsService, err = dnsrecordsv1.NewDnsRecordsV1(&dnsrecordsv1.DnsRecordsV1Options{
|
|
Authenticator: authenticator,
|
|
Crn: core.StringPtr(c.CRN),
|
|
ZoneIdentifier: core.StringPtr(zoneID),
|
|
})
|
|
if err != nil {
|
|
return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud public records client: %v", err)
|
|
}
|
|
if c.Endpoint != "" {
|
|
service.publicRecordsService.SetServiceURL(c.Endpoint)
|
|
}
|
|
case strings.Contains(crn.ServiceName, "dns-svcs"):
|
|
isPrivate = true
|
|
// Private DNS service
|
|
service.privateDNSService, err = dnssvcsv1.NewDnsSvcsV1(&dnssvcsv1.DnsSvcsV1Options{
|
|
Authenticator: authenticator,
|
|
})
|
|
if err != nil {
|
|
return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud private records client: %v", err)
|
|
}
|
|
if c.Endpoint != "" {
|
|
service.privateDNSService.SetServiceURL(c.Endpoint)
|
|
}
|
|
default:
|
|
return service, isPrivate, fmt.Errorf("IBM Cloud instance crn is not provided or invalid dns crn : %s", c.CRN)
|
|
}
|
|
|
|
return service, isPrivate, nil
|
|
}
|
|
|
|
// NewIBMCloudProvider creates a new IBMCloud provider.
|
|
//
|
|
// Returns the provider or an error if a provider could not be created.
|
|
func NewIBMCloudProvider(configFile string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, source source.Source, proxiedByDefault bool, dryRun bool) (*IBMCloudProvider, error) {
|
|
cfg, err := getConfig(configFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
authenticator := &core.IamAuthenticator{
|
|
ApiKey: cfg.APIKey,
|
|
}
|
|
if cfg.IAMURL != "" {
|
|
authenticator = &core.IamAuthenticator{
|
|
ApiKey: cfg.APIKey,
|
|
URL: cfg.IAMURL,
|
|
}
|
|
}
|
|
|
|
client, isPrivate, err := cfg.Validate(authenticator, domainFilter, zoneIDFilter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
provider := &IBMCloudProvider{
|
|
Client: client,
|
|
source: source,
|
|
domainFilter: domainFilter,
|
|
zoneIDFilter: zoneIDFilter,
|
|
instanceID: cfg.InstanceID,
|
|
privateZone: isPrivate,
|
|
proxiedByDefault: proxiedByDefault,
|
|
DryRun: dryRun,
|
|
}
|
|
return provider, nil
|
|
}
|
|
|
|
// Records gets the current records.
|
|
//
|
|
// Returns the current records or an error if the operation failed.
|
|
func (p *IBMCloudProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, err error) {
|
|
if p.privateZone {
|
|
endpoints, err = p.privateRecords(ctx)
|
|
} else {
|
|
endpoints, err = p.publicRecords(ctx)
|
|
}
|
|
return endpoints, err
|
|
}
|
|
|
|
// ApplyChanges applies a given set of changes in a given zone.
|
|
func (p *IBMCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
|
log.Debugln("applying change...")
|
|
ibmcloudChanges := []*ibmcloudChange{}
|
|
for _, endpoint := range changes.Create {
|
|
for _, target := range endpoint.Targets {
|
|
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordCreate, endpoint, target))
|
|
}
|
|
}
|
|
|
|
for i, desired := range changes.UpdateNew {
|
|
current := changes.UpdateOld[i]
|
|
|
|
add, remove, leave := provider.Difference(current.Targets, desired.Targets)
|
|
|
|
log.Debugf("add: %v, remove: %v, leave: %v", add, remove, leave)
|
|
for _, a := range add {
|
|
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordCreate, desired, a))
|
|
}
|
|
|
|
for _, a := range leave {
|
|
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordUpdate, desired, a))
|
|
}
|
|
|
|
for _, a := range remove {
|
|
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, current, a))
|
|
}
|
|
}
|
|
|
|
for _, endpoint := range changes.Delete {
|
|
for _, target := range endpoint.Targets {
|
|
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, endpoint, target))
|
|
}
|
|
}
|
|
|
|
return p.submitChanges(ctx, ibmcloudChanges)
|
|
}
|
|
|
|
// AdjustEndpoints modifies the endpoints as needed by the specific provider
|
|
func (p *IBMCloudProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
|
|
adjustedEndpoints := []*endpoint.Endpoint{}
|
|
for _, e := range endpoints {
|
|
log.Debugf("adjusting endpont: %v", *e)
|
|
proxied := shouldBeProxied(e, p.proxiedByDefault)
|
|
if proxied {
|
|
e.RecordTTL = 0
|
|
}
|
|
e.SetProviderSpecificProperty(proxyFilter, strconv.FormatBool(proxied))
|
|
|
|
adjustedEndpoints = append(adjustedEndpoints, e)
|
|
}
|
|
return adjustedEndpoints, nil
|
|
}
|
|
|
|
// submitChanges takes a zone and a collection of Changes and sends them as a single transaction.
|
|
func (p *IBMCloudProvider) submitChanges(ctx context.Context, changes []*ibmcloudChange) error {
|
|
// return early if there is nothing to change
|
|
if len(changes) == 0 {
|
|
return nil
|
|
}
|
|
|
|
log.Debugln("submmiting change...")
|
|
if p.privateZone {
|
|
return p.submitChangesForPrivateDNS(ctx, changes)
|
|
}
|
|
return p.submitChangesForPublicDNS(ctx, changes)
|
|
}
|
|
|
|
// submitChangesForPublicDNS takes a zone and a collection of Changes and sends them as a single transaction on public dns.
|
|
func (p *IBMCloudProvider) submitChangesForPublicDNS(ctx context.Context, changes []*ibmcloudChange) error {
|
|
records, err := p.listAllPublicRecords(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, change := range changes {
|
|
logFields := log.Fields{
|
|
"record": *change.PublicResourceRecord.Name,
|
|
"type": *change.PublicResourceRecord.Type,
|
|
"ttl": *change.PublicResourceRecord.TTL,
|
|
"action": change.Action,
|
|
}
|
|
|
|
if p.DryRun {
|
|
continue
|
|
}
|
|
|
|
log.WithFields(logFields).Info("Changing record.")
|
|
|
|
if change.Action == recordUpdate {
|
|
recordID := p.getPublicRecordID(records, change.PublicResourceRecord)
|
|
if recordID == "" {
|
|
log.WithFields(logFields).Errorf("failed to find previous record: %v", *change.PublicResourceRecord.Name)
|
|
continue
|
|
}
|
|
p.updateRecord(ctx, "", recordID, change)
|
|
} else if change.Action == recordDelete {
|
|
recordID := p.getPublicRecordID(records, change.PublicResourceRecord)
|
|
if recordID == "" {
|
|
log.WithFields(logFields).Errorf("failed to find previous record: %v", *change.PublicResourceRecord.Name)
|
|
continue
|
|
}
|
|
p.deleteRecord(ctx, "", recordID)
|
|
} else if change.Action == recordCreate {
|
|
p.createRecord(ctx, "", change)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// submitChangesForPrivateDNS takes a zone and a collection of Changes and sends them as a single transaction on private dns.
|
|
func (p *IBMCloudProvider) submitChangesForPrivateDNS(ctx context.Context, changes []*ibmcloudChange) error {
|
|
zones, err := p.privateZones(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// separate into per-zone change sets to be passed to the API.
|
|
changesByPrivateZone := p.changesByPrivateZone(ctx, zones, changes)
|
|
|
|
for zoneID, changes := range changesByPrivateZone {
|
|
records, err := p.listAllPrivateRecords(ctx, zoneID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, change := range changes {
|
|
logFields := log.Fields{
|
|
"record": *change.PrivateResourceRecord.Name,
|
|
"type": *change.PrivateResourceRecord.Type,
|
|
"ttl": *change.PrivateResourceRecord.TTL,
|
|
"action": change.Action,
|
|
}
|
|
|
|
log.WithFields(logFields).Info("Changing record.")
|
|
|
|
if p.DryRun {
|
|
continue
|
|
}
|
|
|
|
if change.Action == recordUpdate {
|
|
recordID := p.getPrivateRecordID(records, change.PrivateResourceRecord)
|
|
if recordID == "" {
|
|
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.PrivateResourceRecord)
|
|
continue
|
|
}
|
|
p.updateRecord(ctx, zoneID, recordID, change)
|
|
} else if change.Action == recordDelete {
|
|
recordID := p.getPrivateRecordID(records, change.PrivateResourceRecord)
|
|
if recordID == "" {
|
|
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.PrivateResourceRecord)
|
|
continue
|
|
}
|
|
p.deleteRecord(ctx, zoneID, recordID)
|
|
} else if change.Action == recordCreate {
|
|
p.createRecord(ctx, zoneID, change)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// privateZones return zones in private dns
|
|
func (p *IBMCloudProvider) privateZones(ctx context.Context) ([]dnssvcsv1.Dnszone, error) {
|
|
result := []dnssvcsv1.Dnszone{}
|
|
// if there is a zoneIDfilter configured
|
|
// && if the filter isn't just a blank string (used in tests)
|
|
if len(p.zoneIDFilter.ZoneIDs) > 0 && p.zoneIDFilter.ZoneIDs[0] != "" {
|
|
log.Debugln("zoneIDFilter configured. only looking up zone IDs defined")
|
|
for _, zoneID := range p.zoneIDFilter.ZoneIDs {
|
|
log.Debugf("looking up zone %s", zoneID)
|
|
detailResponse, _, err := p.Client.GetDnszoneWithContext(ctx, &dnssvcsv1.GetDnszoneOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
DnszoneID: core.StringPtr(zoneID),
|
|
})
|
|
if err != nil {
|
|
log.Errorf("zone %s lookup failed, %v", zoneID, err)
|
|
continue
|
|
}
|
|
log.WithFields(log.Fields{
|
|
"zoneName": *detailResponse.Name,
|
|
"zoneID": *detailResponse.ID,
|
|
}).Debugln("adding zone for consideration")
|
|
result = append(result, *detailResponse)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
log.Debugln("no zoneIDFilter configured, looking at all zones")
|
|
|
|
zonesResponse, _, err := p.Client.ListDnszonesWithContext(ctx, &dnssvcsv1.ListDnszonesOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, zone := range zonesResponse.Dnszones {
|
|
if !p.domainFilter.Match(*zone.Name) {
|
|
log.Debugf("zone %s not in domain filter", *zone.Name)
|
|
continue
|
|
}
|
|
result = append(result, zone)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// activePrivateZone active zone with new records add if not active
|
|
func (p *IBMCloudProvider) activePrivateZone(ctx context.Context, zoneID, vpc string) {
|
|
permittedNetworkVpc := &dnssvcsv1.PermittedNetworkVpc{
|
|
VpcCrn: core.StringPtr(vpc),
|
|
}
|
|
createPermittedNetworkOptions := &dnssvcsv1.CreatePermittedNetworkOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
DnszoneID: core.StringPtr(zoneID),
|
|
PermittedNetwork: permittedNetworkVpc,
|
|
Type: core.StringPtr("vpc"),
|
|
}
|
|
_, _, err := p.Client.CreatePermittedNetworkWithContext(ctx, createPermittedNetworkOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to active zone %s in VPC %s with error: %v", zoneID, vpc, err)
|
|
}
|
|
}
|
|
|
|
// changesByPrivateZone separates a multi-zone change into a single change per zone.
|
|
func (p *IBMCloudProvider) changesByPrivateZone(ctx context.Context, zones []dnssvcsv1.Dnszone, changeSet []*ibmcloudChange) map[string][]*ibmcloudChange {
|
|
changes := make(map[string][]*ibmcloudChange)
|
|
zoneNameIDMapper := provider.ZoneIDName{}
|
|
for _, z := range zones {
|
|
zoneNameIDMapper.Add(*z.ID, *z.Name)
|
|
changes[*z.ID] = []*ibmcloudChange{}
|
|
}
|
|
|
|
for _, c := range changeSet {
|
|
zoneID, _ := zoneNameIDMapper.FindZone(*c.PrivateResourceRecord.Name)
|
|
if zoneID == "" {
|
|
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", *c.PrivateResourceRecord.Name)
|
|
continue
|
|
}
|
|
changes[zoneID] = append(changes[zoneID], c)
|
|
}
|
|
|
|
return changes
|
|
}
|
|
|
|
func (p *IBMCloudProvider) publicRecords(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
|
log.Debugf("Listing records on public zone")
|
|
dnsRecords, err := p.listAllPublicRecords(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.groupPublicRecords(dnsRecords), nil
|
|
}
|
|
|
|
func (p *IBMCloudProvider) listAllPublicRecords(ctx context.Context) ([]dnsrecordsv1.DnsrecordDetails, error) {
|
|
var dnsRecords []dnsrecordsv1.DnsrecordDetails
|
|
page := 1
|
|
GETRECORDS:
|
|
listAllDNSRecordsOptions := &dnsrecordsv1.ListAllDnsRecordsOptions{
|
|
Page: core.Int64Ptr(int64(page)),
|
|
}
|
|
records, _, err := p.Client.ListAllDDNSRecordsWithContext(ctx, listAllDNSRecordsOptions)
|
|
if err != nil {
|
|
return dnsRecords, err
|
|
}
|
|
dnsRecords = append(dnsRecords, records.Result...)
|
|
// Loop if more records exist
|
|
if *records.ResultInfo.TotalCount > int64(page*100) {
|
|
page = page + 1
|
|
log.Debugf("More than one pages records found, page: %d", page)
|
|
goto GETRECORDS
|
|
}
|
|
return dnsRecords, nil
|
|
}
|
|
|
|
func (p *IBMCloudProvider) groupPublicRecords(records []dnsrecordsv1.DnsrecordDetails) []*endpoint.Endpoint {
|
|
endpoints := []*endpoint.Endpoint{}
|
|
|
|
// group supported records by name and type
|
|
groups := map[string][]dnsrecordsv1.DnsrecordDetails{}
|
|
|
|
for _, r := range records {
|
|
if !provider.SupportedRecordType(*r.Type) {
|
|
continue
|
|
}
|
|
|
|
groupBy := *r.Name + *r.Type
|
|
if _, ok := groups[groupBy]; !ok {
|
|
groups[groupBy] = []dnsrecordsv1.DnsrecordDetails{}
|
|
}
|
|
|
|
groups[groupBy] = append(groups[groupBy], r)
|
|
}
|
|
|
|
// create single endpoint with all the targets for each name/type
|
|
for _, records := range groups {
|
|
targets := make([]string, len(records))
|
|
for i, record := range records {
|
|
targets[i] = *record.Content
|
|
}
|
|
|
|
ep := endpoint.NewEndpointWithTTL(
|
|
*records[0].Name,
|
|
*records[0].Type,
|
|
endpoint.TTL(*records[0].TTL),
|
|
targets...).WithProviderSpecific(proxyFilter, strconv.FormatBool(*records[0].Proxied))
|
|
|
|
log.Debugf(
|
|
"Found %s record for '%s' with target '%s'.",
|
|
ep.RecordType,
|
|
ep.DNSName,
|
|
ep.Targets,
|
|
)
|
|
|
|
endpoints = append(endpoints, ep)
|
|
}
|
|
return endpoints
|
|
}
|
|
|
|
func (p *IBMCloudProvider) privateRecords(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
|
log.Debugf("Listing records on private zone")
|
|
var vpc string
|
|
zones, err := p.privateZones(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sources, err := p.source.Endpoints(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Filter VPC annoation for private zone active
|
|
for _, source := range sources {
|
|
vpc = checkVPCAnnotation(source)
|
|
if len(vpc) > 0 {
|
|
log.Debugf("VPC found: %s", vpc)
|
|
break
|
|
}
|
|
}
|
|
|
|
endpoints := []*endpoint.Endpoint{}
|
|
for _, zone := range zones {
|
|
if len(vpc) > 0 && *zone.State == zoneStatePendingNetwork {
|
|
log.Debugf("active zone: %s", *zone.ID)
|
|
p.activePrivateZone(ctx, *zone.ID, vpc)
|
|
}
|
|
|
|
dnsRecords, err := p.listAllPrivateRecords(ctx, *zone.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endpoints = append(endpoints, p.groupPrivateRecords(dnsRecords)...)
|
|
}
|
|
|
|
return endpoints, nil
|
|
}
|
|
|
|
func (p *IBMCloudProvider) listAllPrivateRecords(ctx context.Context, zoneID string) ([]dnssvcsv1.ResourceRecord, error) {
|
|
var dnsRecords []dnssvcsv1.ResourceRecord
|
|
offset := 0
|
|
GETRECORDS:
|
|
listResourceRecordsOptions := &dnssvcsv1.ListResourceRecordsOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
DnszoneID: core.StringPtr(zoneID),
|
|
Offset: core.Int64Ptr(int64(offset)),
|
|
}
|
|
records, _, err := p.Client.ListResourceRecordsWithContext(ctx, listResourceRecordsOptions)
|
|
if err != nil {
|
|
return dnsRecords, err
|
|
}
|
|
oRecords := records.ResourceRecords
|
|
dnsRecords = append(dnsRecords, oRecords...)
|
|
// Loop if more records exist
|
|
if int64(offset+1) < *records.TotalCount && int64(offset+200) < *records.TotalCount {
|
|
offset = offset + 200
|
|
log.Debugf("More than one pages records found, page: %d", offset/200+1)
|
|
goto GETRECORDS
|
|
}
|
|
return dnsRecords, nil
|
|
}
|
|
|
|
func (p *IBMCloudProvider) groupPrivateRecords(records []dnssvcsv1.ResourceRecord) []*endpoint.Endpoint {
|
|
endpoints := []*endpoint.Endpoint{}
|
|
// group supported records by name and type
|
|
groups := map[string][]dnssvcsv1.ResourceRecord{}
|
|
for _, r := range records {
|
|
if !provider.SupportedRecordType(*r.Type) || !privateTypeSupported[*r.Type] {
|
|
continue
|
|
}
|
|
rname := *r.Name
|
|
rtype := *r.Type
|
|
groupBy := rname + rtype
|
|
if _, ok := groups[groupBy]; !ok {
|
|
groups[groupBy] = []dnssvcsv1.ResourceRecord{}
|
|
}
|
|
|
|
groups[groupBy] = append(groups[groupBy], r)
|
|
}
|
|
|
|
// create single endpoint with all the targets for each name/type
|
|
for _, records := range groups {
|
|
targets := make([]string, len(records))
|
|
for i, record := range records {
|
|
data := record.Rdata
|
|
log.Debugf("record data: %v", data)
|
|
switch *record.Type {
|
|
case "A":
|
|
if !isNil(data["ip"]) {
|
|
targets[i] = data["ip"].(string)
|
|
}
|
|
case "CNAME":
|
|
if !isNil(data["cname"]) {
|
|
targets[i] = data["cname"].(string)
|
|
}
|
|
case "TXT":
|
|
if !isNil(data["text"]) {
|
|
targets[i] = data["text"].(string)
|
|
}
|
|
log.Debugf("text record data: %v", targets[i])
|
|
}
|
|
}
|
|
|
|
ep := endpoint.NewEndpointWithTTL(
|
|
*records[0].Name,
|
|
*records[0].Type,
|
|
endpoint.TTL(*records[0].TTL), targets...)
|
|
|
|
log.Debugf(
|
|
"Found %s record for '%s' with target '%s'.",
|
|
ep.RecordType,
|
|
ep.DNSName,
|
|
ep.Targets,
|
|
)
|
|
|
|
endpoints = append(endpoints, ep)
|
|
}
|
|
return endpoints
|
|
}
|
|
|
|
func (p *IBMCloudProvider) getPublicRecordID(records []dnsrecordsv1.DnsrecordDetails, record dnsrecordsv1.DnsrecordDetails) string {
|
|
for _, zoneRecord := range records {
|
|
if *zoneRecord.Name == *record.Name && *zoneRecord.Type == *record.Type && *zoneRecord.Content == *record.Content {
|
|
return *zoneRecord.ID
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (p *IBMCloudProvider) getPrivateRecordID(records []dnssvcsv1.ResourceRecord, record dnssvcsv1.ResourceRecord) string {
|
|
for _, zoneRecord := range records {
|
|
if *zoneRecord.Name == *record.Name && *zoneRecord.Type == *record.Type {
|
|
return *zoneRecord.ID
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (p *IBMCloudProvider) newIBMCloudChange(action string, endpoint *endpoint.Endpoint, target string) *ibmcloudChange {
|
|
ttl := defaultPublicRecordTTL
|
|
proxied := shouldBeProxied(endpoint, p.proxiedByDefault)
|
|
|
|
if endpoint.RecordTTL.IsConfigured() {
|
|
ttl = int(endpoint.RecordTTL)
|
|
}
|
|
|
|
if p.privateZone {
|
|
rData := make(map[string]interface{})
|
|
switch endpoint.RecordType {
|
|
case "A":
|
|
rData[dnssvcsv1.CreateResourceRecordOptions_Type_A] = &dnssvcsv1.ResourceRecordInputRdataRdataARecord{
|
|
Ip: core.StringPtr(target),
|
|
}
|
|
case "CNAME":
|
|
rData[dnssvcsv1.CreateResourceRecordOptions_Type_Cname] = &dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord{
|
|
Cname: core.StringPtr(target),
|
|
}
|
|
case "TXT":
|
|
rData[dnssvcsv1.CreateResourceRecordOptions_Type_Txt] = &dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord{
|
|
Text: core.StringPtr(target),
|
|
}
|
|
}
|
|
return &ibmcloudChange{
|
|
Action: action,
|
|
PrivateResourceRecord: dnssvcsv1.ResourceRecord{
|
|
Name: core.StringPtr(endpoint.DNSName),
|
|
TTL: core.Int64Ptr(int64(ttl)),
|
|
Type: core.StringPtr(endpoint.RecordType),
|
|
Rdata: rData,
|
|
},
|
|
}
|
|
}
|
|
|
|
return &ibmcloudChange{
|
|
Action: action,
|
|
PublicResourceRecord: dnsrecordsv1.DnsrecordDetails{
|
|
Name: core.StringPtr(endpoint.DNSName),
|
|
TTL: core.Int64Ptr(int64(ttl)),
|
|
Proxied: core.BoolPtr(proxied),
|
|
Type: core.StringPtr(endpoint.RecordType),
|
|
Content: core.StringPtr(target),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p *IBMCloudProvider) createRecord(ctx context.Context, zoneID string, change *ibmcloudChange) {
|
|
if p.privateZone {
|
|
createResourceRecordOptions := &dnssvcsv1.CreateResourceRecordOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
DnszoneID: core.StringPtr(zoneID),
|
|
Name: change.PrivateResourceRecord.Name,
|
|
Type: change.PrivateResourceRecord.Type,
|
|
TTL: change.PrivateResourceRecord.TTL,
|
|
}
|
|
switch *change.PrivateResourceRecord.Type {
|
|
case "A":
|
|
data, _ := change.PrivateResourceRecord.Rdata[dnssvcsv1.CreateResourceRecordOptions_Type_A].(*dnssvcsv1.ResourceRecordInputRdataRdataARecord)
|
|
aData, _ := p.Client.NewResourceRecordInputRdataRdataARecord(*data.Ip)
|
|
createResourceRecordOptions.SetRdata(aData)
|
|
case "CNAME":
|
|
data, _ := change.PrivateResourceRecord.Rdata[dnssvcsv1.CreateResourceRecordOptions_Type_Cname].(*dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord)
|
|
cnameData, _ := p.Client.NewResourceRecordInputRdataRdataCnameRecord(*data.Cname)
|
|
createResourceRecordOptions.SetRdata(cnameData)
|
|
case "TXT":
|
|
data, _ := change.PrivateResourceRecord.Rdata[dnssvcsv1.CreateResourceRecordOptions_Type_Txt].(*dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord)
|
|
txtData, _ := p.Client.NewResourceRecordInputRdataRdataTxtRecord(*data.Text)
|
|
createResourceRecordOptions.SetRdata(txtData)
|
|
}
|
|
_, _, err := p.Client.CreateResourceRecordWithContext(ctx, createResourceRecordOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to create %s type record named %s: %v", *change.PrivateResourceRecord.Type, *change.PrivateResourceRecord.Name, err)
|
|
}
|
|
} else {
|
|
createDNSRecordOptions := &dnsrecordsv1.CreateDnsRecordOptions{
|
|
Name: change.PublicResourceRecord.Name,
|
|
Type: change.PublicResourceRecord.Type,
|
|
TTL: change.PublicResourceRecord.TTL,
|
|
Content: change.PublicResourceRecord.Content,
|
|
}
|
|
_, _, err := p.Client.CreateDNSRecordWithContext(ctx, createDNSRecordOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to create %s type record named %s: %v", *change.PublicResourceRecord.Type, *change.PublicResourceRecord.Name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *IBMCloudProvider) updateRecord(ctx context.Context, zoneID, recordID string, change *ibmcloudChange) {
|
|
if p.privateZone {
|
|
updateResourceRecordOptions := &dnssvcsv1.UpdateResourceRecordOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
DnszoneID: core.StringPtr(zoneID),
|
|
RecordID: core.StringPtr(recordID),
|
|
Name: change.PrivateResourceRecord.Name,
|
|
TTL: change.PrivateResourceRecord.TTL,
|
|
}
|
|
switch *change.PrivateResourceRecord.Type {
|
|
case "A":
|
|
data, _ := change.PrivateResourceRecord.Rdata[dnssvcsv1.CreateResourceRecordOptions_Type_A].(*dnssvcsv1.ResourceRecordInputRdataRdataARecord)
|
|
aData, _ := p.Client.NewResourceRecordUpdateInputRdataRdataARecord(*data.Ip)
|
|
updateResourceRecordOptions.SetRdata(aData)
|
|
case "CNAME":
|
|
data, _ := change.PrivateResourceRecord.Rdata[dnssvcsv1.CreateResourceRecordOptions_Type_Cname].(*dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord)
|
|
cnameData, _ := p.Client.NewResourceRecordUpdateInputRdataRdataCnameRecord(*data.Cname)
|
|
updateResourceRecordOptions.SetRdata(cnameData)
|
|
case "TXT":
|
|
data, _ := change.PrivateResourceRecord.Rdata[dnssvcsv1.CreateResourceRecordOptions_Type_Txt].(*dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord)
|
|
txtData, _ := p.Client.NewResourceRecordUpdateInputRdataRdataTxtRecord(*data.Text)
|
|
updateResourceRecordOptions.SetRdata(txtData)
|
|
}
|
|
_, _, err := p.Client.UpdateResourceRecordWithContext(ctx, updateResourceRecordOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to update %s type record named %s: %v", *change.PublicResourceRecord.Type, *change.PublicResourceRecord.Name, err)
|
|
}
|
|
} else {
|
|
updateDNSRecordOptions := &dnsrecordsv1.UpdateDnsRecordOptions{
|
|
DnsrecordIdentifier: &recordID,
|
|
Name: change.PublicResourceRecord.Name,
|
|
Type: change.PublicResourceRecord.Type,
|
|
TTL: change.PublicResourceRecord.TTL,
|
|
Content: change.PublicResourceRecord.Content,
|
|
Proxied: change.PublicResourceRecord.Proxied,
|
|
}
|
|
_, _, err := p.Client.UpdateDNSRecordWithContext(ctx, updateDNSRecordOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to update %s type record named %s: %v", *change.PublicResourceRecord.Type, *change.PublicResourceRecord.Name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *IBMCloudProvider) deleteRecord(ctx context.Context, zoneID, recordID string) {
|
|
if p.privateZone {
|
|
deleteResourceRecordOptions := &dnssvcsv1.DeleteResourceRecordOptions{
|
|
InstanceID: core.StringPtr(p.instanceID),
|
|
DnszoneID: core.StringPtr(zoneID),
|
|
RecordID: core.StringPtr(recordID),
|
|
}
|
|
_, err := p.Client.DeleteResourceRecordWithContext(ctx, deleteResourceRecordOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to delete record %s: %v", recordID, err)
|
|
}
|
|
} else {
|
|
deleteDNSRecordOptions := &dnsrecordsv1.DeleteDnsRecordOptions{
|
|
DnsrecordIdentifier: &recordID,
|
|
}
|
|
_, _, err := p.Client.DeleteDNSRecordWithContext(ctx, deleteDNSRecordOptions)
|
|
if err != nil {
|
|
log.Errorf("failed to delete record %s: %v", recordID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func shouldBeProxied(endpoint *endpoint.Endpoint, proxiedByDefault bool) bool {
|
|
proxied := proxiedByDefault
|
|
|
|
for _, v := range endpoint.ProviderSpecific {
|
|
if v.Name == proxyFilter {
|
|
b, err := strconv.ParseBool(v.Value)
|
|
if err != nil {
|
|
log.Errorf("Failed to parse annotation [%s]: %v", proxyFilter, err)
|
|
} else {
|
|
proxied = b
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if proxyTypeNotSupported[endpoint.RecordType] || strings.Contains(endpoint.DNSName, "*") {
|
|
proxied = false
|
|
}
|
|
return proxied
|
|
}
|
|
|
|
func checkVPCAnnotation(endpoint *endpoint.Endpoint) string {
|
|
var vpc string
|
|
for _, v := range endpoint.ProviderSpecific {
|
|
if v.Name == vpcFilter {
|
|
vpcCrn, err := crn.Parse(v.Value)
|
|
if vpcCrn.ResourceType != "vpc" || err != nil {
|
|
log.Errorf("Failed to parse vpc [%s]: %v", v.Value, err)
|
|
} else {
|
|
vpc = v.Value
|
|
}
|
|
break
|
|
}
|
|
}
|
|
return vpc
|
|
}
|
|
|
|
func isNil(i interface{}) bool {
|
|
if i == nil {
|
|
return true
|
|
}
|
|
switch reflect.TypeOf(i).Kind() {
|
|
case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice:
|
|
return reflect.ValueOf(i).IsNil()
|
|
}
|
|
return false
|
|
}
|