mirror of
				https://github.com/kubernetes-sigs/external-dns.git
				synced 2025-10-31 02:31:00 +01:00 
			
		
		
		
	Merge branch 'kubernetes-sigs:master' into master
This commit is contained in:
		
						commit
						e4cb7fbf09
					
				
							
								
								
									
										13
									
								
								.github/workflows/lint-test-chart.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/lint-test-chart.yaml
									
									
									
									
										vendored
									
									
								
							| @ -15,10 +15,19 @@ jobs: | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
| 
 | ||||
|       - name: Run Artifact Hub lint | ||||
|         shell: bash | ||||
|         run: | | ||||
|           set -euo pipefail | ||||
|           curl -Lo ah_linux_amd64.tar.gz https://github.com/artifacthub/hub/releases/download/v1.6.0/ah_1.6.0_linux_amd64.tar.gz | ||||
|           tar -xzvf ah_linux_amd64.tar.gz ah | ||||
|           ./ah lint --kind helm || exit 1 | ||||
|           rm -f ./ah ./ah_linux_amd64.tar.gz | ||||
| 
 | ||||
|       - name: Set up Helm | ||||
|         uses: azure/setup-helm@v1 | ||||
|         with: | ||||
|           version: v3.6.3 | ||||
|           version: 3.* | ||||
| 
 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
| @ -26,7 +35,7 @@ jobs: | ||||
|           python-version: 3.7 | ||||
| 
 | ||||
|       - name: Set up chart-testing | ||||
|         uses: helm/chart-testing-action@v2.1.0 | ||||
|         uses: helm/chart-testing-action@v2.2.0 | ||||
| 
 | ||||
|       - name: Run chart-testing (list-changed) | ||||
|         id: list-changed | ||||
|  | ||||
							
								
								
									
										24
									
								
								.github/workflows/release-chart.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/release-chart.yaml
									
									
									
									
										vendored
									
									
								
							| @ -17,6 +17,27 @@ jobs: | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
| 
 | ||||
|       - name: Get chart version | ||||
|         id: chart_version | ||||
|         shell: bash | ||||
|         run: | | ||||
|           set -euo pipefail | ||||
|           chart_version="$(grep -Po "(?<=^version: ).+" charts/external-dns/Chart.yaml)" | ||||
|           echo "::set-output name=version::${chart_version}" | ||||
| 
 | ||||
|       - name: Get changelog entry | ||||
|         id: changelog_reader | ||||
|         uses: mindsers/changelog-reader-action@v2 | ||||
|         with: | ||||
|           path: charts/external-dns/CHANGELOG.md | ||||
|           version: "v${{ steps.chart_version.outputs.version }}" | ||||
| 
 | ||||
|       - name: Create release notes | ||||
|         shell: bash | ||||
|         run: | | ||||
|           set -euo pipefail | ||||
|           printf '${{ steps.changelog_reader.outputs.changes }}' > charts/external-dns/_release-notes.md | ||||
| 
 | ||||
|       - name: Configure Git | ||||
|         run: | | ||||
|           git config user.name "$GITHUB_ACTOR" | ||||
| @ -28,7 +49,8 @@ jobs: | ||||
|           version: v3.6.3 | ||||
| 
 | ||||
|       - name: Run chart-releaser | ||||
|         uses: helm/chart-releaser-action@v1.2.1 | ||||
|         uses: helm/chart-releaser-action@v1.3.0 | ||||
|         env: | ||||
|           CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | ||||
|           CR_RELEASE_NAME_TEMPLATE: "external-dns-helm-chart-{{ .Version }}" | ||||
|           CR_RELEASE_NOTES_FILE: _release-notes.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) | ||||
|  | ||||
							
								
								
									
										24
									
								
								charts/external-dns/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								charts/external-dns/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| # ExternalDNS Helm Chart Changelog | ||||
| 
 | ||||
| All notable changes to this project will be documented in this file. | ||||
| 
 | ||||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| <!-- ## [UNRELEASED] | ||||
| ### Added | ||||
| ### Changed | ||||
| ### Deprecated | ||||
| ### Removed --> | ||||
| 
 | ||||
| ## [v1.8.0] - UNRELEASED | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - Add annotations to Deployment. [#2477](https://github.com/kubernetes-sigs/external-dns/pull/2477) from @beastob | ||||
| 
 | ||||
| ### Changed | ||||
| 
 | ||||
