Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Dnsimple as new provider #224

Merged
merged 17 commits into from
Oct 11, 2017
Merged

Conversation

jose5918
Copy link
Contributor

@jose5918 jose5918 commented Jun 6, 2017

This PR adds Dnsimple as a provider option. I was just having a bit of trouble adding dnsimple's go library as a dependency with glide (Still a bit new with Go).

Edit: Got glide to run but seems it added a way more files than necessary

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Jun 6, 2017
@jose5918 jose5918 force-pushed the dnsimple branch 4 times, most recently from 6f819a3 to e091def Compare June 7, 2017 00:09
@ideahitme
Copy link

@jose5918 thanks for PR !

Kind request to either:

  1. Not add the vendor before PR is reviewed (we can tolerate failing build), just include glide files so we can build it locally. Once PR is approved you can commit dependencies
    Or
  2. Add dependencies with a separate commit

@jose5918
Copy link
Contributor Author

jose5918 commented Jun 7, 2017

@ideahitme Let me know if that looks better for you guys or if you'd prefer additional changes

@linki
Copy link
Member

linki commented Jun 12, 2017

Glanced over it and looks good. I'll try it out this week. Thank you @jose5918.

@linki linki mentioned this pull request Jun 12, 2017

type mockDnsimpleZonesService struct{}

func (m *mockDnsimpleZonesService) ListZones(accountID string, options *dnsimple.ZoneListOptions) (*dnsimple.ZonesResponse, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kind request to use testify/mock instead of mocking separate service for each type of expected response :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i know we are using this pattern in other dns providers, but tests are getting incredibly bloated and difficult to read, so we need to take time to refactor existing tests as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ideahitme Sure thing. Also let me know if I should make any changes related to this issue #228

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, sorry for keep you waiting. We will try to review this PR in the next few days. Thanks a lot for support ;)

@linki
Copy link
Member

linki commented Jun 23, 2017

@jose5918 I merged in master and fixed some conflicts. Hope that's ok.

@linki
Copy link
Member

linki commented Jun 23, 2017

Tested it out in my account. Initial record creation of records works fine:

$ external-dns --source service --domain-filter linki.space --provider dnsimple --namespace INFO[0001] Changing records: CREATE {0  0 A myapp.linki.space 146.148.123.148 0 0 false []  }
INFO[0001] Changing records: CREATE {0  0 TXT myapp.linki.space "heritage=external-dns,external-dns/owner=linki-local" 0 0 false []  }

But then I found a couple of issues:

On the second iteration it throw an error:

$ external-dns --source service --domain-filter linki.space --provider dnsimple --namespace external-dns-test --registry=txt --txt-owner-id=linki-local
INFO[0001] Changing records: CREATE {0  0 A myapp.linki.space 146.148.123.148 0 0 false []  }
ERRO[0001] POST https://api.dnsimple.com/v2/61844/zones/linki.space/records: 400 Zone record already exists

Then I removed the annotation so that it would delete the record. Also got an error:

$ external-dns --source service --domain-filter linki.space --provider dnsimple --namespace external-dns-test --registry=txt --txt-owner-id=linki-local
INFO[0001] Changing records: DELETE {0  0 A myapp 146.148.123.148 0 0 false []  }
ERRO[0001] GET https://api.dnsimple.com/v2/61844/zones//records: 404 Zone `records` not found

@jose5918
Copy link
Contributor Author

@linki Alright I will look into seeing what I broke. Looks like I had some mistakes in the unit test so I likely broke the delete functionality without realizing it when doing some cleanup.

@jose5918
Copy link
Contributor Author

@linki It should be fixed now. Here is an example test

