-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Add PDNS as a Provider #373
Conversation
Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please follow instructions at https://github.com/kubernetes/kubernetes/wiki/CLA-FAQ to sign the CLA. It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please follow instructions at https://github.com/kubernetes/kubernetes/wiki/CLA-FAQ to sign the CLA. It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
@ffledgling Thanks for your PR! |
@njuettner Thanks for taking a look! This PR is in a bit of flux at the moment and I should've marked it as incomplete. I'll be updating this in the upcoming days, the vendoring, external package and a lot of other things have been changing very quickly. When it's done I'll update either via a comment here or via Slack. Cheers! |
I've updated this commit and it now implements the pdns provider fully (for the most part). Some notes:
|
ec0ecfb
to
6f14638
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in general looks okay, but tests are really needed to have major functions covered for provider/pdns.go file.
pkg/apis/externaldns/types.go
Outdated
@@ -129,7 +133,7 @@ func (cfg *Config) ParseFlags(args []string) error { | |||
app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal) | |||
|
|||
// Flags related to providers | |||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory") | |||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory", "pdns") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please also mention it in the options list (same line)
provider/pdns.go
Outdated
defaultServerID = "localhost" | ||
defaultTTL = 300 | ||
|
||
maxUInt32 = ^uint32(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is already defined in SL: math.MaxUint32
and math.MaxInt32
respectively.
provider/pdns.go
Outdated
// We do not support dry running, exit safely instead of surprising the user | ||
// TODO: Add Dry Run support | ||
if dryRun { | ||
log.Fatalf("PDNS Provider does not currently support dry-run, stopping.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this can't be returned as an error instead. It would fatal in the main then, which I find slightly preferable
provider/pdns.go
Outdated
} | ||
|
||
if apikey == "" { | ||
log.Warnf("API Key for PDNS is empty. Specify using --pdns-api-key=") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it work without specifying apikey? if not then this should return an error
provider/pdns.go
Outdated
log.Warnf("API Key for PDNS is empty. Specify using --pdns-api-key=") | ||
} | ||
if len(domainFilter.filters) == 0 { | ||
log.Warnf("Domain Filter is not supported by PDNS. It will be ignored.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it be safer just to quit if domain filter is passed ?
provider/pdns.go
Outdated
package provider | ||
|
||
import ( | ||
//"strings" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should really be removed
provider/pdns.go
Outdated
endpoints = []*endpoint.Endpoint{} | ||
|
||
for _, record := range rr.Records { | ||
//func NewEndpointWithTTL(dnsName, target, recordType string, ttl TTL) *Endpoint |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove commented code
provider/pdns.go
Outdated
return err | ||
} | ||
for _, zone := range zonelist { | ||
jso, _ := json.Marshal(zone) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the error here should be handled
provider/pdns.go
Outdated
} | ||
} | ||
*/ | ||
mastermap := make(map[string]map[string]map[string][]*endpoint.Endpoint) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to refactor this code, so that we don't have triple nested maps ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ffledgling could you take a look on that again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a local copy of this patched, but this is blocked on mock testing which is exhibiting segfaults, I'm trying to still fix this. 👍
what is the status here ? |
@ideahitme This has fallen by the wayside for a bit because some other stuff came up, but I'll try to clean it up and send it in by the second/third week of January. I have addressed most of your concerns in the review, but I need to add tests, which requires a bit of refactoring. |
@ffledgling sure, ping me when it is ready |
hi, I`m trying to build it, but no luck, can you check it ?
errors I get:
|
@fxpester That shouldn't happen, looks like something's out of sync. I'll take a look when I get in tomorrow. |
Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please follow instructions at https://git.k8s.io/community/CLA.md#the-contributor-license-agreement to sign the CLA. It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
Merged-in current master which uses |
@linki I noticed, I'll switch over to dep. |
While I work on this, here's the binary with the pdns bits built in for those who wish to play around with it - https://pastebin.ffledgling.com/external-dns/ (Please do not use this in production). |
@ideahitme @njuettner I think this is ready for review on my end, can you please take a look? I think I've addressed all prior concerns here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some comments, mostly covering code comments and a bit of error handling.
provider/pdns.go
Outdated
"context" | ||
"encoding/json" | ||
"errors" | ||
//"fmt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Can we get rid of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do.
provider/pdns.go
Outdated
|
||
// ConvertEndpointsToZones marshals endpoints into pdns compatible Zone structs | ||
func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changetype pdnsChangeType) (zonelist []pgo.Zone, _ error) { | ||
/* eg of mastermap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a leftover for developing purpose? If not, I would move it to the comment of the method, otherwise just drop it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a stale comment from a previous implementation, I'll remove it.
|
||
sort.SliceStable(zones, func(i, j int) bool { return len(zones[i].Name) > len(zones[j].Name) }) | ||
|
||
// NOTE: Complexity of this loop is O(Zones*Endpoints). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love that you mention the complexity here, might be useful in the future.
ep := endpoints[0] | ||
dnsname := ensureTrailingDot(ep.DNSName) | ||
if strings.HasSuffix(dnsname, zone.Name) { | ||
// The assumption here is that there will only ever be one target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR is against master, so that shouldn't be a problem, am I right? If so, it is not a concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, this is just here as a note in case it changes in the future and results in a breakage. It's an assumption, so I thought to document it.
provider/pdns.go
Outdated
for _, zone := range zonelist { | ||
jso, err := json.Marshal(zone) | ||
if err != nil { | ||
log.Debugf("JSON Marshal for zone struct failed!") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should return the error if we fail to unmarshal. It's not so much of a common problem but I think that just logging is pretty dangerous. Also, the log is at debug level which means that it will pass completely unnoticed in case with a lower debug level.
provider/pdns.go
Outdated
func (c *PDNSAPIClient) PatchZone(zoneID string, zoneStruct pgo.Zone) (*http.Response, error) { | ||
resp, err := c.client.ZonesApi.PatchZone(c.authCtx, defaultServerID, zoneID, zoneStruct) | ||
if err != nil { | ||
log.Warnf("Unable to patch zone %s. %v", zoneStruct.Name, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it okay to only log this error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Raffo Would you prefer this be a hard fail instead? Or are you suggesting it should be logged at level info
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to return an error here, in the end we want to know that something went wrong. Also, I see that when we call p.mutateRecords
we don't actually have any check for the error... is that intended and do I look at it right? If it is not intended, maybe a run of a linter would help to spot those cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I left it as a warning because network blips might result in one-off failures of the request, so it might make sense to retry instead of hard failing the first time we see an error. An alternative of-course would be to add retry logic w/ back-off so that we hard fail if we see no response after say 3 tries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please return errors and because api-sdks like aws do retries, you might want to do the retry inside the pdns provider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now retries thrice and returns an error otherwise.
provider/pdns.go
Outdated
func (c *PDNSAPIClient) ListZones() ([]pgo.Zone, *http.Response, error) { | ||
zones, resp, err := c.client.ZonesApi.ListZones(c.authCtx, defaultServerID) | ||
if err != nil { | ||
log.Warnf("Unable to fetch zones. %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it okay to only log this error?
provider/pdns.go
Outdated
jso, err := json.Marshal(zone) | ||
if err != nil { | ||
log.Debugf("JSON Marshal for zone struct failed!") | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this has to be in a else clause?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as Raffo mentioned if you return the err
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the problem is that the JSON marshal itself is not used for anything except debug logging itself, does it still make sense to return an error? We'd be throwing an error for something that failed in our own logging setup. I'm still fine with returning an error, just making sure that's the intention.
startTime := time.Now() | ||
|
||
// Create | ||
for _, change := range changes.Create { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would check if debug is true before, otherwise it will loop for nothing everytime the method got called if you don't debug it. Same for Update and Delete, @ffledgling thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I'll update code accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changes.Create
could also implement Stringer interface String() string
and then plan.Changes
has to know about debug yes/no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignoring this change for now because short-circuiting on debug will require a change in the PDNS provider creation struct, which will require a sizable refactor of the code and tests, which after discussion on IRC with @szuecs and @njuettner is more trouble than it's worth at the moment.
@ffledgling I've resolved the conflict in types.go, besides that could you take a look again what Raffo was mentioning to you? I think after that we're happy to merge it. Thanks again! |
@ffledgling did you had time to take a look? I think there's only a minor part missing here. |
Commit adds: * Implementation of PowerDNS as a provider * Tests for said implementation * github.com/ffledgling/pdns-go, which provides go client bindings for PowerDNS's HTTP API, as a dependency * "pdns" as an additional option for the `--provider` flag * `--pdns-server` and `--pdns-api-key` as additional flags for PowerDNS specific configuration
👍 |
Is it possible to include basic auth authentication? |
@tuapuikia what do you mean by basic auth authentication? The provider already supports a password protected PDNS API instance. Did you have something else in mind? |
@ffledgling the current provider only support x-auth token. But library support basic auth to authenticate api behind proxy. |
@tuapuikia Do you mind opening a new issue with examples of what you want? |
When pushing individual tags, specifying the repos is mandatory.
This is the first commit in what is likely going to be a bunch of commits. It adds pdns-go bindings to the vendor directory, that will let us the pdns provider to external-dns. (Issue #363 )