Skip to content

Commit

Permalink
Merge branch 'master' into raffo/provider-structure-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Raffo committed May 11, 2020
2 parents 955a805 + 49d3e68 commit 9748af1
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 104 deletions.
2 changes: 1 addition & 1 deletion docs/ttl.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ PRs welcome!

Notes
=====
When the `external-dns.alpha.kubernetes.io/ttl` annotation is not provided, the TTL will default to 0 seconds and `enpoint.TTL.isConfigured()` will be false.
When the `external-dns.alpha.kubernetes.io/ttl` annotation is not provided, the TTL will default to 0 seconds and `endpoint.TTL.isConfigured()` will be false.

### AWS Provider
The AWS Provider overrides the value to 300s when the TTL is 0.
Expand Down
16 changes: 8 additions & 8 deletions docs/tutorials/istio.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ It is meant to supplement the other provider-specific setup tutorials.

* Manifest (for clusters without RBAC enabled)
* Manifest (for clusters with RBAC enabled)
* Update existing Existing DNS Deployment
* Update existing ExternalDNS Deployment

### Manifest (for clusters without RBAC enabled)

Expand Down Expand Up @@ -48,7 +48,7 @@ kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
Expand All @@ -66,7 +66,7 @@ rules:
resources: ["gateways"]
verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
Expand Down Expand Up @@ -110,7 +110,7 @@ spec:
- --txt-owner-id=my-identifier
```
### Update existing Existing DNS Deployment
### Update existing ExternalDNS Deployment
* For clusters with running `external-dns`, you can just update the deployment.
* With access to the `kube-system` namespace, update the existing `external-dns` deployment.
Expand All @@ -130,7 +130,7 @@ kubectl patch clusterrole external-dns --type='json' \
-p='[{"op": "add", "path": "/rules/4", "value": { "apiGroups": [ "networking.istio.io"], "resources": ["gateways"],"verbs": ["get", "watch", "list" ]} }]'
```

### Verify External DNS works (Gateway example)
### Verify ExternalDNS works (Gateway example)