| - Fix RBAC for `istio-virtualservice` source when `istio-gateway` isn't also added. [#2564](https://github.com/kubernetes-sigs/external-dns/pull/2564) from @mcwarman | ||||
| @ -2,7 +2,7 @@ apiVersion: v2 | ||||
| name: external-dns | ||||
| description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers. | ||||
| type: application | ||||
| version: 1.7.1 | ||||
| version: 1.8.0 | ||||
| appVersion: 0.10.2 | ||||
| keywords: | ||||
|   - kubernetes | ||||
| @ -18,8 +18,6 @@ maintainers: | ||||
| annotations: | ||||
|   artifacthub.io/changes: | | ||||
|     - kind: added | ||||
|       description: "Allow custom ClusterRole rules to be specified for sources without defaults." | ||||
|       description: "Add annotations to Deployment." | ||||
|     - kind: changed | ||||
|       description: "Update ExternalDNS version to v0.10.2." | ||||
|     - kind: changed | ||||
|       description: "Set ClusterRole rules based more enabled sources." | ||||
|       description: "Fix RBAC for istio-virtualservice source when istio-gateway isn't also added." | ||||
|  | ||||
| @ -3,3 +3,5 @@ | ||||
| ## Chart Changes | ||||
| 
 | ||||
| When contributing chart changes please follow the same process as when contributing other content but also please **DON'T** modify _Chart.yaml_ in the PR as this would result in a chart release when merged and will mean that your PR will need modifying before it can be accepted. The chart version will be updated as part of the PR to release the chart. | ||||
| 
 | ||||
| Please **DO** add your changes to the _CHANGELOG.md_ file in the chart directory under the `## [UNRELEASED]` section, if there isn't an uncommented `## [UNRELEASED]` section please copy the commented out template and use that. | ||||
|  | ||||
| @ -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 | ||||
| ``` | ||||
							
								
								
									
										5
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								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 | ||||
| @ -46,7 +45,7 @@ require ( | ||||
| 	github.com/sirupsen/logrus v1.8.1 | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	github.com/transip/gotransip/v6 v6.14.0 | ||||
| 	github.com/ukfast/sdk-go v1.4.23 | ||||
| 	github.com/ukfast/sdk-go v1.4.34 | ||||
| 	github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 | ||||
| 	github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556 | ||||
| 	github.com/vultr/govultr/v2 v2.14.1 | ||||
| @ -132,7 +131,7 @@ require ( | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/stretchr/objx v0.3.0 // indirect | ||||
| 	github.com/terra-farm/udnssdk v1.3.5 // indirect | ||||
| 	github.com/ukfast/go-durationstring v1.0.0 // indirect | ||||
| 	github.com/ukfast/go-durationstring v1.1.0 // indirect | ||||
| 	go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect | ||||
| 	go.opencensus.io v0.23.0 // indirect | ||||
| 	go.uber.org/atomic v1.7.0 // indirect | ||||
|  | ||||
							
								
								
									
										10
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								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= | ||||
| @ -1200,10 +1198,10 @@ github.com/tsaarni/x500dn v0.0.0-20210331182804-14283c7f5a16/go.mod h1:RquKZ5rER | ||||
| github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= | ||||
| github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= | ||||
| github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | ||||
| github.com/ukfast/go-durationstring v1.0.0 h1:kgPuA7XjLjgLDfkG8j0MpolxcZh/eMdiVoOIFD/uc5I= | ||||
| github.com/ukfast/go-durationstring v1.0.0/go.mod h1:Ci81n51kfxlKUIaLY9cINIKRO94VTqV+iCGbOMTb0V8= | ||||
| github.com/ukfast/sdk-go v1.4.23 h1:dLZmHW2jgV0QQ2TGGdbL2tYVdtQPcuUub7Rzh+6Cqic= | ||||
| github.com/ukfast/sdk-go v1.4.23/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo= | ||||
| github.com/ukfast/go-durationstring v1.1.0 h1:Ki0ubc5jqSt7XuAs+gkPNpHYolIwbcsRW4LS239tIHA= | ||||
| github.com/ukfast/go-durationstring v1.1.0/go.mod h1:Ci81n51kfxlKUIaLY9cINIKRO94VTqV+iCGbOMTb0V8= | ||||
| github.com/ukfast/sdk-go v1.4.34 h1:xuNbJ+WxsUqBfrm6eEdnTi6GcKL3R1cLxSN0jMk+4Rc= | ||||
| github.com/ukfast/sdk-go v1.4.34/go.mod h1:vxlI1IHy2pp04AYqRMm0MHWSWOF0lwTkPJXHxTDLPok= | ||||
| github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= | ||||
| github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 h1:n7unetnX8WWTc0U85h/0+dJoLWLqoaJwowXB9RkBdxU= | ||||
| github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60/go.mod h1:43vmy6GEvRuVMpGEWfJ/JoEM6RIqUQI1/tb8JqZR1zI= | ||||
|  | ||||
| @ -3,7 +3,7 @@ kind: Kustomization | ||||
| 
 | ||||
| images: | ||||
|   - name: k8s.gcr.io/external-dns/external-dns | ||||
|     newTag: v0.10.2 | ||||
|     newTag: v0.11.0 | ||||
| 
 | ||||
| resources: | ||||
|   - ./external-dns-deployment.yaml | ||||
|  | ||||
							
								
								
									
										3
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								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": | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
| @ -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, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user