$ external-dns --source service --domain-filter kube-external-test.io --provider dnsimple --namespace external-dns-jose --registry=txt
INFO[0008] All records are already up to date           
INFO[0069] Changing records: CREATE {0  0 A example.kube-external-test.io 35.185.193.99 0 0 false []  } in zone: kube-external-test.io 
INFO[0069] Changing records: CREATE {0  0 TXT example.kube-external-test.io "heritage=external-dns,external-dns/owner=default" 0 0 false []  } in zone: kube-external-test.io 
INFO[0130] Changing records: CREATE {0  0 A example2.kube-external-test.io 35.185.193.99 0 0 false []  } in zone: kube-external-test.io 
INFO[0130] Changing records: CREATE {0  0 TXT example2.kube-external-test.io "heritage=external-dns,external-dns/owner=default" 0 0 false []  } in zone: kube-external-test.io 
INFO[0130] Changing records: DELETE {0  0 A example.kube-external-test.io 35.185.193.99 0 0 false []  } in zone: kube-external-test.io 
INFO[0131] Changing records: DELETE {0  0 TXT example.kube-external-test.io "heritage=external-dns,external-dns/owner=default" 0 0 false []  } in zone: kube-external-test.io 

@ideahitme Is this sort of what you had in mind with the testify mock tests?

@ideahitme
Copy link

@jose5918 sorry for keep you waiting. I will merge the conflict on this branch and review your PR :)

@ideahitme
Copy link

Hi @jose5918 could you please remove the provider/mocks/ package entirely and define mocks in the provider/dnsimple_test.go I would avoid using auto-generated code for a simple task of defining a mock

@jose5918
Copy link
Contributor Author

jose5918 commented Jul 11, 2017

@ideahitme Closing was an accident, meant to just let you know that I would make the changes.

@linki
Copy link
Member

linki commented Aug 18, 2017

@jose5918 will test again and then come back here

@linki linki self-assigned this Aug 18, 2017
@arno01
Copy link

arno01 commented Oct 8, 2017

@linki any news?

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/large labels Oct 9, 2017
@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Oct 9, 2017
@linki
Copy link
Member

linki commented Oct 9, 2017

@jose5918 I merged master, fixed some conflicts, added dnsimple's go code, removed the need for suitableZone from the provider package and added the provider to the changelog.

I just tested all the combinations I can think of against an AWS and GKE cluster (CNAME vs. A record) and it worked perfectly fine.

This is a strong good to go. Thanks @jose5918, great work! 👍

@linki linki added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 11, 2017
@linki linki merged commit 09c35b6 into kubernetes-sigs:master Oct 11, 2017
@linki
Copy link
Member

linki commented Oct 11, 2017

Thank you, @jose5918 😻

@MichielDeMey
Copy link

You might want to add a markdown document here as well https://github.com/kubernetes-incubator/external-dns/tree/master/docs/tutorials to help other users setup DNSimple with external-dns.

@linki
Copy link
Member

linki commented Oct 13, 2017

@MichielDeMey Good suggestion. Would you be willing to write a tutorial for DNSimple @jose5918?

@jose5918
Copy link
Contributor Author

@linki I can write a tutorial, but I no longer have a dnsimple account to verify against so I can't verify that my documentation will be correct

@arno01
Copy link

arno01 commented Dec 2, 2017

@jose5918 I could verify your steps. I have a DNSimple account and running Kubernetes 1.8.4.
I never used the external-dns module and have no idea (yet) on how to plug it into my K8s and use it.
So I would greatly appreciate if you could give some steps in a way:

  • how do I use external-dns with my current K8s deployment? (the simplest and straight-forward way);
  • how do I setup external-dns so that it would use my DNSimple account?;

Please let me know.
If you drop the steps here or in a gist, I will verify and comment.

Thanks for your work!

@jose5918
Copy link
Contributor Author

jose5918 commented Dec 4, 2017

@arno01 https://gist.github.com/jose5918/26d184d90fb4b9c7c045c8d71e2ec4c5

Let me know if it works with those instructions. It's been a while and I think I first tried it on Kubernetes 1.6

@arno01
Copy link

arno01 commented Dec 4, 2017

@jose5918 thanks for the instruction!

