github.com/pmoroney/dnscontrol@v0.2.4-0.20171024134423-fad98f73f44a/providers/gandi/gandiProvider.go (about) 1 package gandi 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 8 "github.com/StackExchange/dnscontrol/models" 9 "github.com/StackExchange/dnscontrol/providers" 10 "github.com/StackExchange/dnscontrol/providers/diff" 11 "github.com/pkg/errors" 12 13 "strings" 14 15 gandidomain "github.com/prasmussen/gandi-api/domain" 16 gandirecord "github.com/prasmussen/gandi-api/domain/zone/record" 17 ) 18 19 /* 20 21 Gandi API DNS provider: 22 23 Info required in `creds.json`: 24 - apikey 25 26 */ 27 28 var docNotes = providers.DocumentationNotes{ 29 providers.DocCreateDomains: providers.Cannot("Can only manage domains registered through their service"), 30 providers.DocOfficiallySupported: providers.Cannot(), 31 } 32 33 func init() { 34 providers.RegisterDomainServiceProviderType("GANDI", newGandi, providers.CanUsePTR, 35 providers.CanUseSRV, docNotes, providers.CantUseNOPURGE) 36 } 37 38 type GandiApi struct { 39 ApiKey string 40 domainIndex map[string]int64 // Map of domainname to index 41 nameservers map[string][]*models.Nameserver 42 ZoneId int64 43 } 44 45 type gandiRecord struct { 46 gandirecord.RecordInfo 47 } 48 49 func (c *GandiApi) getDomainInfo(domain string) (*gandidomain.DomainInfo, error) { 50 if err := c.fetchDomainList(); err != nil { 51 return nil, err 52 } 53 _, ok := c.domainIndex[domain] 54 if !ok { 55 return nil, fmt.Errorf("%s not listed in zones for gandi account", domain) 56 } 57 return c.fetchDomainInfo(domain) 58 } 59 60 func (c *GandiApi) GetNameservers(domain string) ([]*models.Nameserver, error) { 61 domaininfo, err := c.getDomainInfo(domain) 62 if err != nil { 63 return nil, err 64 } 65 ns := []*models.Nameserver{} 66 for _, nsname := range domaininfo.Nameservers { 67 ns = append(ns, &models.Nameserver{Name: nsname}) 68 } 69 return ns, nil 70 } 71 72 func (c *GandiApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { 73 dc.Punycode() 74 dc.CombineSRVs() 75 dc.CombineMXs() 76 domaininfo, err := c.getDomainInfo(dc.Name) 77 if err != nil { 78 return nil, err 79 } 80 foundRecords, err := c.getZoneRecords(domaininfo.ZoneId, dc.Name) 81 if err != nil { 82 return nil, err 83 } 84 85 expectedRecordSets := make([]gandirecord.RecordSet, 0, len(dc.Records)) 86 recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records)) 87 for _, rec := range dc.Records { 88 if rec.TTL < 300 { 89 log.Printf("WARNING: Gandi does not support ttls < 300. %s will not be set to %d.", rec.NameFQDN, rec.TTL) 90 rec.TTL = 300 91 } 92 if rec.TTL > 2592000 { 93 return nil, errors.Errorf("ERROR: Gandi does not support TTLs > 30 days (TTL=%d)", rec.TTL) 94 } 95 if rec.Type == "TXT" { 96 rec.Target = "\"" + rec.Target + "\"" // FIXME(tlim): Should do proper quoting. 97 } 98 if rec.Type == "NS" && rec.Name == "@" { 99 if !strings.HasSuffix(rec.Target, ".gandi.net.") { 100 log.Printf("WARNING: Gandi does not support changing apex NS records. %s will not be added.", rec.Target) 101 } 102 continue 103 } 104 rs := gandirecord.RecordSet{ 105 "type": rec.Type, 106 "name": rec.Name, 107 "value": rec.Target, 108 "ttl": rec.TTL, 109 } 110 expectedRecordSets = append(expectedRecordSets, rs) 111 recordsToKeep = append(recordsToKeep, rec) 112 } 113 dc.Records = recordsToKeep 114 differ := diff.New(dc) 115 _, create, del, mod := differ.IncrementalDiff(foundRecords) 116 117 // Print a list of changes. Generate an actual change that is the zone 118 changes := false 119 desc := "" 120 for _, i := range create { 121 changes = true 122 desc += "\n" + i.String() 123 } 124 for _, i := range del { 125 changes = true 126 desc += "\n" + i.String() 127 } 128 for _, i := range mod { 129 changes = true 130 desc += "\n" + i.String() 131 } 132 133 msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc) 134 corrections := []*models.Correction{} 135 if changes { 136 corrections = append(corrections, 137 &models.Correction{ 138 Msg: msg, 139 F: func() error { 140 fmt.Printf("CREATING ZONE: %v\n", dc.Name) 141 return c.createGandiZone(dc.Name, domaininfo.ZoneId, expectedRecordSets) 142 }, 143 }) 144 } 145 146 return corrections, nil 147 } 148 149 func newGandi(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { 150 api := &GandiApi{} 151 api.ApiKey = m["apikey"] 152 if api.ApiKey == "" { 153 return nil, fmt.Errorf("Gandi apikey must be provided.") 154 } 155 156 return api, nil 157 }