Follow the [Istio ingress traffic tutorial](https://istio.io/docs/tasks/traffic-management/ingress/)
to deploy a sample service that will be exposed outside of the service mesh.
Expand Down Expand Up @@ -217,7 +217,7 @@ transfer-encoding: chunked

**Note:** The `-H` flag in the original Istio tutorial is no longer necessary in the `curl` commands.

### Debug External-DNS
### Debug ExternalDNS

* Look for the deployment pod to see the status

Expand All @@ -239,8 +239,8 @@ At this point, you can `create` or `update` any `Istio Gateway` object with `hos

```console
time="2020-01-17T06:08:08Z" level=info msg="Desired change: CREATE httpbin.example.com A"
time="2020-01-17T06:08:08Z" level=info msg="Desired change: CREATE httpbin.example.comm TXT"
time="2020-01-17T06:08:08Z" level=info msg="2 record(s) in zone example.comm. were successfully updated"
time="2020-01-17T06:08:08Z" level=info msg="Desired change: CREATE httpbin.example.com TXT"
time="2020-01-17T06:08:08Z" level=info msg="2 record(s) in zone example.com. were successfully updated"
time="2020-01-17T06:09:08Z" level=info msg="All records are already up to date, there are no changes for the matching hosted zones"
```

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5
github.com/digitalocean/godo v1.34.0
github.com/dnaeon/go-vcr v1.0.1 // indirect
github.com/dnsimple/dnsimple-go v0.14.0
github.com/dnsimple/dnsimple-go v0.60.0
github.com/exoscale/egoscale v0.18.1
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
github.com/go-resty/resty v1.8.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v0.14.0 h1:JGYtVVA/uHc91q0LjDWqR1oVj6EGu9Kn0lMRxjH/w30=
github.com/dnsimple/dnsimple-go v0.14.0/go.mod h1:0FYu4qVNv/UcfZPNwa9zi68IkggJu3TIwM54D7rhmI4=
github.com/dnsimple/dnsimple-go v0.60.0 h1:N+q+ML1CZGf+5r4udu9Opy7WJNtOaFT9aM86Af9gLhk=
github.com/dnsimple/dnsimple-go v0.60.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
Expand Down
142 changes: 81 additions & 61 deletions provider/dnsimple/dnsimple.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,68 +25,60 @@ import (

"github.com/dnsimple/dnsimple-go/dnsimple"
log "github.com/sirupsen/logrus"
"golang.org/x/oauth2"

"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
)

const dnsimpleRecordTTL = 3600 // Default TTL of 1 hour if not set (DNSimple's default)

type identityService struct {
type dnsimpleIdentityService struct {
service *dnsimple.IdentityService
}

func (i identityService) Whoami() (*dnsimple.WhoamiResponse, error) {
return i.service.Whoami()
func (i dnsimpleIdentityService) Whoami(ctx context.Context) (*dnsimple.WhoamiResponse, error) {
return i.service.Whoami(ctx)
}

// Returns the account ID given dnsimple credentials
func (p *dnsimpleProvider) GetAccountID(credentials dnsimple.Credentials, client dnsimple.Client) (accountID string, err error) {
// get DNSimple client accountID
whoamiResponse, err := client.Identity.Whoami()
if err != nil {
return "", err
}
return strconv.Itoa(whoamiResponse.Data.Account.ID), nil
}

// dnsimpleZoneServiceInterface is an interface that contains all necessary zone services from dnsimple
// dnsimpleZoneServiceInterface is an interface that contains all necessary zone services from DNSimple
type dnsimpleZoneServiceInterface interface {
ListZones(accountID string, options *dnsimple.ZoneListOptions) (*dnsimple.ZonesResponse, error)
ListRecords(accountID string, zoneID string, options *dnsimple.ZoneRecordListOptions) (*dnsimple.ZoneRecordsResponse, error)
CreateRecord(accountID string, zoneID string, recordAttributes dnsimple.ZoneRecord) (*dnsimple.ZoneRecordResponse, error)
DeleteRecord(accountID string, zoneID string, recordID int) (*dnsimple.ZoneRecordResponse, error)
UpdateRecord(accountID string, zoneID string, recordID int, recordAttributes dnsimple.ZoneRecord) (*dnsimple.ZoneRecordResponse, error)
ListZones(ctx context.Context, accountID string, options *dnsimple.ZoneListOptions) (*dnsimple.ZonesResponse, error)
ListRecords(ctx context.Context, accountID string, zoneID string, options *dnsimple.ZoneRecordListOptions) (*dnsimple.ZoneRecordsResponse, error)
CreateRecord(ctx context.Context, accountID string, zoneID string, recordAttributes dnsimple.ZoneRecordAttributes) (*dnsimple.ZoneRecordResponse, error)
DeleteRecord(ctx context.Context, accountID string, zoneID string, recordID int64) (*dnsimple.ZoneRecordResponse, error)
UpdateRecord(ctx context.Context, accountID string, zoneID string, recordID int64, recordAttributes dnsimple.ZoneRecordAttributes) (*dnsimple.ZoneRecordResponse, error)
}

type dnsimpleZoneService struct {
service *dnsimple.ZonesService
}

func (z dnsimpleZoneService) ListZones(accountID string, options *dnsimple.ZoneListOptions) (*dnsimple.ZonesResponse, error) {
return z.service.ListZones(accountID, options)
func (z dnsimpleZoneService) ListZones(ctx context.Context, accountID string, options *dnsimple.ZoneListOptions) (*dnsimple.ZonesResponse, error) {
return z.service.ListZones(ctx, accountID, options)
}

func (z dnsimpleZoneService) ListRecords(accountID string, zoneID string, options *dnsimple.ZoneRecordListOptions) (*dnsimple.ZoneRecordsResponse, error) {
return z.service.ListRecords(accountID, zoneID, options)
func (z dnsimpleZoneService) ListRecords(ctx context.Context, accountID string, zoneID string, options *dnsimple.ZoneRecordListOptions) (*dnsimple.ZoneRecordsResponse, error) {
return z.service.ListRecords(ctx, accountID, zoneID, options)
}

func (z dnsimpleZoneService) CreateRecord(accountID string, zoneID string, recordAttributes dnsimple.ZoneRecord) (*dnsimple.ZoneRecordResponse, error) {
return z.service.CreateRecord(accountID, zoneID, recordAttributes)
func (z dnsimpleZoneService) CreateRecord(ctx context.Context, accountID string, zoneID string, recordAttributes dnsimple.ZoneRecordAttributes) (*dnsimple.ZoneRecordResponse, error) {
return z.service.CreateRecord(ctx, accountID, zoneID, recordAttributes)
}

func (z dnsimpleZoneService) DeleteRecord(accountID string, zoneID string, recordID int) (*dnsimple.ZoneRecordResponse, error) {
return z.service.DeleteRecord(accountID, zoneID, recordID)
func (z dnsimpleZoneService) DeleteRecord(ctx context.Context, accountID string, zoneID string, recordID int64) (*dnsimple.ZoneRecordResponse, error) {
return z.service.DeleteRecord(ctx, accountID, zoneID, recordID)
}

func (z dnsimpleZoneService) UpdateRecord(accountID string, zoneID string, recordID int, recordAttributes dnsimple.ZoneRecord) (*dnsimple.ZoneRecordResponse, error) {
return z.service.UpdateRecord(accountID, zoneID, recordID, recordAttributes)
func (z dnsimpleZoneService) UpdateRecord(ctx context.Context, accountID string, zoneID string, recordID int64, recordAttributes dnsimple.ZoneRecordAttributes) (*dnsimple.ZoneRecordResponse, error) {
return z.service.UpdateRecord(ctx, accountID, zoneID, recordID, recordAttributes)
}

type dnsimpleProvider struct {
client dnsimpleZoneServiceInterface
identity identityService
identity dnsimpleIdentityService
accountID string
domainFilter endpoint.DomainFilter
zoneIDFilter provider.ZoneIDFilter
Expand All @@ -110,30 +102,47 @@ func NewDnsimpleProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provid
if len(oauthToken) == 0 {
return nil, fmt.Errorf("No dnsimple oauth token provided")
}
client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(oauthToken))

ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: oauthToken})
tc := oauth2.NewClient(context.Background(), ts)

client := dnsimple.NewClient(tc)
client.SetUserAgent(fmt.Sprintf("Kubernetes ExternalDNS/%s", externaldns.Version))

provider := &dnsimpleProvider{
client: dnsimpleZoneService{service: client.Zones},
identity: identityService{service: client.Identity},
identity: dnsimpleIdentityService{service: client.Identity},
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
dryRun: dryRun,
}
whoamiResponse, err := provider.identity.service.Whoami()

whoamiResponse, err := provider.identity.Whoami(context.Background())
if err != nil {
return nil, err
}
provider.accountID = strconv.Itoa(whoamiResponse.Data.Account.ID)
provider.accountID = int64ToString(whoamiResponse.Data.Account.ID)
return provider, nil
}

// GetAccountID returns the account ID given DNSimple credentials.
func (p *dnsimpleProvider) GetAccountID(ctx context.Context) (accountID string, err error) {
// get DNSimple client accountID
whoamiResponse, err := p.identity.Whoami(ctx)
if err != nil {
return "", err
}
return int64ToString(whoamiResponse.Data.Account.ID), nil
}

// Returns a list of filtered Zones
func (p *dnsimpleProvider) Zones() (map[string]dnsimple.Zone, error) {
func (p *dnsimpleProvider) Zones(ctx context.Context) (map[string]dnsimple.Zone, error) {
zones := make(map[string]dnsimple.Zone)
page := 1
listOptions := &dnsimple.ZoneListOptions{}
for {
listOptions.Page = page
zonesResponse, err := p.client.ListZones(p.accountID, listOptions)
listOptions.Page = &page
zonesResponse, err := p.client.ListZones(ctx, p.accountID, listOptions)
if err != nil {
return nil, err
}
Expand All @@ -142,11 +151,11 @@ func (p *dnsimpleProvider) Zones() (map[string]dnsimple.Zone, error) {
continue
}

if !p.zoneIDFilter.Match(strconv.Itoa(zone.ID)) {
if !p.zoneIDFilter.Match(int64ToString(zone.ID)) {
continue
}

zones[strconv.Itoa(zone.ID)] = zone
zones[int64ToString(zone.ID)] = zone
}

page++
Expand All @@ -159,16 +168,16 @@ func (p *dnsimpleProvider) Zones() (map[string]dnsimple.Zone, error) {

// Records returns a list of endpoints in a given zone
func (p *dnsimpleProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, _ error) {
zones, err := p.Zones()
zones, err := p.Zones(ctx)
if err != nil {
return nil, err
}
for _, zone := range zones {
page := 1
listOptions := &dnsimple.ZoneRecordListOptions{}
for {
listOptions.Page = page
records, err := p.client.ListRecords(p.accountID, zone.Name, listOptions)
listOptions.Page = &page
records, err := p.client.ListRecords(ctx, p.accountID, zone.Name, listOptions)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -225,12 +234,12 @@ func newDnsimpleChanges(action string, endpoints []*endpoint.Endpoint) []*dnsimp
}

// submitChanges takes a zone and a collection of changes and makes all changes from the collection
func (p *dnsimpleProvider) submitChanges(changes []*dnsimpleChange) error {
func (p *dnsimpleProvider) submitChanges(ctx context.Context, changes []*dnsimpleChange) error {
if len(changes) == 0 {
log.Infof("All records are already up to date")
return nil
}
zones, err := p.Zones()
zones, err := p.Zones(ctx)
if err != nil {
return err
}
Expand All @@ -249,28 +258,35 @@ func (p *dnsimpleProvider) submitChanges(changes []*dnsimpleChange) error {
change.ResourceRecordSet.Name = strings.TrimSuffix(change.ResourceRecordSet.Name, fmt.Sprintf(".%s", zone.Name))
}

recordAttributes := dnsimple.ZoneRecordAttributes{
Name: &change.ResourceRecordSet.Name,
Type: change.ResourceRecordSet.Type,
Content: change.ResourceRecordSet.Content,
TTL: change.ResourceRecordSet.TTL,
}

if !p.dryRun {
switch change.Action {
case dnsimpleCreate:
_, err := p.client.CreateRecord(p.accountID, zone.Name, change.ResourceRecordSet)
_, err := p.client.CreateRecord(ctx, p.accountID, zone.Name, recordAttributes)
if err != nil {
return err
}
case dnsimpleDelete:
recordID, err := p.GetRecordID(zone.Name, change.ResourceRecordSet.Name)
recordID, err := p.GetRecordID(ctx, zone.Name, *recordAttributes.Name)
if err != nil {
return err
}
_, err = p.client.DeleteRecord(p.accountID, zone.Name, recordID)
_, err = p.client.DeleteRecord(ctx, p.accountID, zone.Name, recordID)
if err != nil {
return err
}
case dnsimpleUpdate:
recordID, err := p.GetRecordID(zone.Name, change.ResourceRecordSet.Name)
recordID, err := p.GetRecordID(ctx, zone.Name, *recordAttributes.Name)
if err != nil {
return err
}
_, err = p.client.UpdateRecord(p.accountID, zone.Name, recordID, change.ResourceRecordSet)
_, err = p.client.UpdateRecord(ctx, p.accountID, zone.Name, recordID, recordAttributes)
if err != nil {
return err
}
Expand All @@ -280,13 +296,13 @@ func (p *dnsimpleProvider) submitChanges(changes []*dnsimpleChange) error {
return nil
}

// Returns the record ID for a given record name and zone
func (p *dnsimpleProvider) GetRecordID(zone string, recordName string) (recordID int, err error) {
// GetRecordID returns the record ID for a given record name and zone.
func (p *dnsimpleProvider) GetRecordID(ctx context.Context, zone string, recordName string) (recordID int64, err error) {
page := 1
listOptions := &dnsimple.ZoneRecordListOptions{Name: recordName}
listOptions := &dnsimple.ZoneRecordListOptions{Name: &recordName}
for {
listOptions.Page = page
records, err := p.client.ListRecords(p.accountID, zone, listOptions)
listOptions.Page = &page
records, err := p.client.ListRecords(ctx, p.accountID, zone, listOptions)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -320,18 +336,18 @@ func dnsimpleSuitableZone(hostname string, zones map[string]dnsimple.Zone) *dnsi
}

// CreateRecords creates records for a given slice of endpoints
func (p *dnsimpleProvider) CreateRecords(endpoints []*endpoint.Endpoint) error {
return p.submitChanges(newDnsimpleChanges(dnsimpleCreate, endpoints))
func (p *dnsimpleProvider) CreateRecords(ctx context.Context, endpoints []*endpoint.Endpoint) error {
return p.submitChanges(ctx, newDnsimpleChanges(dnsimpleCreate, endpoints))
}

// DeleteRecords deletes records for a given slice of endpoints
func (p *dnsimpleProvider) DeleteRecords(endpoints []*endpoint.Endpoint) error {
return p.submitChanges(newDnsimpleChanges(dnsimpleDelete, endpoints))
func (p *dnsimpleProvider) DeleteRecords(ctx context.Context, endpoints []*endpoint.Endpoint) error {
return p.submitChanges(ctx, newDnsimpleChanges(dnsimpleDelete, endpoints))
}

// UpdateRecords updates records for a given slice of endpoints
func (p *dnsimpleProvider) UpdateRecords(endpoints []*endpoint.Endpoint) error {
return p.submitChanges(newDnsimpleChanges(dnsimpleUpdate, endpoints))
func (p *dnsimpleProvider) UpdateRecords(ctx context.Context, endpoints []*endpoint.Endpoint) error {
return p.submitChanges(ctx, newDnsimpleChanges(dnsimpleUpdate, endpoints))
}

// ApplyChanges applies a given set of changes
Expand All @@ -342,5 +358,9 @@ func (p *dnsimpleProvider) ApplyChanges(ctx context.Context, changes *plan.Chang
combinedChanges = append(combinedChanges, newDnsimpleChanges(dnsimpleUpdate, changes.UpdateNew)...)
combinedChanges = append(combinedChanges, newDnsimpleChanges(dnsimpleDelete, changes.Delete)...)

return p.submitChanges(combinedChanges)
return p.submitChanges(ctx, combinedChanges)
}

func int64ToString(i int64) string {
return strconv.FormatInt(i, 10)
}
Loading

0 comments on commit 9748af1

Please sign in to comment.