From 4f6ab703c164bab822dfbfd8db4ffcb45043557f Mon Sep 17 00:00:00 2001 From: Rafal Korepta Date: Wed, 9 Mar 2022 22:20:14 +0100 Subject: [PATCH] Revert hetzner plugin --- README.md | 3 - docs/tutorials/hetzner.md | 191 ------------------- go.mod | 1 - go.sum | 2 - main.go | 3 - pkg/apis/externaldns/types.go | 2 +- provider/hetzner/hetzner.go | 259 -------------------------- provider/hetzner/hetzner_test.go | 305 ------------------------------- 8 files changed, 1 insertion(+), 765 deletions(-) delete mode 100644 docs/tutorials/hetzner.md delete mode 100644 provider/hetzner/hetzner.go delete mode 100644 provider/hetzner/hetzner_test.go diff --git a/README.md b/README.md index 84d174824..5b2fea0b3 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ ExternalDNS' allows you to keep selected zones (via `--domain-filter`) synchroni * [CloudFlare](https://www.cloudflare.com/dns) * [RcodeZero](https://www.rcodezero.at/) * [DigitalOcean](https://www.digitalocean.com/products/networking) -* [Hetzner](https://hetzner.com/) * [DNSimple](https://dnsimple.com/) * [Infoblox](https://www.infoblox.com/products/dns/) * [Dyn](https://dyn.com/dns/) @@ -89,7 +88,6 @@ The following table clarifies the current status of the providers according to t | CloudFlare | Beta | | | RcodeZero | Alpha | | | DigitalOcean | Alpha | | -| Hetzner | Alpha | @21h | | DNSimple | Alpha | | | Infoblox | Alpha | @saileshgiri | | Dyn | Alpha | | @@ -147,7 +145,6 @@ The following tutorials are provided: * [BlueCat](docs/tutorials/bluecat.md) * [CoreDNS](docs/tutorials/coredns.md) * [DigitalOcean](docs/tutorials/digitalocean.md) -* [Hetzner](docs/tutorials/hetzner.md) * [DNSimple](docs/tutorials/dnsimple.md) * [Dyn](docs/tutorials/dyn.md) * [Exoscale](docs/tutorials/exoscale.md) diff --git a/docs/tutorials/hetzner.md b/docs/tutorials/hetzner.md deleted file mode 100644 index dbae14e98..000000000 --- a/docs/tutorials/hetzner.md +++ /dev/null @@ -1,191 +0,0 @@ -# Setting up ExternalDNS for Services on Hetzner DNS - -This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Hetzner DNS. - -Make sure to use **>=0.7.6** version of ExternalDNS for this tutorial. - -## Creating a Hetzner DNS zone - -If you want to learn about how to use Hetzner's DNS service read the following tutorial series: - -[An Introduction to Managing DNS](https://wiki.hetzner.de/index.php/DNS_Overview), and [Add a new DNS zone](https://wiki.hetzner.de/index.php/Getting_started). - -Create a new DNS zone where you want to create your records in. Let's use `example.com` as an example here. - -## Creating Hetzner Credentials - -Generate a new personal token by going to [the API settings](https://dns.hetzner.com/settings/api-token) or follow [Generating an API access token](https://wiki.hetzner.de/index.php/API_access_token) if you need more information. Give the token a name and choose read and write access. The token needs to be passed to ExternalDNS so make a note of it for later use. - -The environment variable `HETZNER_TOKEN` will be needed to run ExternalDNS with Hetzner. - -## 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: - replicas: 1 - selector: - matchLabels: - app: external-dns - strategy: - type: Recreate - template: - metadata: - labels: - app: external-dns - spec: - containers: - - name: external-dns - image: k8s.gcr.io/external-dns/external-dns:v0.7.6 - 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=hetzner - env: - - name: HETZNER_TOKEN - value: "YOUR_HETZNER_DNS_API_KEY" -``` - -### 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: - replicas: 1 - selector: - matchLabels: - app: external-dns - strategy: - type: Recreate - template: - metadata: - labels: - app: external-dns - spec: - serviceAccountName: external-dns - containers: - - name: external-dns - image: k8s.gcr.io/external-dns/external-dns:v0.7.6 - 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=hetzner - env: - - name: HETZNER_TOKEN - value: "YOUR_HETZNER_DNS_API_KEY" -``` - - -## 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: - replicas: 1 - 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: my-app.example.com -spec: - selector: - app: nginx - type: LoadBalancer - ports: - - protocol: TCP - port: 80 - targetPort: 80 -``` - -Note the annotation on the service; use the same hostname as the Hetzner DNS zone created above. - -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: - -```console -$ 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 Hetzner DNS records. - -## Verifying Hetzner DNS records - -Check your [Hetzner DNS UI](https://dns.hetzner.com/) to view the records for your Hetzner DNS zone. - -Click on the zone for the one created above if a different domain was used. - -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 Hetzner DNS records, we can delete the tutorial's example: - -``` -$ kubectl delete service -f nginx.yaml -$ kubectl delete service -f externaldns.yaml -``` diff --git a/go.mod b/go.mod index f7bf2c67e..11398fb0e 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.17 require ( cloud.google.com/go/compute v1.2.0 - git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d github.com/Azure/azure-sdk-for-go v61.5.0+incompatible github.com/Azure/go-autorest/autorest v0.11.21 github.com/Azure/go-autorest/autorest/adal v0.9.16 diff --git a/go.sum b/go.sum index 6898f0a01..b4e99dcff 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk= code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d h1:d6sdozgfqtgaOhjUn++lbo5siX3HELjcOUnbtrvVQi4= -git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d/go.mod h1:n26Twiii5jhkMC+Ocz/s8R73cBBcXRIwyTqQ+6bOZGo= git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM= github.com/0x4c6565/genie v1.0.0/go.mod h1:fDOjW0hFamMWOIkh4irf2D/TZpXXWMFtpP8MfgK0N3c= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= diff --git a/main.go b/main.go index fd3b48465..a4f5ed00b 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,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/hetzner" "sigs.k8s.io/external-dns/provider/infoblox" "sigs.k8s.io/external-dns/provider/inmemory" "sigs.k8s.io/external-dns/provider/linode" @@ -227,8 +226,6 @@ func main() { p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun) case "digitalocean": p, err = digitalocean.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun, cfg.DigitalOceanAPIPageSize) - case "hetzner": - p, err = hetzner.NewHetznerProvider(ctx, domainFilter, cfg.DryRun) case "ovh": p, err = ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHApiRateLimit, cfg.DryRun) case "linode": diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 3b06e3a00..f559c84d5 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -397,7 +397,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets) // Flags related to providers - app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi, safedns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi", "safedns") + app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi, safedns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi", "safedns") app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains) app.Flag("regex-domain-filter", "Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional)").Default(defaultConfig.RegexDomainFilter.String()).RegexpVar(&cfg.RegexDomainFilter) diff --git a/provider/hetzner/hetzner.go b/provider/hetzner/hetzner.go deleted file mode 100644 index 5f367bb2c..000000000 --- a/provider/hetzner/hetzner.go +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2020 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 hetzner - -import ( - "context" - "errors" - "os" - "strings" - - hclouddns "git.blindage.org/21h/hcloud-dns" - log "github.com/sirupsen/logrus" - - "sigs.k8s.io/external-dns/endpoint" - "sigs.k8s.io/external-dns/plan" - "sigs.k8s.io/external-dns/provider" -) - -const ( - hetznerCreate = "CREATE" - hetznerDelete = "DELETE" - hetznerUpdate = "UPDATE" - hetznerTTL = 600 -) - -type HetznerChanges struct { - Action string - ZoneID string - ZoneName string - ResourceRecordSet hclouddns.HCloudRecord -} - -type HetznerProvider struct { - provider.BaseProvider - Client hclouddns.HCloudClientAdapter - domainFilter endpoint.DomainFilter - DryRun bool -} - -func NewHetznerProvider(ctx context.Context, domainFilter endpoint.DomainFilter, dryRun bool) (*HetznerProvider, error) { - token, ok := os.LookupEnv("HETZNER_TOKEN") - if !ok { - return nil, errors.New("no environment variable HETZNER_TOKEN provided") - } - - client := hclouddns.New(token) - - provider := &HetznerProvider{ - Client: client, - domainFilter: domainFilter, - DryRun: dryRun, - } - return provider, nil -} - -func (p *HetznerProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { - zones, err := p.Client.GetZones(hclouddns.HCloudGetZonesParams{}) - if err != nil { - return nil, err - } - endpoints := []*endpoint.Endpoint{} - for _, zone := range zones.Zones { - records, err := p.Client.GetRecords(hclouddns.HCloudGetRecordsParams{ZoneID: zone.ID}) - if err != nil { - return nil, err - } - - for _, r := range records.Records { - if provider.SupportedRecordType(string(r.RecordType)) { - name := r.Name + "." + zone.Name - - if r.Name == "@" { - name = zone.Name - } - - endpoints = append(endpoints, endpoint.NewEndpoint(name, string(r.RecordType), r.Value)) - } - } - } - - return endpoints, nil -} - -func (p *HetznerProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { - combinedChanges := make([]*HetznerChanges, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) - - combinedChanges = append(combinedChanges, p.newHetznerChanges(hetznerCreate, changes.Create)...) - combinedChanges = append(combinedChanges, p.newHetznerChanges(hetznerUpdate, changes.UpdateNew)...) - combinedChanges = append(combinedChanges, p.newHetznerChanges(hetznerDelete, changes.Delete)...) - - return p.submitChanges(ctx, combinedChanges) -} - -func (p *HetznerProvider) submitChanges(ctx context.Context, changes []*HetznerChanges) error { - if len(changes) == 0 { - log.Infof("All records are already up to date") - return nil - } - - zones, err := p.Client.GetZones(hclouddns.HCloudGetZonesParams{}) - if err != nil { - return err - } - - zoneChanges := p.seperateChangesByZone(zones.Zones, changes) - - for _, changes := range zoneChanges { - for _, change := range changes { - // Prepare record name - recordName := strings.TrimSuffix(change.ResourceRecordSet.Name, "."+change.ZoneName) - if recordName == change.ZoneName { - recordName = "@" - } - if change.ResourceRecordSet.RecordType == hclouddns.CNAME && !strings.HasSuffix(change.ResourceRecordSet.Value, ".") { - change.ResourceRecordSet.Value += "." - } - change.ResourceRecordSet.Name = recordName - - // Get ID of record if not create operation - if change.Action != hetznerCreate { - allRecords, err := p.Client.GetRecords(hclouddns.HCloudGetRecordsParams{ZoneID: change.ZoneID}) - if err != nil { - return err - } - for _, record := range allRecords.Records { - if record.Name == change.ResourceRecordSet.Name && record.RecordType == change.ResourceRecordSet.RecordType { - change.ResourceRecordSet.ID = record.ID - break - } - } - } - - logMessage := "Changing record" - if p.DryRun { - logMessage = "Would change record" - } - log.WithFields(log.Fields{ - "id": change.ResourceRecordSet.ID, - "record": change.ResourceRecordSet.Name, - "type": change.ResourceRecordSet.RecordType, - "value": change.ResourceRecordSet.Value, - "ttl": change.ResourceRecordSet.TTL, - "action": change.Action, - "zone": change.ZoneName, - "zone_id": change.ZoneID, - }).Info(logMessage) - if p.DryRun { - continue - } - - switch change.Action { - case hetznerCreate: - record := hclouddns.HCloudRecord{ - RecordType: change.ResourceRecordSet.RecordType, - ZoneID: change.ZoneID, - Name: change.ResourceRecordSet.Name, - Value: change.ResourceRecordSet.Value, - TTL: change.ResourceRecordSet.TTL, - } - answer, err := p.Client.CreateRecord(record) - if err != nil { - log.WithFields(log.Fields{ - "Code": answer.Error.Code, - "Message": answer.Error.Message, - "Record name": answer.Record.Name, - "Record type": answer.Record.RecordType, - "Record value": answer.Record.Value, - }).Warning("Create problem") - return err - } - case hetznerDelete: - answer, err := p.Client.DeleteRecord(change.ResourceRecordSet.ID) - if err != nil { - log.WithFields(log.Fields{ - "Code": answer.Error.Code, - "Message": answer.Error.Message, - }).Warning("Delete problem") - return err - } - case hetznerUpdate: - record := hclouddns.HCloudRecord{ - RecordType: change.ResourceRecordSet.RecordType, - ZoneID: change.ZoneID, - Name: change.ResourceRecordSet.Name, - Value: change.ResourceRecordSet.Value, - TTL: change.ResourceRecordSet.TTL, - ID: change.ResourceRecordSet.ID, - } - answer, err := p.Client.UpdateRecord(record) - if err != nil { - log.WithFields(log.Fields{ - "Code": answer.Error.Code, - "Message": answer.Error.Message, - "Record name": answer.Record.Name, - "Record type": answer.Record.RecordType, - "Record value": answer.Record.Value, - }).Warning("Update problem") - return err - } - } - } - } - - return nil -} - -func (p *HetznerProvider) newHetznerChanges(action string, endpoints []*endpoint.Endpoint) []*HetznerChanges { - changes := make([]*HetznerChanges, 0, len(endpoints)) - ttl := hetznerTTL - for _, e := range endpoints { - if e.RecordTTL.IsConfigured() { - ttl = int(e.RecordTTL) - } - change := &HetznerChanges{ - Action: action, - ResourceRecordSet: hclouddns.HCloudRecord{ - RecordType: hclouddns.RecordType(e.RecordType), - Name: e.DNSName, - Value: e.Targets[0], - TTL: ttl, - }, - } - changes = append(changes, change) - } - return changes -} - -func (p *HetznerProvider) seperateChangesByZone(zones []hclouddns.HCloudZone, changes []*HetznerChanges) map[string][]*HetznerChanges { - change := make(map[string][]*HetznerChanges) - zoneNameID := provider.ZoneIDName{} - - for _, z := range zones { - zoneNameID.Add(z.ID, z.Name) - change[z.ID] = []*HetznerChanges{} - } - - for _, c := range changes { - zoneID, zoneName := zoneNameID.FindZone(c.ResourceRecordSet.Name) - if zoneName == "" { - log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.ResourceRecordSet.Name) - continue - } - c.ZoneName = zoneName - c.ZoneID = zoneID - change[zoneID] = append(change[zoneID], c) - } - return change -} diff --git a/provider/hetzner/hetzner_test.go b/provider/hetzner/hetzner_test.go deleted file mode 100644 index 9ef5c92e8..000000000 --- a/provider/hetzner/hetzner_test.go +++ /dev/null @@ -1,305 +0,0 @@ -/* -Copyright 2020 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 hetzner - -import ( - "context" - "fmt" - "github.com/maxatome/go-testdeep/td" - "os" - "reflect" - "testing" - - hclouddns "git.blindage.org/21h/hcloud-dns" - "github.com/stretchr/testify/assert" - - "sigs.k8s.io/external-dns/endpoint" - "sigs.k8s.io/external-dns/plan" -) - -type mockHCloudClientAdapter interface { - GetZone(ID string) (hclouddns.HCloudAnswerGetZone, error) - GetZones(params hclouddns.HCloudGetZonesParams) (hclouddns.HCloudAnswerGetZones, error) - UpdateZone(zone hclouddns.HCloudZone) (hclouddns.HCloudAnswerGetZone, error) - DeleteZone(ID string) (hclouddns.HCloudAnswerDeleteZone, error) - CreateZone(zone hclouddns.HCloudZone) (hclouddns.HCloudAnswerGetZone, error) - ImportZoneString(zoneID string, zonePlainText string) (hclouddns.HCloudAnswerGetZone, error) - ExportZoneToString(zoneID string) (hclouddns.HCloudAnswerGetZonePlainText, error) - ValidateZoneString(zonePlainText string) (hclouddns.HCloudAnswerZoneValidate, error) - GetRecord(ID string) (hclouddns.HCloudAnswerGetRecord, error) - GetRecords(params hclouddns.HCloudGetRecordsParams) (hclouddns.HCloudAnswerGetRecords, error) - UpdateRecord(record hclouddns.HCloudRecord) (hclouddns.HCloudAnswerGetRecord, error) - DeleteRecord(ID string) (hclouddns.HCloudAnswerDeleteRecord, error) - CreateRecord(record hclouddns.HCloudRecord) (hclouddns.HCloudAnswerGetRecord, error) - CreateRecordBulk(record []hclouddns.HCloudRecord) (hclouddns.HCloudAnswerCreateRecords, error) - UpdateRecordBulk(record []hclouddns.HCloudRecord) (hclouddns.HCloudAnswerUpdateRecords, error) -} - -type MockAction struct { - Name string - RecordData hclouddns.HCloudRecord -} - -type mockHCloudClient struct { - Token string `yaml:"token"` - Actions []MockAction -} - -// New instance -func mockHCloudNew(t string) *mockHCloudClient { - return &mockHCloudClient{ - Token: t, - } -} - -// Mock all methods - -func (m *mockHCloudClient) GetZone(ID string) (hclouddns.HCloudAnswerGetZone, error) { - return hclouddns.HCloudAnswerGetZone{}, nil -} - -func (m *mockHCloudClient) GetZones(params hclouddns.HCloudGetZonesParams) (hclouddns.HCloudAnswerGetZones, error) { - return hclouddns.HCloudAnswerGetZones{ - Zones: []hclouddns.HCloudZone{ - { - ID: "HetznerZoneID", - Name: "blindage.org", - TTL: 666, - RecordsCount: 1, - }, - }, - }, nil -} - -// zones -func (m *mockHCloudClient) UpdateZone(zone hclouddns.HCloudZone) (hclouddns.HCloudAnswerGetZone, error) { - return hclouddns.HCloudAnswerGetZone{}, nil -} -func (m *mockHCloudClient) DeleteZone(ID string) (hclouddns.HCloudAnswerDeleteZone, error) { - return hclouddns.HCloudAnswerDeleteZone{}, nil -} -func (m *mockHCloudClient) CreateZone(zone hclouddns.HCloudZone) (hclouddns.HCloudAnswerGetZone, error) { - return hclouddns.HCloudAnswerGetZone{}, nil -} -func (m *mockHCloudClient) ImportZoneString(zoneID string, zonePlainText string) (hclouddns.HCloudAnswerGetZone, error) { - return hclouddns.HCloudAnswerGetZone{}, nil -} -func (m *mockHCloudClient) ExportZoneToString(zoneID string) (hclouddns.HCloudAnswerGetZonePlainText, error) { - return hclouddns.HCloudAnswerGetZonePlainText{}, nil -} -func (m *mockHCloudClient) ValidateZoneString(zonePlainText string) (hclouddns.HCloudAnswerZoneValidate, error) { - return hclouddns.HCloudAnswerZoneValidate{}, nil -} - -// records - -func (m *mockHCloudClient) GetRecord(ID string) (hclouddns.HCloudAnswerGetRecord, error) { - return hclouddns.HCloudAnswerGetRecord{}, nil -} - -func (m *mockHCloudClient) GetRecords(params hclouddns.HCloudGetRecordsParams) (hclouddns.HCloudAnswerGetRecords, error) { - return hclouddns.HCloudAnswerGetRecords{ - Records: []hclouddns.HCloudRecord{ - { - RecordType: hclouddns.RecordType("A"), - ID: "ATypeRecordID", - ZoneID: "HetznerZoneID", - Name: "@", - Value: "127.0.0.1", - TTL: 666, - }, - }, - }, nil -} -func (m *mockHCloudClient) UpdateRecord(record hclouddns.HCloudRecord) (hclouddns.HCloudAnswerGetRecord, error) { - m.Actions = append(m.Actions, MockAction{ - Name: "UpdateRecord", - RecordData: record, - }) - return hclouddns.HCloudAnswerGetRecord{}, nil -} -func (m *mockHCloudClient) DeleteRecord(ID string) (hclouddns.HCloudAnswerDeleteRecord, error) { - m.Actions = append(m.Actions, MockAction{ - Name: "DeleteRecord", - RecordData: hclouddns.HCloudRecord{ - ID: ID, - }, - }) - return hclouddns.HCloudAnswerDeleteRecord{}, nil -} -func (m *mockHCloudClient) CreateRecord(record hclouddns.HCloudRecord) (hclouddns.HCloudAnswerGetRecord, error) { - m.Actions = append(m.Actions, MockAction{ - Name: "CreateRecord", - RecordData: record, - }) - return hclouddns.HCloudAnswerGetRecord{}, nil -} -func (m *mockHCloudClient) CreateRecordBulk(record []hclouddns.HCloudRecord) (hclouddns.HCloudAnswerCreateRecords, error) { - return hclouddns.HCloudAnswerCreateRecords{}, nil -} -func (m *mockHCloudClient) UpdateRecordBulk(record []hclouddns.HCloudRecord) (hclouddns.HCloudAnswerUpdateRecords, error) { - return hclouddns.HCloudAnswerUpdateRecords{}, nil -} - -func TestNewHetznerProvider(t *testing.T) { - _ = os.Setenv("HETZNER_TOKEN", "myHetznerToken") - _, err := NewHetznerProvider(context.Background(), endpoint.NewDomainFilter([]string{"blindage.org"}), true) - if err != nil { - t.Errorf("failed : %s", err) - } - - _ = os.Unsetenv("HETZNER_TOKEN") - _, err = NewHetznerProvider(context.Background(), endpoint.NewDomainFilter([]string{"blindage.org"}), true) - if err == nil { - t.Errorf("expected to fail") - } -} - -func TestHetznerProvider_TestData(t *testing.T) { - - mockedClient := mockHCloudNew("myHetznerToken") - - // Check test zone data is ok - expectedZonesAnswer := hclouddns.HCloudAnswerGetZones{ - Zones: []hclouddns.HCloudZone{ - { - ID: "HetznerZoneID", - Name: "blindage.org", - TTL: 666, - RecordsCount: 1, - }, - }, - } - - testingZonesAnswer, err := mockedClient.GetZones(hclouddns.HCloudGetZonesParams{}) - if err != nil { - t.Errorf("should not fail, %s", err) - } - - if !reflect.DeepEqual(expectedZonesAnswer, testingZonesAnswer) { - t.Errorf("should be equal, %s", err) - } - - // Check test record data is ok - expectedRecordsAnswer := hclouddns.HCloudAnswerGetRecords{ - Records: []hclouddns.HCloudRecord{ - { - RecordType: hclouddns.RecordType("A"), - ID: "ATypeRecordID", - ZoneID: "HetznerZoneID", - Name: "@", - Value: "127.0.0.1", - TTL: 666, - }, - }, - } - - testingRecordsAnswer, err := mockedClient.GetRecords(hclouddns.HCloudGetRecordsParams{}) - if err != nil { - t.Errorf("should not fail, %s", err) - } - - if !reflect.DeepEqual(expectedRecordsAnswer, testingRecordsAnswer) { - t.Errorf("should be equal, %s", err) - } - -} - -func TestHetznerProvider_Records(t *testing.T) { - - mockedClient := mockHCloudNew("myHetznerToken") - - mockedProvider := &HetznerProvider{ - Client: mockedClient, - } - - // Now check Records function of provider, if ZoneID equal "blindage.org" must be returned - endpoints, err := mockedProvider.Records(context.Background()) - if err != nil { - t.Errorf("should not fail, %s", err) - } - fmt.Printf("%+v\n", endpoints[0].DNSName) - assert.Equal(t, "blindage.org", endpoints[0].DNSName) -} - -func TestHetznerProvider_ApplyChanges(t *testing.T) { - changes := &plan.Changes{} - mockedClient := mockHCloudNew("myHetznerToken") - mockedProvider := &HetznerProvider{ - Client: mockedClient, - } - - changes.Create = []*endpoint.Endpoint{ - {DNSName: "blindage.org", Targets: endpoint.Targets{"target"}}, - {DNSName: "test.blindage.org", Targets: endpoint.Targets{"target"}, RecordTTL: 666}, - } - changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test.blindage.org", Targets: endpoint.Targets{"target-new"}, RecordType: "A", RecordTTL: 777}} - changes.Delete = []*endpoint.Endpoint{{DNSName: "test.blindage.org", Targets: endpoint.Targets{"target"}, RecordType: "A"}} - - err := mockedProvider.ApplyChanges(context.Background(), changes) - if err != nil { - t.Errorf("should not fail, %s", err) - } - - if len(mockedClient.Actions) != 4 { - t.Errorf("should be 4 changes not %d", len(mockedClient.Actions)) - } -} - -func TestHetznerProvider_ApplyChangesCreateUpdateCname(t *testing.T) { - changes := &plan.Changes{} - mockedClient := mockHCloudNew("myHetznerToken") - mockedProvider := &HetznerProvider{ - Client: mockedClient, - } - - changes.Create = []*endpoint.Endpoint{ - {DNSName: "test-cname.blindage.org", Targets: endpoint.Targets{"target"}, RecordTTL: 666, RecordType: "CNAME"}, - } - changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test-cname2.blindage.org", Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 777}} - - err := mockedProvider.ApplyChanges(context.Background(), changes) - if err != nil { - t.Errorf("should not fail, %s", err) - } - - td.Cmp(t, mockedClient.Actions, []MockAction{ - { - Name: "CreateRecord", - RecordData: hclouddns.HCloudRecord{ - RecordType: "CNAME", - ID: "", - Created: "", - Modified: "", - ZoneID: "HetznerZoneID", - Name: "test-cname", - Value: "target.", - TTL: 666, - }, - }, - { - Name: "UpdateRecord", - RecordData: hclouddns.HCloudRecord{ - RecordType: "CNAME", - ID: "", - Created: "", - Modified: "", - ZoneID: "HetznerZoneID", - Name: "test-cname2", - Value: "target-new.", - TTL: 777, - }, - }, - }) -}