I have tried it with my configuration, unfortunately it did not work as I expected. Maybe I am doing something wrong...

I do not have a LoadBalancer since I am running K8s on my own server, so I am using ClusterIP for the services and the Ingress resource to expose them.

I followed the instruction with the following changes:

  • --source=ingress for external-dns;
  • --domain-filter=nixaid.com as I am the owner of it;
  • DNSIMPLE_OAUTH set to my Account access token (not the User one);

Then, I had to create the RBAC file dnsimple-rbac.yaml with the following contents in order to get rid of:

$ kubectl -n nginx-ingress logs -f external-dns-5c86f88ff8-q7zcp
...
time="2017-12-04T21:40:29Z" level=info msg="Connected to cluster at https://10.96.0.1:443" 
time="2017-12-04T21:40:30Z" level=error msg="ingresses.extensions is forbidden: User "system:serviceaccount:default:default" cannot list ingresses.extensions at the cluster scope" 
  • dnsimple-rbac.yaml:
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sa-external-dns
  labels:
    app: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: sa-external-dns
  labels:
    app: external-dns
rules:
  - apiGroups:
      - ""
      - extensions
    resources:
      - ingresses
      - services
    verbs:
      - get
      - list
      - watch
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: sa-external-dns
  labels:
    app: external-dns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: sa-external-dns
subjects:
  - kind: ServiceAccount
    name: sa-external-dns
    namespace: default
  • nginx-test1.yaml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-test1
spec:
  template:
    metadata:
      labels:
        app: nginx-test1
    spec:
      containers:
      - image: nginx:alpine
        name: nginx-test1
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-test1
spec:
  selector:
    app: nginx-test1
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-test1
  annotations:
    ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/tls-acme: "true"
    external-dns.alpha.kubernetes.io/hostname: test1.nixaid.com
spec:
  rules:
  - host: "test1.nixaid.com"
    http:
      paths:
      - backend:
          serviceName: nginx-test1
          servicePort: 80
        path: /
  tls:
    - hosts:
      - test1.nixaid.com
      secretName: test1.nixaid.com

My K8s nodes and their external IPs:

$ kubectl get nodes -o wide
NAME          STATUS    ROLES     AGE       VERSION   EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION              CONTAINER-RUNTIME
k8s-master    Ready     master    6d        v1.8.4    <none>        REDACTED                REDACTED   docker://Unknown
k8s-worker1   Ready     <none>    6d        v1.8.4    10.0.2.111    REDACTED                REDACTED   docker://Unknown
k8s-worker2   Ready     <none>    6d        v1.8.4    10.0.2.112    REDACTED                REDACTED   docker://Unknown

The logs from external-dns v0.4.8:

