diff --git a/OWNERS b/OWNERS index 978723e6f..dbc97b90d 100644 --- a/OWNERS +++ b/OWNERS @@ -51,6 +51,6 @@ filters: "provider/cloudflare": labels: - provider/cloudflare - "provider/(akamai|alibabacloud|civo|designate|digitalocean|dnsimple|exoscale|gandi|godaddy|ibmcloud|linode|ns1|oci|ovh|pihole|plural|scaleway|tencentcloud|transip|ultradns)": + "provider/(akamai|alibabacloud|civo|designate|digitalocean|dnsimple|exoscale|gandi|godaddy|linode|ns1|oci|ovh|pihole|plural|scaleway|tencentcloud|transip|ultradns)": labels: - provider diff --git a/README.md b/README.md index d3aa4ec6c..aacb436ee 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,6 @@ The following table clarifies the current status of the providers according to t | UltraDNS | Alpha | | | GoDaddy | Alpha | | | Gandi | Alpha | @packi | -| IBMCloud | Alpha | @hughhuangzh | | TencentCloud | Alpha | @Hyzhou | | Plural | Alpha | @michaeljguarino | | Pi-hole | Alpha | @tinyzimmer | @@ -213,7 +212,6 @@ The following tutorials are provided: - [UltraDNS](docs/tutorials/ultradns.md) - [GoDaddy](docs/tutorials/godaddy.md) - [Gandi](docs/tutorials/gandi.md) -- [IBM Cloud](docs/tutorials/ibmcloud.md) - [Nodes as source](docs/sources/nodes.md) - [TencentCloud](docs/tutorials/tencentcloud.md) - [Plural](docs/tutorials/plural.md) diff --git a/controller/execute.go b/controller/execute.go index 6ae1cf5b3..49c83b78b 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -53,7 +53,6 @@ import ( "sigs.k8s.io/external-dns/provider/gandi" "sigs.k8s.io/external-dns/provider/godaddy" "sigs.k8s.io/external-dns/provider/google" - "sigs.k8s.io/external-dns/provider/ibmcloud" "sigs.k8s.io/external-dns/provider/inmemory" "sigs.k8s.io/external-dns/provider/linode" "sigs.k8s.io/external-dns/provider/ns1" @@ -307,8 +306,6 @@ func Execute() { APIVersion: cfg.PiholeApiVersion, }, ) - case "ibmcloud": - p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun) case "plural": p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider) case "tencentcloud": diff --git a/docs/annotations/annotations.md b/docs/annotations/annotations.md index 0c6154b37..c5c172f23 100644 --- a/docs/annotations/annotations.md +++ b/docs/annotations/annotations.md @@ -107,7 +107,6 @@ Some providers define their own annotations. Cloud-specific annotations have key |------------|------------------------------------------------| | AWS | `external-dns.alpha.kubernetes.io/aws-` | | CloudFlare | `external-dns.alpha.kubernetes.io/cloudflare-` | -| IBM Cloud | `external-dns.alpha.kubernetes.io/ibmcloud-` | | Scaleway | `external-dns.alpha.kubernetes.io/scw-` | Additional annotations that are currently implemented only by AWS are: diff --git a/docs/flags.md b/docs/flags.md index 867031aaf..8276d2cec 100644 --- a/docs/flags.md +++ b/docs/flags.md @@ -51,7 +51,7 @@ | `--target-net-filter=TARGET-NET-FILTER` | Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional) | | `--[no-]traefik-disable-legacy` | Disable listeners on Resources under the traefik.containo.us API Group | | `--[no-]traefik-disable-new` | Disable listeners on Resources under the traefik.io API Group | -| `--provider=provider` | The DNS provider where the DNS records will be created (required, options: akamai, alibabacloud, aws, aws-sd, azure, azure-dns, azure-private-dns, civo, cloudflare, coredns, digitalocean, dnsimple, exoscale, gandi, godaddy, google, ibmcloud, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, tencentcloud, transip, ultradns, webhook) | +| `--provider=provider` | The DNS provider where the DNS records will be created (required, options: akamai, alibabacloud, aws, aws-sd, azure, azure-dns, azure-private-dns, civo, cloudflare, coredns, digitalocean, dnsimple, exoscale, gandi, godaddy, google, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, tencentcloud, transip, ultradns, webhook) | | `--provider-cache-time=0s` | The time to cache the DNS provider record list requests. | | `--domain-filter=` | Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional) | | `--exclude-domains=` | Exclude subdomains (optional) | @@ -120,8 +120,6 @@ | `--[no-]ns1-ignoressl` | When using the NS1 provider, specify whether to verify the SSL certificate (default: false) | | `--ns1-min-ttl=NS1-MIN-TTL` | Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this. | | `--digitalocean-api-page-size=50` | Configure the page size used when querying the DigitalOcean API. | -| `--ibmcloud-config-file="/etc/kubernetes/ibmcloud.json"` | When using the IBM Cloud provider, specify the IBM Cloud configuration file (required when --provider=ibmcloud | -| `--[no-]ibmcloud-proxied` | When using the IBM provider, specify if the proxy mode must be enabled (default: disabled) | | `--godaddy-api-key=""` | When using the GoDaddy provider, specify the API Key (required when --provider=godaddy) | | `--godaddy-api-secret=""` | When using the GoDaddy provider, specify the API secret (required when --provider=godaddy) | | `--godaddy-api-ttl=GODADDY-API-TTL` | TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is not provided. | diff --git a/docs/providers.md b/docs/providers.md index 4a9511aa2..a50344798 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -18,7 +18,6 @@ Provider supported configurations | Gandi | n/a | no | 600 | | GoDaddy | n/a | yes | 600 | | Google GCP | n/a | yes | 300 | -| IBMCloud | n/a | yes | 1 | | InMemory | n/a | n/a | n/a | | Linode | n/a | n/a | n/a | | NS1 | n/a | yes | 10 | diff --git a/docs/tutorials/ibmcloud.md b/docs/tutorials/ibmcloud.md deleted file mode 100644 index 6d3033659..000000000 --- a/docs/tutorials/ibmcloud.md +++ /dev/null @@ -1,277 +0,0 @@ -# IBMCloud - -This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using IBMCloud DNS. - -This tutorial uses [IBMCloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-getting-started) for all -IBM Cloud commands and assumes that the Kubernetes cluster was created via IBM Cloud Kubernetes Service and `kubectl` commands -are being run on an orchestration node. - -## Creating a IBMCloud DNS zone - -The IBMCloud provider for ExternalDNS will find suitable zones for domains it manages; it will -not automatically create zones. -For public zone, This tutorial assume that the [IBMCloud Internet Services](https://cloud.ibm.com/catalog/services/internet-services) was provisioned and the [cis cli plugin](https://cloud.ibm.com/docs/cis?topic=cis-cli-plugin-cis-cli) was installed with IBMCloud CLI -For private zone, This tutorial assume that the [IBMCloud DNS Services](https://cloud.ibm.com/catalog/services/dns-services) was provisioned and the [dns cli plugin](https://cloud.ibm.com/docs/dns-svcs?topic=dns-svcs-cli-plugin-dns-services-cli-commands) was installed with IBMCloud CLI - -### Public Zone - -For this tutorial, we create public zone named `example.com` on IBMCloud Internet Services instance `external-dns-public` - -```sh -ibmcloud cis domain-add example.com -i external-dns-public -``` - -Follow [step](https://cloud.ibm.com/docs/cis?topic=cis-getting-started#configure-your-name-servers-with-the-registrar-or-existing-dns-provider) to active your zone - -### Private Zone - -For this tutorial, we create private zone named `example.com` on IBMCloud DNS Services instance `external-dns-private` - -```sh -ibmcloud dns zone-create example.com -i external-dns-private -``` - -## Creating configuration file - -The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named azure.json with content similar to this: - -```json -{ - "apiKey": "1234567890abcdefghijklmnopqrstuvwxyz", - "instanceCrn": "crn:v1:bluemix:public:internet-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:b950da8a-5be6-4691-810e-36388c77b0a3::" -} -``` - -You can create or find the `apiKey` in your ibmcloud IAM --> [API Keys page](https://cloud.ibm.com/iam/apikeys) - -You can find the `instanceCrn` in your service instance details - -Now you can create a file named 'ibmcloud.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret: - -```sh -kubectl create secret generic ibmcloud-config-file --from-file=/local/path/to/ibmcloud.json -``` - -## Deploy ExternalDNS - -Connect your `kubectl` client to the cluster you want to test ExternalDNS with. -Then apply one of the following manifests file to deploy ExternalDNS. - -### Manifest (for clusters without RBAC enabled) - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: external-dns -spec: - strategy: - type: Recreate - selector: - matchLabels: - app: external-dns - template: - metadata: - labels: - app: external-dns - spec: - containers: - - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.17.0 - args: - - --source=service # ingress is also possible - - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. - - --provider=ibmcloud - - --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud - volumeMounts: - - name: ibmcloud-config-file - mountPath: /etc/kubernetes - readOnly: true - volumes: - - name: ibmcloud-config-file - secret: - secretName: ibmcloud-config-file - items: - - key: externaldns-config.json - path: ibmcloud.json -``` - -### Manifest (for clusters with RBAC enabled) - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: external-dns ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: external-dns -rules: -- apiGroups: [""] - resources: ["services","endpoints","pods"] - verbs: ["get","watch","list"] -- apiGroups: ["extensions","networking.k8s.io"] - resources: ["ingresses"] - verbs: ["get","watch","list"] -- apiGroups: [""] - resources: ["nodes"] - verbs: ["list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: external-dns-viewer -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: external-dns -subjects: -- kind: ServiceAccount - name: external-dns - namespace: default ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: external-dns -spec: - strategy: - type: Recreate - selector: - matchLabels: - app: external-dns - template: - metadata: - labels: - app: external-dns - spec: - serviceAccountName: external-dns - containers: - - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.17.0 - args: - - --source=service # ingress is also possible - - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. - - --provider=ibmcloud - - --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud public zone - volumeMounts: - - name: ibmcloud-config-file - mountPath: /etc/kubernetes - readOnly: true - volumes: - - name: ibmcloud-config-file - secret: - secretName: ibmcloud-config-file - items: - - key: externaldns-config.json - path: ibmcloud.json -``` - -## Deploying an Nginx Service - -Create a service file called `nginx.yaml` with the following contents: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx -spec: - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - spec: - containers: - - image: nginx - name: nginx - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx - annotations: - external-dns.alpha.kubernetes.io/hostname: www.example.com - external-dns.alpha.kubernetes.io/ttl: "120" #optional -spec: - selector: - app: nginx - type: LoadBalancer - ports: - - protocol: TCP - port: 80 - targetPort: 80 -``` - -Note the annotation on the service; use the hostname as the IBMCloud DNS zone created above. The annotation may also be a subdomain -of the DNS zone (e.g. 'www.example.com'). - -By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above. -This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300. - -ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation -will cause ExternalDNS to remove the corresponding DNS records. - -Create the deployment and service: - -```sh -kubectl create -f nginx.yaml -``` - -Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service. - -Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize -the IBMCloud DNS records. - -## Verifying IBMCloud DNS records - -Run the following command to view the A records: - -### Public Zone - -```sh -# Get the domain ID with below command on IBMCloud Internet Services instance `external-dns-public` -$ ibmcloud cis domains -i external-dns-public -# Get the records with domain ID -$ ibmcloud cis dns-records DOMAIN_ID -i external-dns-public -``` - -### Private Zone - -```sh -# Get the domain ID with below command on IBMCloud DNS Services instance `external-dns-private` -$ ibmcloud dns zones -i external-dns-private -# Get the records with domain ID -$ ibmcloud dns resource-records ZONE_ID -i external-dns-public -``` - -This should show the external IP address of the service as the A record for your domain. - -## Cleanup - -Now that we have verified that ExternalDNS will automatically manage IBMCloud DNS records, we can delete the tutorial's example: - -```sh -kubectl delete -f nginx.yaml -kubectl delete -f externaldns.yaml -``` - -## Setting proxied records on public zone - -Using the `external-dns.alpha.kubernetes.io/ibmcloud-proxied: "true"` annotation on your ingress or service, you can specify if the proxy feature of IBMCloud public DNS should be enabled for that record. This setting will override the global `--ibmcloud-proxied` setting. - -## Active priviate zone with VPC allocated - -By default, IBMCloud DNS Services don't active your private zone with new zone added. -With External DNS, you can use `external-dns.alpha.kubernetes.io/ibmcloud-vpc: "crn:v1:bluemix:public:is:us-south:a/bcf1865e99742d38d2d5fc3fb80a5496::vpc:r006-74353823-a60d-42e4-97c5-5e2551278435"` annotation on your ingress or service. -It will active your private zone with in specific VPC for that record created in. -This setting won't work if the private zone was active already. - -Note: the annotaion value is the VPC CRN, every IBM Cloud service have a valid CRN. diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index df96f71bd..d1389c3e4 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -199,8 +199,6 @@ type Config struct { GoDaddyTTL int64 GoDaddyOTE bool OCPRouterName string - IBMCloudProxied bool - IBMCloudConfigFile string TencentCloudConfigFile string TencentCloudZoneType string PiholeServer string @@ -293,8 +291,6 @@ var defaultConfig = &Config{ GoogleBatchChangeSize: 1000, GoogleProject: "", GoogleZoneVisibility: "", - IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json", - IBMCloudProxied: false, IgnoreHostnameAnnotation: false, IgnoreIngressRulesSpec: false, IgnoreIngressTLSSpec: false, @@ -495,7 +491,7 @@ func App(cfg *Config) *kingpin.Application { app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew) // Flags related to providers - providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"} + providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"} app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: "+strings.Join(providers, ", ")+")").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, providers...) app.Flag("provider-cache-time", "The time to cache the DNS provider record list requests.").Default(defaultConfig.ProviderCacheTime.String()).DurationVar(&cfg.ProviderCacheTime) app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) @@ -567,8 +563,6 @@ func App(cfg *Config) *kingpin.Application { app.Flag("ns1-ignoressl", "When using the NS1 provider, specify whether to verify the SSL certificate (default: false)").Default(strconv.FormatBool(defaultConfig.NS1IgnoreSSL)).BoolVar(&cfg.NS1IgnoreSSL) app.Flag("ns1-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.NS1MinTTLSeconds) app.Flag("digitalocean-api-page-size", "Configure the page size used when querying the DigitalOcean API.").Default(strconv.Itoa(defaultConfig.DigitalOceanAPIPageSize)).IntVar(&cfg.DigitalOceanAPIPageSize) - app.Flag("ibmcloud-config-file", "When using the IBM Cloud provider, specify the IBM Cloud configuration file (required when --provider=ibmcloud").Default(defaultConfig.IBMCloudConfigFile).StringVar(&cfg.IBMCloudConfigFile) - app.Flag("ibmcloud-proxied", "When using the IBM provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.IBMCloudProxied) // GoDaddy flags app.Flag("godaddy-api-key", "When using the GoDaddy provider, specify the API Key (required when --provider=godaddy)").Default(defaultConfig.GoDaddyAPIKey).StringVar(&cfg.GoDaddyAPIKey) app.Flag("godaddy-api-secret", "When using the GoDaddy provider, specify the API secret (required when --provider=godaddy)").Default(defaultConfig.GoDaddySecretKey).StringVar(&cfg.GoDaddySecretKey) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index fe13da1a5..a41124389 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -125,8 +125,6 @@ var ( RFC2136Host: []string{""}, RFC2136LoadBalancingStrategy: "disabled", OCPRouterName: "default", - IBMCloudProxied: false, - IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json", TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json", TencentCloudZoneType: "", PiholeApiVersion: "5", @@ -242,8 +240,6 @@ var ( RFC2136BatchChangeSize: 100, RFC2136Host: []string{"rfc2136-host1", "rfc2136-host2"}, RFC2136LoadBalancingStrategy: "round-robin", - IBMCloudProxied: true, - IBMCloudConfigFile: "ibmcloud.json", TencentCloudConfigFile: "tencent-cloud.json", TencentCloudZoneType: "private", PiholeApiVersion: "6", @@ -396,8 +392,6 @@ func TestParseFlags(t *testing.T) { "--rfc2136-load-balancing-strategy=round-robin", "--rfc2136-host=rfc2136-host1", "--rfc2136-host=rfc2136-host2", - "--ibmcloud-proxied", - "--ibmcloud-config-file=ibmcloud.json", "--tencent-cloud-config-file=tencent-cloud.json", "--tencent-cloud-zone-type=private", }, @@ -516,8 +510,6 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100", "EXTERNAL_DNS_RFC2136_LOAD_BALANCING_STRATEGY": "round-robin", "EXTERNAL_DNS_RFC2136_HOST": "rfc2136-host1\nrfc2136-host2", - "EXTERNAL_DNS_IBMCLOUD_PROXIED": "1", - "EXTERNAL_DNS_IBMCLOUD_CONFIG_FILE": "ibmcloud.json", "EXTERNAL_DNS_TENCENT_CLOUD_CONFIG_FILE": "tencent-cloud.json", "EXTERNAL_DNS_TENCENT_CLOUD_ZONE_TYPE": "private", }, diff --git a/provider/ibmcloud/ibmcloud.go b/provider/ibmcloud/ibmcloud.go deleted file mode 100644 index 4f3d33a35..000000000 --- a/provider/ibmcloud/ibmcloud.go +++ /dev/null @@ -1,1008 +0,0 @@ -/* -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" - yaml "github.com/goccy/go-yaml" - - 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" - // defaultTTL 1 = automatic - defaultTTL = 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': %w", configFile, err) - } - cfg := &ibmcloudConfig{} - err = yaml.Unmarshal(contents, &cfg) - if err != nil { - return nil, fmt.Errorf("failed to read IBM Cloud config file '%s': %w", 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: %w", 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: %w", 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: %w", 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: %w", 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 - } - - return &IBMCloudProvider{ - Client: client, - source: source, - domainFilter: domainFilter, - zoneIDFilter: zoneIDFilter, - instanceID: cfg.InstanceID, - privateZone: isPrivate, - proxiedByDefault: proxiedByDefault, - DryRun: dryRun, - }, 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 _, et := range changes.Create { - for _, target := range et.Targets { - ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordCreate, et, 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 _, et := range changes.Delete { - for _, target := range et.Targets { - ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, et, 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 _, src := range sources { - vpc = checkVPCAnnotation(src) - if len(vpc) > 0 { - log.Debugf("VPC found: %s", vpc) - break - } - } - - var 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 { - var 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 := defaultTTL - 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 err != nil || vpcCrn.ResourceType != "vpc" { - log.Errorf("Failed to parse vpc [%s]: %v", v.Value, err) - } else { - vpc = v.Value - } - break - } - } - return vpc -} - -// TODO: could be shared function -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() - default: - return false - } -} diff --git a/provider/ibmcloud/ibmcloud_test.go b/provider/ibmcloud/ibmcloud_test.go deleted file mode 100644 index 18440e2f6..000000000 --- a/provider/ibmcloud/ibmcloud_test.go +++ /dev/null @@ -1,953 +0,0 @@ -/* -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" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/IBM/go-sdk-core/v5/core" - "github.com/IBM/networking-go-sdk/dnsrecordsv1" - "github.com/stretchr/testify/require" - - "github.com/IBM/networking-go-sdk/dnssvcsv1" - - . "github.com/onsi/ginkgo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - "sigs.k8s.io/external-dns/endpoint" - "sigs.k8s.io/external-dns/plan" - "sigs.k8s.io/external-dns/provider" -) - -func NewMockIBMCloudDNSAPI() *mockIbmcloudClientInterface { - // Setup public example responses - firstPublicRecord := dnsrecordsv1.DnsrecordDetails{ - ID: core.StringPtr("123"), - Name: core.StringPtr("test.example.com"), - Type: core.StringPtr("A"), - Content: core.StringPtr("1.2.3.4"), - Proxied: core.BoolPtr(true), - TTL: core.Int64Ptr(int64(120)), - } - secondPublicRecord := dnsrecordsv1.DnsrecordDetails{ - ID: core.StringPtr("456"), - Name: core.StringPtr("test.example.com"), - Type: core.StringPtr("TXT"), - Proxied: core.BoolPtr(false), - Content: core.StringPtr("\"heritage=external-dns,external-dns/owner=tower-pdns\""), - TTL: core.Int64Ptr(int64(120)), - } - publicRecordsResult := []dnsrecordsv1.DnsrecordDetails{firstPublicRecord, secondPublicRecord} - publicRecordsResultInfo := &dnsrecordsv1.ResultInfo{ - Page: core.Int64Ptr(int64(1)), - TotalCount: core.Int64Ptr(int64(1)), - } - - publicRecordsResp := &dnsrecordsv1.ListDnsrecordsResp{ - Result: publicRecordsResult, - ResultInfo: publicRecordsResultInfo, - } - // Setup private example responses - firstPrivateZone := dnssvcsv1.Dnszone{ - ID: core.StringPtr("123"), - Name: core.StringPtr("example.com"), - State: core.StringPtr(zoneStatePendingNetwork), - } - - secondPrivateZone := dnssvcsv1.Dnszone{ - ID: core.StringPtr("456"), - Name: core.StringPtr("example1.com"), - State: core.StringPtr(zoneStateActive), - } - privateZones := []dnssvcsv1.Dnszone{firstPrivateZone, secondPrivateZone} - listZonesResp := &dnssvcsv1.ListDnszones{ - Dnszones: privateZones, - } - firstPrivateRecord := dnssvcsv1.ResourceRecord{ - ID: core.StringPtr("123"), - Name: core.StringPtr("test.example.com"), - Type: core.StringPtr("A"), - Rdata: map[string]interface{}{"ip": "1.2.3.4"}, - TTL: core.Int64Ptr(int64(120)), - } - secondPrivateRecord := dnssvcsv1.ResourceRecord{ - ID: core.StringPtr("456"), - Name: core.StringPtr("testCNAME.example.com"), - Type: core.StringPtr("CNAME"), - Rdata: map[string]interface{}{"cname": "test.example.com"}, - TTL: core.Int64Ptr(int64(120)), - } - thirdPrivateRecord := dnssvcsv1.ResourceRecord{ - ID: core.StringPtr("789"), - Name: core.StringPtr("test.example.com"), - Type: core.StringPtr("TXT"), - Rdata: map[string]interface{}{"text": "\"heritage=external-dns,external-dns/owner=tower-pdns\""}, - TTL: core.Int64Ptr(int64(120)), - } - privateRecords := []dnssvcsv1.ResourceRecord{firstPrivateRecord, secondPrivateRecord, thirdPrivateRecord} - privateRecordsResop := &dnssvcsv1.ListResourceRecords{ - ResourceRecords: privateRecords, - Offset: core.Int64Ptr(int64(0)), - TotalCount: core.Int64Ptr(int64(1)), - } - - // Setup record rData - inputARecord := &dnssvcsv1.ResourceRecordInputRdataRdataARecord{ - Ip: core.StringPtr("1.2.3.4"), - } - inputCnameRecord := &dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord{ - Cname: core.StringPtr("test.example.com"), - } - inputTxtRecord := &dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord{ - Text: core.StringPtr("test"), - } - - updateARecord := &dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord{ - Ip: core.StringPtr("1.2.3.4"), - } - updateCnameRecord := &dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord{ - Cname: core.StringPtr("test.example.com"), - } - updateTxtRecord := &dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord{ - Text: core.StringPtr("test"), - } - - // Setup mock services - mockDNSClient := &mockIbmcloudClientInterface{} - mockDNSClient.On("CreateDNSRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("UpdateDNSRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("DeleteDNSRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("ListAllDDNSRecordsWithContext", mock.Anything, mock.Anything).Return(publicRecordsResp, nil, nil) - mockDNSClient.On("ListDnszonesWithContext", mock.Anything, mock.Anything).Return(listZonesResp, nil, nil) - mockDNSClient.On("GetDnszoneWithContext", mock.Anything, mock.Anything).Return(&firstPrivateZone, nil, nil) - mockDNSClient.On("ListResourceRecordsWithContext", mock.Anything, mock.Anything).Return(privateRecordsResop, nil, nil) - mockDNSClient.On("CreatePermittedNetworkWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("CreateResourceRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("DeleteResourceRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("UpdateResourceRecordWithContext", mock.Anything, mock.Anything).Return(nil, nil, nil) - mockDNSClient.On("NewResourceRecordInputRdataRdataARecord", mock.Anything).Return(inputARecord, nil) - mockDNSClient.On("NewResourceRecordInputRdataRdataCnameRecord", mock.Anything).Return(inputCnameRecord, nil) - mockDNSClient.On("NewResourceRecordInputRdataRdataTxtRecord", mock.Anything).Return(inputTxtRecord, nil) - mockDNSClient.On("NewResourceRecordUpdateInputRdataRdataARecord", mock.Anything).Return(updateARecord, nil) - mockDNSClient.On("NewResourceRecordUpdateInputRdataRdataCnameRecord", mock.Anything).Return(updateCnameRecord, nil) - mockDNSClient.On("NewResourceRecordUpdateInputRdataRdataTxtRecord", mock.Anything).Return(updateTxtRecord, nil) - - return mockDNSClient -} - -func newTestIBMCloudProvider(private bool) *IBMCloudProvider { - mockSource := &mockSource{} - endpoints := []*endpoint.Endpoint{ - { - DNSName: "new.example.com", - Targets: endpoint.Targets{"4.3.2.1"}, - ProviderSpecific: endpoint.ProviderSpecific{ - { - Name: "ibmcloud-vpc", - Value: "crn:v1:staging:public:is:us-south:a/0821fa9f9ebcc7b7c9a0d6e9bf9442a4::vpc:be33cdad-9a03-4bfa-82ca-eadb9f1de688", - }, - }, - }, - } - mockSource.On("Endpoints", mock.Anything).Return(endpoints, nil, nil) - - domainFilterTest := endpoint.NewDomainFilter([]string{"example.com"}) - - return &IBMCloudProvider{ - Client: NewMockIBMCloudDNSAPI(), - source: mockSource, - domainFilter: domainFilterTest, - DryRun: false, - instanceID: "test123", - privateZone: private, - } -} - -func TestPublic_Records(t *testing.T) { - p := newTestIBMCloudProvider(false) - endpoints, err := p.Records(context.Background()) - if err != nil { - t.Errorf("Failed to get records: %v", err) - } else { - if len(endpoints) != 2 { - t.Errorf("Incorrect number of records: %d", len(endpoints)) - } - for _, endpoint := range endpoints { - t.Logf("Endpoint for %++v", *endpoint) - } - } -} - -func TestPrivate_Records(t *testing.T) { - p := newTestIBMCloudProvider(true) - endpoints, err := p.Records(context.Background()) - if err != nil { - t.Errorf("Failed to get records: %v", err) - } else { - if len(endpoints) != 3 { - t.Errorf("Incorrect number of records: %d", len(endpoints)) - } - for _, endpoint := range endpoints { - t.Logf("Endpoint for %++v", *endpoint) - } - } -} - -func TestPublic_ApplyChanges(t *testing.T) { - p := newTestIBMCloudProvider(false) - - changes := plan.Changes{ - Create: []*endpoint.Endpoint{ - { - DNSName: "newA.example.com", - RecordType: "A", - RecordTTL: 300, - Targets: endpoint.NewTargets("4.3.2.1"), - ProviderSpecific: endpoint.ProviderSpecific{ - { - Name: "ibmcloud-proxied", - Value: "false", - }, - }, - }, - }, - UpdateOld: []*endpoint.Endpoint{ - { - DNSName: "test.example.com", - RecordType: "A", - RecordTTL: 180, - Targets: endpoint.NewTargets("1.2.3.4"), - ProviderSpecific: endpoint.ProviderSpecific{ - { - Name: "ibmcloud-proxied", - Value: "false", - }, - }, - }, - }, - UpdateNew: []*endpoint.Endpoint{ - { - DNSName: "test.example.com", - RecordType: "A", - RecordTTL: 180, - Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"), - ProviderSpecific: endpoint.ProviderSpecific{ - { - Name: "ibmcloud-proxied", - Value: "true", - }, - }, - }, - }, - Delete: []*endpoint.Endpoint{ - { - DNSName: "test.example.com", - RecordType: "TXT", - RecordTTL: 300, - Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=tower-pdns\""), - }, - }, - } - ctx := context.Background() - err := p.ApplyChanges(ctx, &changes) - if err != nil { - t.Errorf("should not fail, %s", err) - } -} - -func TestPrivate_ApplyChanges(t *testing.T) { - p := newTestIBMCloudProvider(true) - - endpointsCreate, err := p.AdjustEndpoints([]*endpoint.Endpoint{ - { - DNSName: "newA.example.com", - RecordType: "A", - RecordTTL: 120, - Targets: endpoint.NewTargets("4.3.2.1"), - ProviderSpecific: endpoint.ProviderSpecific{ - { - Name: "ibmcloud-vpc", - Value: "crn:v1:staging:public:is:us-south:a/0821fa9f9ebcc7b7c9a0d6e9bf9442a4::vpc:be33cdad-9a03-4bfa-82ca-eadb9f1de688", - }, - }, - }, - { - DNSName: "newCNAME.example.com", - RecordType: "CNAME", - RecordTTL: 180, - Targets: endpoint.NewTargets("newA.example.com"), - }, - { - DNSName: "newTXT.example.com", - RecordType: "TXT", - RecordTTL: 240, - Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=tower-pdns\""), - }, - }) - require.NoError(t, err) - - endpointsUpdate, err := p.AdjustEndpoints([]*endpoint.Endpoint{ - { - DNSName: "test.example.com", - RecordType: "A", - RecordTTL: 180, - Targets: endpoint.NewTargets("1.2.3.4", "5.6.7.8"), - }, - }) - require.NoError(t, err) - - changes := plan.Changes{ - Create: endpointsCreate, - UpdateOld: []*endpoint.Endpoint{ - { - DNSName: "test.example.com", - RecordType: "A", - RecordTTL: 180, - Targets: endpoint.NewTargets("1.2.3.4"), - }, - }, - UpdateNew: endpointsUpdate, - Delete: []*endpoint.Endpoint{ - { - DNSName: "test.example.com", - RecordType: "TXT", - RecordTTL: 300, - Targets: endpoint.NewTargets("\"heritage=external-dns,external-dns/owner=tower-pdns\""), - }, - }, - } - ctx := context.Background() - err = p.ApplyChanges(ctx, &changes) - if err != nil { - t.Errorf("should not fail, %s", err) - } -} - -func TestAdjustEndpoints(t *testing.T) { - p := newTestIBMCloudProvider(false) - endpoints := []*endpoint.Endpoint{ - { - DNSName: "test.example.com", - Targets: endpoint.Targets{"1.2.3.4"}, - RecordType: endpoint.RecordTypeA, - RecordTTL: 300, - Labels: endpoint.Labels{}, - ProviderSpecific: endpoint.ProviderSpecific{ - { - Name: "ibmcloud-proxied", - Value: "1", - }, - }, - }, - } - - ep, err := p.AdjustEndpoints(endpoints) - assert.NoError(t, err) - - assert.Equal(t, endpoint.TTL(0), ep[0].RecordTTL) - assert.Equal(t, "test.example.com", ep[0].DNSName) - proxied, _ := ep[0].GetProviderSpecificProperty("ibmcloud-proxied") - assert.Equal(t, "true", proxied) -} - -func TestPrivateZone_withFilterID(t *testing.T) { - p := newTestIBMCloudProvider(true) - p.zoneIDFilter = provider.NewZoneIDFilter([]string{"123", "456"}) - - zones, err := p.privateZones(context.Background()) - if err != nil { - t.Errorf("should not fail, %s", err) - } else { - if len(zones) != 2 { - t.Errorf("Incorrect number of zones: %d", len(zones)) - } - for _, zone := range zones { - t.Logf("zone %s", *zone.ID) - } - } -} - -func TestPublicConfig_Validate(t *testing.T) { - // mock http server - testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - defer GinkgoRecover() - time.Sleep(0) - - // Set mock response - res.Header().Set("Content-type", "application/json") - res.WriteHeader(http.StatusOK) - fmt.Fprintf(res, "%s", `{"success": true, "errors": [["Errors"]], "messages": [["Messages"]], "result": [{"id": "123", "created_on": "2014-01-01T05:20:00.12345Z", "modified_on": "2014-01-01T05:20:00.12345Z", "name": "example.com", "original_registrar": "GoDaddy", "original_dnshost": "NameCheap", "status": "active", "paused": false, "original_name_servers": ["ns1.originaldnshost.com"], "name_servers": ["ns001.name.cloud.ibm.com"]}], "result_info": {"page": 1, "per_page": 20, "count": 1, "total_count": 2000}}`) - })) - zoneIDFilterTest := provider.NewZoneIDFilter([]string{"123"}) - domainFilterTest := endpoint.NewDomainFilter([]string{"example.com"}) - cfg := &ibmcloudConfig{ - Endpoint: testServer.URL, - CRN: "crn:v1:bluemix:public:internet-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:a6338168-9510-4951-9d67-425612de96f0::", - } - crn := cfg.CRN - authenticator := &core.NoAuthAuthenticator{} - service, isPrivate, err := cfg.Validate(authenticator, domainFilterTest, provider.NewZoneIDFilter([]string{""})) - assert.NoError(t, err) - assert.False(t, isPrivate) - assert.Equal(t, crn, *service.publicRecordsService.Crn) - assert.Equal(t, "123", *service.publicRecordsService.ZoneIdentifier) - - service, isPrivate, err = cfg.Validate(authenticator, endpoint.NewDomainFilter([]string{""}), zoneIDFilterTest) - assert.NoError(t, err) - assert.False(t, isPrivate) - assert.Equal(t, crn, *service.publicRecordsService.Crn) - assert.Equal(t, "123", *service.publicRecordsService.ZoneIdentifier) - - testServer.Close() -} - -func TestPrivateConfig_Validate(t *testing.T) { - zoneIDFilterTest := provider.NewZoneIDFilter([]string{"123"}) - domainFilterTest := endpoint.NewDomainFilter([]string{"example.com"}) - authenticator := &core.NoAuthAuthenticator{} - cfg := &ibmcloudConfig{ - Endpoint: "XXX", - CRN: "crn:v1:bluemix:public:dns-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:a6338168-9510-4951-9d67-425612de96f0::", - } - _, isPrivate, err := cfg.Validate(authenticator, domainFilterTest, zoneIDFilterTest) - assert.NoError(t, err) - assert.True(t, isPrivate) -} - -// mockIbmcloudClientInterface is an autogenerated mock type for the ibmcloudClient type -type mockIbmcloudClientInterface struct { - mock.Mock -} - -// CreateDNSRecordWithContext provides a mock function with given fields: ctx, createDnsRecordOptions -func (_m *mockIbmcloudClientInterface) CreateDNSRecordWithContext(ctx context.Context, createDnsRecordOptions *dnsrecordsv1.CreateDnsRecordOptions) (*dnsrecordsv1.DnsrecordResp, *core.DetailedResponse, error) { - ret := _m.Called(ctx, createDnsRecordOptions) - - var r0 *dnsrecordsv1.DnsrecordResp - if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.CreateDnsRecordOptions) *dnsrecordsv1.DnsrecordResp); ok { - r0 = rf(ctx, createDnsRecordOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnsrecordsv1.DnsrecordResp) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.CreateDnsRecordOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, createDnsRecordOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.CreateDnsRecordOptions) error); ok { - r2 = rf(ctx, createDnsRecordOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// CreatePermittedNetworkWithContext provides a mock function with given fields: ctx, createPermittedNetworkOptions -func (_m *mockIbmcloudClientInterface) CreatePermittedNetworkWithContext(ctx context.Context, createPermittedNetworkOptions *dnssvcsv1.CreatePermittedNetworkOptions) (*dnssvcsv1.PermittedNetwork, *core.DetailedResponse, error) { - ret := _m.Called(ctx, createPermittedNetworkOptions) - - var r0 *dnssvcsv1.PermittedNetwork - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.CreatePermittedNetworkOptions) *dnssvcsv1.PermittedNetwork); ok { - r0 = rf(ctx, createPermittedNetworkOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.PermittedNetwork) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.CreatePermittedNetworkOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, createPermittedNetworkOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.CreatePermittedNetworkOptions) error); ok { - r2 = rf(ctx, createPermittedNetworkOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// CreateResourceRecordWithContext provides a mock function with given fields: ctx, createResourceRecordOptions -func (_m *mockIbmcloudClientInterface) CreateResourceRecordWithContext(ctx context.Context, createResourceRecordOptions *dnssvcsv1.CreateResourceRecordOptions) (*dnssvcsv1.ResourceRecord, *core.DetailedResponse, error) { - ret := _m.Called(ctx, createResourceRecordOptions) - - var r0 *dnssvcsv1.ResourceRecord - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.CreateResourceRecordOptions) *dnssvcsv1.ResourceRecord); ok { - r0 = rf(ctx, createResourceRecordOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecord) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.CreateResourceRecordOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, createResourceRecordOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.CreateResourceRecordOptions) error); ok { - r2 = rf(ctx, createResourceRecordOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// DeleteDNSRecordWithContext provides a mock function with given fields: ctx, deleteDnsRecordOptions -func (_m *mockIbmcloudClientInterface) DeleteDNSRecordWithContext(ctx context.Context, deleteDnsRecordOptions *dnsrecordsv1.DeleteDnsRecordOptions) (*dnsrecordsv1.DeleteDnsrecordResp, *core.DetailedResponse, error) { - ret := _m.Called(ctx, deleteDnsRecordOptions) - - var r0 *dnsrecordsv1.DeleteDnsrecordResp - if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.DeleteDnsRecordOptions) *dnsrecordsv1.DeleteDnsrecordResp); ok { - r0 = rf(ctx, deleteDnsRecordOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnsrecordsv1.DeleteDnsrecordResp) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.DeleteDnsRecordOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, deleteDnsRecordOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.DeleteDnsRecordOptions) error); ok { - r2 = rf(ctx, deleteDnsRecordOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// DeleteResourceRecordWithContext provides a mock function with given fields: ctx, deleteResourceRecordOptions -func (_m *mockIbmcloudClientInterface) DeleteResourceRecordWithContext(ctx context.Context, deleteResourceRecordOptions *dnssvcsv1.DeleteResourceRecordOptions) (*core.DetailedResponse, error) { - ret := _m.Called(ctx, deleteResourceRecordOptions) - - var r0 *core.DetailedResponse - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.DeleteResourceRecordOptions) *core.DetailedResponse); ok { - r0 = rf(ctx, deleteResourceRecordOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*core.DetailedResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.DeleteResourceRecordOptions) error); ok { - r1 = rf(ctx, deleteResourceRecordOptions) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDnszoneWithContext provides a mock function with given fields: ctx, getDnszoneOptions -func (_m *mockIbmcloudClientInterface) GetDnszoneWithContext(ctx context.Context, getDnszoneOptions *dnssvcsv1.GetDnszoneOptions) (*dnssvcsv1.Dnszone, *core.DetailedResponse, error) { - ret := _m.Called(ctx, getDnszoneOptions) - - var r0 *dnssvcsv1.Dnszone - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.GetDnszoneOptions) *dnssvcsv1.Dnszone); ok { - r0 = rf(ctx, getDnszoneOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.Dnszone) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.GetDnszoneOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, getDnszoneOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.GetDnszoneOptions) error); ok { - r2 = rf(ctx, getDnszoneOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// ListAllDDNSRecordsWithContext provides a mock function with given fields: ctx, listAllDnsRecordsOptions -func (_m *mockIbmcloudClientInterface) ListAllDDNSRecordsWithContext(ctx context.Context, listAllDnsRecordsOptions *dnsrecordsv1.ListAllDnsRecordsOptions) (*dnsrecordsv1.ListDnsrecordsResp, *core.DetailedResponse, error) { - ret := _m.Called(ctx, listAllDnsRecordsOptions) - - var r0 *dnsrecordsv1.ListDnsrecordsResp - if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.ListAllDnsRecordsOptions) *dnsrecordsv1.ListDnsrecordsResp); ok { - r0 = rf(ctx, listAllDnsRecordsOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnsrecordsv1.ListDnsrecordsResp) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.ListAllDnsRecordsOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, listAllDnsRecordsOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.ListAllDnsRecordsOptions) error); ok { - r2 = rf(ctx, listAllDnsRecordsOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// ListDnszonesWithContext provides a mock function with given fields: ctx, listDnszonesOptions -func (_m *mockIbmcloudClientInterface) ListDnszonesWithContext(ctx context.Context, listDnszonesOptions *dnssvcsv1.ListDnszonesOptions) (*dnssvcsv1.ListDnszones, *core.DetailedResponse, error) { - ret := _m.Called(ctx, listDnszonesOptions) - - var r0 *dnssvcsv1.ListDnszones - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.ListDnszonesOptions) *dnssvcsv1.ListDnszones); ok { - r0 = rf(ctx, listDnszonesOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ListDnszones) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.ListDnszonesOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, listDnszonesOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.ListDnszonesOptions) error); ok { - r2 = rf(ctx, listDnszonesOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// ListResourceRecordsWithContext provides a mock function with given fields: ctx, listResourceRecordsOptions -func (_m *mockIbmcloudClientInterface) ListResourceRecordsWithContext(ctx context.Context, listResourceRecordsOptions *dnssvcsv1.ListResourceRecordsOptions) (*dnssvcsv1.ListResourceRecords, *core.DetailedResponse, error) { - ret := _m.Called(ctx, listResourceRecordsOptions) - - var r0 *dnssvcsv1.ListResourceRecords - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.ListResourceRecordsOptions) *dnssvcsv1.ListResourceRecords); ok { - r0 = rf(ctx, listResourceRecordsOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ListResourceRecords) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.ListResourceRecordsOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, listResourceRecordsOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.ListResourceRecordsOptions) error); ok { - r2 = rf(ctx, listResourceRecordsOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// NewResourceRecordInputRdataRdataARecord provides a mock function with given fields: ip -func (_m *mockIbmcloudClientInterface) NewResourceRecordInputRdataRdataARecord(ip string) (*dnssvcsv1.ResourceRecordInputRdataRdataARecord, error) { - ret := _m.Called(ip) - - var r0 *dnssvcsv1.ResourceRecordInputRdataRdataARecord - if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordInputRdataRdataARecord); ok { - r0 = rf(ip) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordInputRdataRdataARecord) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(ip) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewResourceRecordInputRdataRdataCnameRecord provides a mock function with given fields: cname -func (_m *mockIbmcloudClientInterface) NewResourceRecordInputRdataRdataCnameRecord(cname string) (*dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord, error) { - ret := _m.Called(cname) - - var r0 *dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord - if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord); ok { - r0 = rf(cname) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordInputRdataRdataCnameRecord) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(cname) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewResourceRecordInputRdataRdataTxtRecord provides a mock function with given fields: text -func (_m *mockIbmcloudClientInterface) NewResourceRecordInputRdataRdataTxtRecord(text string) (*dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord, error) { - ret := _m.Called(text) - - var r0 *dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord - if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord); ok { - r0 = rf(text) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordInputRdataRdataTxtRecord) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(text) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewResourceRecordUpdateInputRdataRdataARecord provides a mock function with given fields: ip -func (_m *mockIbmcloudClientInterface) NewResourceRecordUpdateInputRdataRdataARecord(ip string) (*dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord, error) { - ret := _m.Called(ip) - - var r0 *dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord - if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord); ok { - r0 = rf(ip) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordUpdateInputRdataRdataARecord) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(ip) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewResourceRecordUpdateInputRdataRdataCnameRecord provides a mock function with given fields: cname -func (_m *mockIbmcloudClientInterface) NewResourceRecordUpdateInputRdataRdataCnameRecord(cname string) (*dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord, error) { - ret := _m.Called(cname) - - var r0 *dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord - if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord); ok { - r0 = rf(cname) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordUpdateInputRdataRdataCnameRecord) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(cname) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewResourceRecordUpdateInputRdataRdataTxtRecord provides a mock function with given fields: text -func (_m *mockIbmcloudClientInterface) NewResourceRecordUpdateInputRdataRdataTxtRecord(text string) (*dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord, error) { - ret := _m.Called(text) - - var r0 *dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord - if rf, ok := ret.Get(0).(func(string) *dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord); ok { - r0 = rf(text) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecordUpdateInputRdataRdataTxtRecord) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(text) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDNSRecordWithContext provides a mock function with given fields: ctx, updateDnsRecordOptions -func (_m *mockIbmcloudClientInterface) UpdateDNSRecordWithContext(ctx context.Context, updateDnsRecordOptions *dnsrecordsv1.UpdateDnsRecordOptions) (*dnsrecordsv1.DnsrecordResp, *core.DetailedResponse, error) { - ret := _m.Called(ctx, updateDnsRecordOptions) - - var r0 *dnsrecordsv1.DnsrecordResp - if rf, ok := ret.Get(0).(func(context.Context, *dnsrecordsv1.UpdateDnsRecordOptions) *dnsrecordsv1.DnsrecordResp); ok { - r0 = rf(ctx, updateDnsRecordOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnsrecordsv1.DnsrecordResp) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnsrecordsv1.UpdateDnsRecordOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, updateDnsRecordOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnsrecordsv1.UpdateDnsRecordOptions) error); ok { - r2 = rf(ctx, updateDnsRecordOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// UpdateResourceRecordWithContext provides a mock function with given fields: ctx, updateResourceRecordOptions -func (_m *mockIbmcloudClientInterface) UpdateResourceRecordWithContext(ctx context.Context, updateResourceRecordOptions *dnssvcsv1.UpdateResourceRecordOptions) (*dnssvcsv1.ResourceRecord, *core.DetailedResponse, error) { - ret := _m.Called(ctx, updateResourceRecordOptions) - - var r0 *dnssvcsv1.ResourceRecord - if rf, ok := ret.Get(0).(func(context.Context, *dnssvcsv1.UpdateResourceRecordOptions) *dnssvcsv1.ResourceRecord); ok { - r0 = rf(ctx, updateResourceRecordOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dnssvcsv1.ResourceRecord) - } - } - - var r1 *core.DetailedResponse - if rf, ok := ret.Get(1).(func(context.Context, *dnssvcsv1.UpdateResourceRecordOptions) *core.DetailedResponse); ok { - r1 = rf(ctx, updateResourceRecordOptions) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*core.DetailedResponse) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, *dnssvcsv1.UpdateResourceRecordOptions) error); ok { - r2 = rf(ctx, updateResourceRecordOptions) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -type mockSource struct { - mock.Mock -} - -// Endpoints provides a mock function with given fields: ctx -func (_m *mockSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { - ret := _m.Called(ctx) - - var r0 []*endpoint.Endpoint - if rf, ok := ret.Get(0).(func(context.Context) []*endpoint.Endpoint); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*endpoint.Endpoint) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddEventHandler provides a mock function with given fields: _a0, _a1 -func (_m *mockSource) AddEventHandler(_a0 context.Context, _a1 func()) { - _m.Called(_a0, _a1) -} diff --git a/source/annotations/annotations.go b/source/annotations/annotations.go index 5c98516e3..bd21ca2bc 100644 --- a/source/annotations/annotations.go +++ b/source/annotations/annotations.go @@ -26,7 +26,6 @@ const ( AWSPrefix = "external-dns.alpha.kubernetes.io/aws-" SCWPrefix = "external-dns.alpha.kubernetes.io/scw-" - IBMCloudPrefix = "external-dns.alpha.kubernetes.io/ibmcloud-" WebhookPrefix = "external-dns.alpha.kubernetes.io/webhook-" CloudflarePrefix = "external-dns.alpha.kubernetes.io/cloudflare-" diff --git a/source/annotations/provider_specific.go b/source/annotations/provider_specific.go index 99d932c31..93612ff09 100644 --- a/source/annotations/provider_specific.go +++ b/source/annotations/provider_specific.go @@ -45,12 +45,6 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid Name: fmt.Sprintf("scw/%s", attr), Value: v, }) - } else if strings.HasPrefix(k, IBMCloudPrefix) { - attr := strings.TrimPrefix(k, IBMCloudPrefix) - providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{ - Name: fmt.Sprintf("ibmcloud-%s", attr), - Value: v, - }) } else if strings.HasPrefix(k, WebhookPrefix) { // Support for wildcard annotations for webhook providers attr := strings.TrimPrefix(k, WebhookPrefix) diff --git a/source/annotations/provider_specific_test.go b/source/annotations/provider_specific_test.go index e9fd76334..c13d123cc 100644 --- a/source/annotations/provider_specific_test.go +++ b/source/annotations/provider_specific_test.go @@ -300,19 +300,6 @@ func TestGetProviderSpecificIdentifierAnnotations(t *testing.T) { }, expectedIdentifier: "id1", }, - { - title: "ibmcloud- provider specific annotations are set correctly", - annotations: map[string]string{ - "external-dns.alpha.kubernetes.io/ibmcloud-annotation-1": "value 1", - SetIdentifierKey: "id1", - "external-dns.alpha.kubernetes.io/ibmcloud-annotation-2": "value 2", - }, - expectedResult: map[string]string{ - "ibmcloud-annotation-1": "value 1", - "ibmcloud-annotation-2": "value 2", - }, - expectedIdentifier: "id1", - }, { title: "webhook- provider specific annotations are set correctly", annotations: map[string]string{