Adding gandi to integration suite. Fixing bugs. (#57)

* Adding gandi to integration suite. Fixing bugs.
Fixes #35
Fixes #36

* small fixes

* gandi docs
This commit is contained in:
Craig Peterson 2017-03-27 16:03:01 -06:00 committed by GitHub
parent efb8e9bbf4
commit d205c8b4ed
6 changed files with 104 additions and 27 deletions

View file

@ -26,7 +26,8 @@ This provider does not recognize any special metadata fields unique to DNSimple.
Example javascript: Example javascript:
{% highlight js %} {% highlight js %}
var DNSIMPLE = NewDnsProvider("dnsimple", DNSIMPLE); var REG_DNSIMPLE = NewRegistrar("dnsimple", "DNSIMPLE");
var DNSIMPLE = NewDnsProvider("dnsimple", "DNSIMPLE");
D("example.tld", REG_DNSIMPLE, DnsProvider(DNSIMPLE), D("example.tld", REG_DNSIMPLE, DnsProvider(DNSIMPLE),
A("test","1.2.3.4") A("test","1.2.3.4")

38
docs/_providers/gandi.md Normal file
View file

@ -0,0 +1,38 @@
---
name: Gandi
layout: default
jsId: GANDI
---
# Gandi Provider
Gandi provides bot a registrar and a dns provider implementation.
## Configuration
In your providers config json file you must provide your Gandi.net api key:
{% highlight json %}
{
"gandi":{
"apikey": "your-gandi-key"
}
}
{% endhighlight %}
## Metadata
This provider does not recognize any special metadata fields unique to DNSimple.
## Usage
Example javascript:
{% highlight js %}
var REG_GANDI = NewRegistrar("gandi", "GANDI");
var GANDI = NewDnsProvider("gandi", "GANDI");
D("example.tld", REG_GANDI, DnsProvider(GANDI),
A("test","1.2.3.4")
);
{% endhighlight %}

View file

@ -3,11 +3,18 @@
"domain": "example.com" "domain": "example.com"
}, },
"DNSIMPLE": { "DNSIMPLE": {
//16/17: no ns records managable. Not even for subdomains.
"knownFailures": "16,17", "knownFailures": "16,17",
"domain": "$DNSIMPLE_DOMAIN", "domain": "$DNSIMPLE_DOMAIN",
"token": "$DNSIMPLE_TOKEN", "token": "$DNSIMPLE_TOKEN",
"baseurl": "https://api.sandbox.dnsimple.com" "baseurl": "https://api.sandbox.dnsimple.com"
}, },
"GANDI":{
//5: gandi does not accept ttls less than 300
"knownFailures": "5",
"domain": "$GANDI_DOMAIN",
"apikey": "$GANDI_KEY"
},
"GCLOUD": { "GCLOUD": {
"domain": "$GCLOUD_DOMAIN", "domain": "$GCLOUD_DOMAIN",
"project_id": "$GCLOUD_PROJECT", "project_id": "$GCLOUD_PROJECT",

View file

@ -191,6 +191,17 @@ func (dc *DomainConfig) Punycode() error {
return nil return nil
} }
// CombineMXs will merge the priority into the target field for all mx records.
// Useful for providers that desire them as one field.
func (dc *DomainConfig) CombineMXs() {
for _, rec := range dc.Records {
if rec.Type == "MX" {
rec.Target = fmt.Sprintf("%d %s", rec.Priority, rec.Target)
rec.Priority = 0
}
}
}
func copyObj(input interface{}, output interface{}) error { func copyObj(input interface{}, output interface{}) error {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf) enc := gob.NewEncoder(buf)

View file

@ -3,11 +3,14 @@ package gandi
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"github.com/StackExchange/dnscontrol/models" "github.com/StackExchange/dnscontrol/models"
"github.com/StackExchange/dnscontrol/providers" "github.com/StackExchange/dnscontrol/providers"
"github.com/StackExchange/dnscontrol/providers/diff" "github.com/StackExchange/dnscontrol/providers/diff"
"strings"
gandidomain "github.com/prasmussen/gandi-api/domain" gandidomain "github.com/prasmussen/gandi-api/domain"
gandirecord "github.com/prasmussen/gandi-api/domain/zone/record" gandirecord "github.com/prasmussen/gandi-api/domain/zone/record"
) )
@ -56,48 +59,63 @@ func (c *GandiApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
} }
func (c *GandiApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { func (c *GandiApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
dc.CombineMXs()
domaininfo, err := c.getDomainInfo(dc.Name) domaininfo, err := c.getDomainInfo(dc.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
foundRecords, err := c.getZoneRecords(domaininfo.ZoneId) foundRecords, err := c.getZoneRecords(domaininfo.ZoneId, dc.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
expectedRecordSets := make([]gandirecord.RecordSet, len(dc.Records)) expectedRecordSets := make([]gandirecord.RecordSet, 0, len(dc.Records))
for i, rec := range dc.Records { recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
if rec.Type == "MX" { for _, rec := range dc.Records {
rec.Target = fmt.Sprintf("%d %s", rec.Priority, rec.Target) if rec.TTL < 300 {
log.Printf("WARNING: Gandi does not support ttls < 300. %s will not be set to %d.", rec.NameFQDN, rec.TTL)
rec.TTL = 300
} }
if rec.Type == "TXT" { if rec.Type == "TXT" {
rec.Target = "\"" + rec.Target + "\"" // FIXME(tlim): Should do proper quoting. rec.Target = "\"" + rec.Target + "\"" // FIXME(tlim): Should do proper quoting.
} }
expectedRecordSets[i] = gandirecord.RecordSet{} if rec.Type == "NS" && rec.Name == "@" {
expectedRecordSets[i]["type"] = rec.Type if !strings.HasSuffix(rec.Target, ".gandi.net.") {
expectedRecordSets[i]["name"] = rec.Name log.Printf("WARNING: Gandi does not support changing apex NS records. %s will not be added.", rec.Target)
expectedRecordSets[i]["value"] = rec.Target
expectedRecordSets[i]["ttl"] = rec.TTL
} }
continue
}
rs := gandirecord.RecordSet{
"type": rec.Type,
"name": rec.Name,
"value": rec.Target,
"ttl": rec.TTL,
}
expectedRecordSets = append(expectedRecordSets, rs)
recordsToKeep = append(recordsToKeep, rec)
}
dc.Records = recordsToKeep
differ := diff.New(dc) differ := diff.New(dc)
_, create, del, mod := differ.IncrementalDiff(foundRecords) _, create, del, mod := differ.IncrementalDiff(foundRecords)
// Print a list of changes. Generate an actual change that is the zone // Print a list of changes. Generate an actual change that is the zone
changes := false changes := false
desc := ""
for _, i := range create { for _, i := range create {
changes = true changes = true
fmt.Println(i) desc += "\n" + i.String()
} }
for _, i := range del { for _, i := range del {
changes = true changes = true
fmt.Println(i) desc += "\n" + i.String()
} }
for _, i := range mod { for _, i := range mod {
changes = true changes = true
fmt.Println(i) desc += "\n" + i.String()
} }
msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)", dc.Name, len(dc.Records)) msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc)
corrections := []*models.Correction{} corrections := []*models.Correction{}
if changes { if changes {
corrections = append(corrections, corrections = append(corrections,

View file

@ -10,6 +10,7 @@ import (
gandiversion "github.com/prasmussen/gandi-api/domain/zone/version" gandiversion "github.com/prasmussen/gandi-api/domain/zone/version"
"github.com/StackExchange/dnscontrol/models" "github.com/StackExchange/dnscontrol/models"
"github.com/miekg/dns/dnsutil"
) )
// fetchDomainList gets list of domains for account. Cache ids for easy lookup. // fetchDomainList gets list of domains for account. Cache ids for easy lookup.
@ -39,7 +40,7 @@ func (c *GandiApi) fetchDomainInfo(fqdn string) (*gandidomain.DomainInfo, error)
} }
// getRecordsForDomain returns a list of records for a zone. // getRecordsForDomain returns a list of records for a zone.
func (c *GandiApi) getZoneRecords(zoneid int64) ([]*models.RecordConfig, error) { func (c *GandiApi) getZoneRecords(zoneid int64, origin string) ([]*models.RecordConfig, error) {
gc := gandiclient.New(c.ApiKey, gandiclient.Production) gc := gandiclient.New(c.ApiKey, gandiclient.Production)
record := gandirecord.New(gc) record := gandirecord.New(gc)
recs, err := record.List(zoneid, 0) recs, err := record.List(zoneid, 0)
@ -48,7 +49,7 @@ func (c *GandiApi) getZoneRecords(zoneid int64) ([]*models.RecordConfig, error)
} }
rcs := make([]*models.RecordConfig, 0, len(recs)) rcs := make([]*models.RecordConfig, 0, len(recs))
for _, r := range recs { for _, r := range recs {
rcs = append(rcs, convert(r)) rcs = append(rcs, convert(r, origin))
} }
return rcs, nil return rcs, nil
} }
@ -133,37 +134,37 @@ func (c *GandiApi) activateVersion(zone_id, version_id int64) (bool, error) {
return version.Set(zone_id, version_id) return version.Set(zone_id, version_id)
} }
func (c *GandiApi) createGandiZone(domainname string, zone_id int64, records []gandirecord.RecordSet) error { func (c *GandiApi) createGandiZone(domainname string, zoneID int64, records []gandirecord.RecordSet) error {
// Get the zone_id of the zone we'll be updating. // Get the zone_id of the zone we'll be updating.
zoneinfo, err := c.getZoneInfo(zone_id) zoneinfo, err := c.getZoneInfo(zoneID)
if err != nil { if err != nil {
return err return err
} }
//fmt.Println("ZONEINFO:", zoneinfo) //fmt.Println("ZONEINFO:", zoneinfo)
zone_id, err = c.getEditableZone(domainname, zoneinfo) zoneID, err = c.getEditableZone(domainname, zoneinfo)
if err != nil { if err != nil {
return err return err
} }
// Get the version_id of the zone we're updating. // Get the versionID of the zone we're updating.
version_id, err := c.makeEditableZone(zone_id) versionID, err := c.makeEditableZone(zoneID)
if err != nil { if err != nil {
return err return err
} }
// Update the new version. // Update the new version.
_, err = c.setZoneRecords(zone_id, version_id, records) _, err = c.setZoneRecords(zoneID, versionID, records)
if err != nil { if err != nil {
return err return err
} }
// Activate zone version // Activate zone version
_, err = c.activateVersion(zone_id, version_id) _, err = c.activateVersion(zoneID, versionID)
if err != nil { if err != nil {
return err return err
} }
_, err = c.setZones(domainname, zone_id) _, err = c.setZones(domainname, zoneID)
if err != nil { if err != nil {
return err return err
} }
@ -171,9 +172,10 @@ func (c *GandiApi) createGandiZone(domainname string, zone_id int64, records []g
return nil return nil
} }
func convert(r *gandirecord.RecordInfo) *models.RecordConfig { func convert(r *gandirecord.RecordInfo, origin string) *models.RecordConfig {
return &models.RecordConfig{ return &models.RecordConfig{
NameFQDN: r.Name, NameFQDN: dnsutil.AddOrigin(r.Name, origin),
Name: r.Name,
Type: r.Type, Type: r.Type,
Original: r, Original: r,
Target: r.Value, Target: r.Value,