-
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
Added Exoscale as Provider #625
Merged
njuettner
merged 16 commits into
kubernetes-sigs:master
from
FaKod:external-dns-exoscale
Jul 13, 2018
Merged
Changes from 2 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
8003a3f
initial Exoscale Provider
FaKod a487d38
exoscale: rename exo into exoscale
07e7103
implemented dryRun
FaKod 635427f
did not match domains
FaKod 14cefa0
added UpdateNew
FaKod 56e4fe8
added an Exoscale tutorial
FaKod df03cb8
added version tag
FaKod f38c347
Merge branch 'master' into external-dns-exoscale
njuettner 717ee84
Merge branch 'master' into external-dns-exoscale
njuettner 6ed7b8d
exoscale: fix boilerplate header
826d874
Merge pull request #4 from greut/fix-header
FaKod a78b209
using defaultConfig n ow
FaKod 42ff664
fix tests
FaKod 5cf6ce9
better test
FaKod 11df25d
revert commit
FaKod 0fd3a7a
added list nodes
FaKod File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package provider | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/exoscale/egoscale" | ||
"github.com/kubernetes-incubator/external-dns/endpoint" | ||
"github.com/kubernetes-incubator/external-dns/plan" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
// EgoscaleClientI for replaceable implementation | ||
type EgoscaleClientI interface { | ||
GetRecords(string) ([]egoscale.DNSRecord, error) | ||
GetDomains() ([]egoscale.DNSDomain, error) | ||
CreateRecord(string, egoscale.DNSRecord) (*egoscale.DNSRecord, error) | ||
DeleteRecord(string, int64) error | ||
} | ||
|
||
// ExoscaleProvider initialized as dns provider with no records | ||
type ExoscaleProvider struct { | ||
domain DomainFilter | ||
client EgoscaleClientI | ||
filter *zoneFilter | ||
OnApplyChanges func(changes *plan.Changes) | ||
} | ||
|
||
// ExoscaleOption for Provider options | ||
type ExoscaleOption func(*ExoscaleProvider) | ||
|
||
// NewExoscaleProvider returns ExoscaleProvider DNS provider interface implementation | ||
func NewExoscaleProvider(endpoint, apiKey, apiSecret string, opts ...ExoscaleOption) *ExoscaleProvider { | ||
client := egoscale.NewClient(endpoint, apiKey, apiSecret) | ||
return NewExoscaleProviderWithClient(endpoint, apiKey, apiSecret, client, opts...) | ||
} | ||
|
||
// NewExoscaleProviderWithClient returns ExoscaleProvider DNS provider interface implementation (Client provided) | ||
func NewExoscaleProviderWithClient(endpoint, apiKey, apiSecret string, client EgoscaleClientI, opts ...ExoscaleOption) *ExoscaleProvider { | ||
ep := &ExoscaleProvider{ | ||
filter: &zoneFilter{}, | ||
OnApplyChanges: func(changes *plan.Changes) {}, | ||
domain: NewDomainFilter([]string{""}), | ||
client: client, | ||
} | ||
for _, opt := range opts { | ||
opt(ep) | ||
} | ||
return ep | ||
} | ||
|
||
func (ep *ExoscaleProvider) getZones() (map[int64]string, error) { | ||
dom, err := ep.client.GetDomains() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
zones := map[int64]string{} | ||
for _, d := range dom { | ||
zones[d.ID] = d.Name | ||
} | ||
return zones, nil | ||
} | ||
|
||
// ApplyChanges simply modifies DNS via exoscale API | ||
func (ep *ExoscaleProvider) ApplyChanges(changes *plan.Changes) error { | ||
ep.OnApplyChanges(changes) | ||
|
||
zones, err := ep.getZones() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, epoint := range changes.Create { | ||
if ep.domain.Match(epoint.DNSName) { | ||
if zoneID, name := ep.filter.EndpointZoneID(epoint, zones); zoneID != 0 { | ||
rec := egoscale.DNSRecord{ | ||
Name: name, | ||
RecordType: epoint.RecordType, | ||
TTL: int(epoint.RecordTTL), | ||
Content: epoint.Targets[0], | ||
} | ||
_, err := ep.client.CreateRecord(zones[zoneID], rec) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
} | ||
for _, epoint := range changes.UpdateNew { | ||
log.Debugf("UPDATE-NEW (ignored) for epoint: %+v", epoint) | ||
} | ||
for _, epoint := range changes.UpdateOld { | ||
log.Debugf("UPDATE-OLD (ignored) for epoint: %+v", epoint) | ||
} | ||
for _, epoint := range changes.Delete { | ||
if zoneID, name := ep.filter.EndpointZoneID(epoint, zones); zoneID != 0 { | ||
records, err := ep.client.GetRecords(zones[zoneID]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, r := range records { | ||
if r.Name == name { | ||
if err := ep.client.DeleteRecord(zones[zoneID], r.ID); err != nil { | ||
return err | ||
} | ||
break | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Records returns the list of endpoints | ||
func (ep *ExoscaleProvider) Records() ([]*endpoint.Endpoint, error) { | ||
endpoints := make([]*endpoint.Endpoint, 0) | ||
|
||
dom, err := ep.client.GetDomains() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, d := range dom { | ||
record, err := ep.client.GetRecords(d.Name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, r := range record { | ||
switch r.RecordType { | ||
case "A", "AAAA", "CNAME", "TXT": | ||
break | ||
default: | ||
continue | ||
} | ||
ep := endpoint.NewEndpointWithTTL(r.Name+"."+d.Name, r.RecordType, endpoint.TTL(r.TTL), r.Content) | ||
endpoints = append(endpoints, ep) | ||
} | ||
} | ||
|
||
log.Infof("called Records() with %d items", len(endpoints)) | ||
return endpoints, nil | ||
} | ||
|
||
// ExoscaleWithDomain modifies the domain on which dns zones are filtered | ||
func ExoscaleWithDomain(domainFilter DomainFilter) ExoscaleOption { | ||
return func(p *ExoscaleProvider) { | ||
p.domain = domainFilter | ||
} | ||
} | ||
|
||
// ExoscaleWithLogging injects logging when ApplyChanges is called | ||
func ExoscaleWithLogging() ExoscaleOption { | ||
return func(p *ExoscaleProvider) { | ||
p.OnApplyChanges = func(changes *plan.Changes) { | ||
for _, v := range changes.Create { | ||
log.Infof("CREATE: %v", v) | ||
} | ||
for _, v := range changes.UpdateOld { | ||
log.Infof("UPDATE (old): %v", v) | ||
} | ||
for _, v := range changes.UpdateNew { | ||
log.Infof("UPDATE (new): %v", v) | ||
} | ||
for _, v := range changes.Delete { | ||
log.Infof("DELETE: %v", v) | ||
} | ||
} | ||
} | ||
} | ||
|
||
type zoneFilter struct { | ||
domain string | ||
} | ||
|
||
// Zones filters map[zoneID]zoneName for names having f.domain as suffix | ||
func (f *zoneFilter) Zones(zones map[int64]string) map[int64]string { | ||
result := map[int64]string{} | ||
for zoneID, zoneName := range zones { | ||
if strings.HasSuffix(zoneName, f.domain) { | ||
result[zoneID] = zoneName | ||
} | ||
} | ||
return result | ||
} | ||
|
||
// EndpointZoneID determines zoneID for endpoint from map[zoneID]zoneName by taking longest suffix zoneName match in endpoint DNSName | ||
// returns 0 if no match found | ||
func (f *zoneFilter) EndpointZoneID(endpoint *endpoint.Endpoint, zones map[int64]string) (zoneID int64, name string) { | ||
var matchZoneID int64 | ||
var matchZoneName string | ||
for zoneID, zoneName := range zones { | ||
if strings.HasSuffix(endpoint.DNSName, "."+zoneName) && len(zoneName) > len(matchZoneName) { | ||
matchZoneName = zoneName | ||
matchZoneID = zoneID | ||
name = strings.TrimSuffix(endpoint.DNSName, "."+zoneName) | ||
} | ||
} | ||
return matchZoneID, name | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Just to understand it a bit better, why do you ignore the updates? I just only see that you're looping over update changes even if you would use a logging level which is not debug?
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 must admit that for my use case it seems to be sufficient to only support create and delete. Is update mandatory?
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'm just only asking because I never saw that someone wouldn't want to update their records. Could there people which might need it?
If you don't use it at all I'm not seeing any point looping over updates which doesn't do anything execept logging.