$ kubectl logs -f external-dns-668c745544-r8dt5
time="2017-12-04T21:50:51Z" level=info msg="config: &{Master: KubeConfig: Sources:[ingress] Namespace: AnnotationFilter: FQDNTemplate: Compatibility: PublishInternal:false Provider:dnsimple GoogleProject: DomainFilter:[nixaid.com] AWSZoneType: AzureConfigFile:/etc/kubernetes/azure.json AzureResourceGroup: CloudflareProxied:false InfobloxGridHost: InfobloxWapiPort:443 InfobloxWapiUsername:admin InfobloxWapiPassword: InfobloxWapiVersion:2.3.1 InfobloxSSLVerify:true InMemoryZones:[] Policy:sync Registry:txt TXTOwnerID:default TXTPrefix: Interval:1m0s Once:false DryRun:false LogFormat:text MetricsAddress::7979 LogLevel:info}" 
time="2017-12-04T21:50:51Z" level=info msg="Connected to cluster at https://10.96.0.1:443" 
time="2017-12-04T21:50:52Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:50:52Z" level=info msg="Changing records: CREATE {0  0 A nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:50:52Z" level=info msg="Changing records: CREATE {0  0 A old.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:50:53Z" level=info msg="Changing records: CREATE {0  0 TXT echo.nixaid.com "heritage=external-dns,external-dns/owner=default" 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:50:54Z" level=info msg="Changing records: CREATE {0  0 TXT nixaid.com "heritage=external-dns,external-dns/owner=default" 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:50:54Z" level=info msg="Changing records: CREATE {0  0 TXT old.nixaid.com "heritage=external-dns,external-dns/owner=default" 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:51:56Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:51:56Z" level=error msg="POST https://api.dnsimple.com/v2/REDACTED/zones/nixaid.com/records: 400 Zone record already exists" 
time="2017-12-04T21:52:57Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:52:57Z" level=error msg="POST https://api.dnsimple.com/v2/REDACTED/zones/nixaid.com/records: 400 Zone record already exists" 
time="2017-12-04T21:53:58Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:53:58Z" level=error msg="POST https://api.dnsimple.com/v2/REDACTED/zones/nixaid.com/records: 400 Zone record already exists" 
time="2017-12-04T21:54:59Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:54:59Z" level=error msg="POST https://api.dnsimple.com/v2/REDACTED/zones/nixaid.com/records: 400 Zone record already exists" 
time="2017-12-04T21:55:59Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:56:00Z" level=error msg="POST https://api.dnsimple.com/v2/REDACTED/zones/nixaid.com/records: 400 Zone record already exists" 
time="2017-12-04T21:57:00Z" level=info msg="Changing records: CREATE {0  0 A echo.nixaid.com 10.0.2.112 0 0 false []  } in zone: nixaid.com" 
time="2017-12-04T21:57:00Z" level=error msg="POST https://api.dnsimple.com/v2/REDACTED/zones/nixaid.com/records: 400 Zone record already exists" 
...
... looping over the echo.nixaid.com (I have an ingress resource for it, but I did not annotate it for the external-dns dnsimple to register it.. not sure if it should be trying to POST it...)
...

I can see that external-dns is trying to set the External IP of my K8s nodes which is not helping much since it is in a Class A (private internet), e.g. 10.0.2.112.

Probably there has to be some way that would be finding out what is my actual public IP or a way to set it in the configuration.

Then there was no message coming regarding the before mentioned nginx-test1 ingress resource, like if the external-dns does not see it is there.. is this normal?

Not sure why is it continuously trying to do POST into my zones (for some, but not all, already existing ingresses), saying that the record already exists... I would not expect it to update my zones unless I say so, e.g. using the annotation external-dns.alpha.kubernetes.io/hostname: test1.nixaid.com.

Please let me know if all this is expected or if I am doing something wrong.

Thanks!

@arno01
Copy link

arno01 commented Dec 4, 2017

Humm, it appears it somehow created a duplicate record with the same Name under my DNSimple account... Probably, the good point that it did create some record there, though, I was not explicitly asking to go through already existing Ingress resources.. I presume that there should be the annotation external-dns.alpha.kubernetes.io/hostname in the first place, before it would be let POST...

$ dig +noall +answer A echo.nixaid.com @8.8.8.8
echo.nixaid.com.	299	IN	A	217.182.136.169
echo.nixaid.com.	299	IN	A	10.0.2.112

See the screenshot:

screenshot from 2017-12-04 23-31-14

Upd I have also noticed one A record added was nixaid.com.nixaid.com, I presume that this could be some issue when it was reading the hostname from the Ingress resource for nixaid.com.

ffledgling pushed a commit to ffledgling/external-dns that referenced this pull request Jan 18, 2018
* Adds Dnsimple as a provider

* chore(vendor): remove vendor for smaller diff

* fix(config): make dnsimple selectable via flags

* Fix delete and update

* Dnsimple testify mock tests

* remove leaked file

* Move and simplify mock functions

* chore: use lowercase for logrus repository

* chore: update dependencies using glide

* chore: vendor dnsimple-go package

* ref: isolate suitable type in source package

* add support for DNSimple, thx @jose5918 :D
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. provider size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants