github.com/pmoroney/dnscontrol@v0.2.4-0.20171024134423-fad98f73f44a/providers/gandi/protocol.go (about) 1 package gandi 2 3 import ( 4 "fmt" 5 6 gandiclient "github.com/prasmussen/gandi-api/client" 7 gandidomain "github.com/prasmussen/gandi-api/domain" 8 gandizone "github.com/prasmussen/gandi-api/domain/zone" 9 gandirecord "github.com/prasmussen/gandi-api/domain/zone/record" 10 gandiversion "github.com/prasmussen/gandi-api/domain/zone/version" 11 12 "github.com/StackExchange/dnscontrol/models" 13 "github.com/miekg/dns/dnsutil" 14 ) 15 16 // fetchDomainList gets list of domains for account. Cache ids for easy lookup. 17 func (c *GandiApi) fetchDomainList() error { 18 if c.domainIndex != nil { 19 return nil 20 } 21 c.domainIndex = map[string]int64{} 22 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 23 domain := gandidomain.New(gc) 24 domains, err := domain.List() 25 if err != nil { 26 // fmt.Println(err) 27 return err 28 } 29 for _, d := range domains { 30 c.domainIndex[d.Fqdn] = d.Id 31 } 32 return nil 33 } 34 35 // fetchDomainInfo gets information about a domain. 36 func (c *GandiApi) fetchDomainInfo(fqdn string) (*gandidomain.DomainInfo, error) { 37 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 38 domain := gandidomain.New(gc) 39 return domain.Info(fqdn) 40 } 41 42 // getRecordsForDomain returns a list of records for a zone. 43 func (c *GandiApi) getZoneRecords(zoneid int64, origin string) ([]*models.RecordConfig, error) { 44 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 45 record := gandirecord.New(gc) 46 recs, err := record.List(zoneid, 0) 47 if err != nil { 48 return nil, err 49 } 50 rcs := make([]*models.RecordConfig, 0, len(recs)) 51 for _, r := range recs { 52 rcs = append(rcs, convert(r, origin)) 53 } 54 return rcs, nil 55 } 56 57 // listZones retrieves the list of zones. 58 func (c *GandiApi) listZones() ([]*gandizone.ZoneInfoBase, error) { 59 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 60 zone := gandizone.New(gc) 61 return zone.List() 62 } 63 64 // setZone assigns a particular zone to a domain. 65 func (c *GandiApi) setZones(domainname string, zone_id int64) (*gandidomain.DomainInfo, error) { 66 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 67 zone := gandizone.New(gc) 68 return zone.Set(domainname, zone_id) 69 } 70 71 // getZoneInfo gets ZoneInfo about a zone. 72 func (c *GandiApi) getZoneInfo(zoneid int64) (*gandizone.ZoneInfo, error) { 73 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 74 zone := gandizone.New(gc) 75 return zone.Info(zoneid) 76 } 77 78 // createZone creates an entirely new zone. 79 func (c *GandiApi) createZone(name string) (*gandizone.ZoneInfo, error) { 80 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 81 zone := gandizone.New(gc) 82 return zone.Create(name) 83 } 84 85 func (c *GandiApi) getEditableZone(domainname string, zoneinfo *gandizone.ZoneInfo) (int64, error) { 86 var zone_id int64 87 if zoneinfo.Domains < 2 { 88 // If there is only on{ domain linked to this zone, use it. 89 zone_id = zoneinfo.Id 90 fmt.Printf("Using zone id=%d named %#v\n", zone_id, zoneinfo.Name) 91 return zone_id, nil 92 } 93 94 // We can't use the zone_id given to us. Let's make/find a new one. 95 zones, err := c.listZones() 96 if err != nil { 97 return 0, err 98 } 99 zonename := fmt.Sprintf("%s dnscontrol", domainname) 100 for _, z := range zones { 101 if z.Name == zonename { 102 zone_id = z.Id 103 fmt.Printf("Recycling zone id=%d named %#v\n", zone_id, z.Name) 104 return zone_id, nil 105 } 106 } 107 zoneinfo, err = c.createZone(zonename) 108 if err != nil { 109 return 0, err 110 } 111 zone_id = zoneinfo.Id 112 fmt.Printf("Created zone id=%d named %#v\n", zone_id, zoneinfo.Name) 113 return zone_id, nil 114 } 115 116 // makeEditableZone 117 func (c *GandiApi) makeEditableZone(zone_id int64) (int64, error) { 118 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 119 version := gandiversion.New(gc) 120 return version.New(zone_id, 0) 121 } 122 123 // setZoneRecords 124 func (c *GandiApi) setZoneRecords(zone_id, version_id int64, records []gandirecord.RecordSet) ([]*gandirecord.RecordInfo, error) { 125 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 126 record := gandirecord.New(gc) 127 return record.SetRecords(zone_id, version_id, records) 128 } 129 130 // activateVersion 131 func (c *GandiApi) activateVersion(zone_id, version_id int64) (bool, error) { 132 gc := gandiclient.New(c.ApiKey, gandiclient.Production) 133 version := gandiversion.New(gc) 134 return version.Set(zone_id, version_id) 135 } 136 137 func (c *GandiApi) createGandiZone(domainname string, zoneID int64, records []gandirecord.RecordSet) error { 138 139 // Get the zone_id of the zone we'll be updating. 140 zoneinfo, err := c.getZoneInfo(zoneID) 141 if err != nil { 142 return err 143 } 144 //fmt.Println("ZONEINFO:", zoneinfo) 145 zoneID, err = c.getEditableZone(domainname, zoneinfo) 146 if err != nil { 147 return err 148 } 149 150 // Get the versionID of the zone we're updating. 151 versionID, err := c.makeEditableZone(zoneID) 152 if err != nil { 153 return err 154 } 155 156 // Update the new version. 157 _, err = c.setZoneRecords(zoneID, versionID, records) 158 if err != nil { 159 return err 160 } 161 162 // Activate zone version 163 _, err = c.activateVersion(zoneID, versionID) 164 if err != nil { 165 return err 166 } 167 _, err = c.setZones(domainname, zoneID) 168 if err != nil { 169 return err 170 } 171 172 return nil 173 } 174 175 // convert takes a DNS record from Gandi and returns our native RecordConfig format. 176 func convert(r *gandirecord.RecordInfo, origin string) *models.RecordConfig { 177 rc := &models.RecordConfig{ 178 NameFQDN: dnsutil.AddOrigin(r.Name, origin), 179 Name: r.Name, 180 Type: r.Type, 181 Original: r, 182 Target: r.Value, 183 TTL: uint32(r.Ttl), 184 } 185 switch r.Type { 186 case "A", "AAAA", "NS", "CNAME", "PTR", "TXT": 187 case "SRV": 188 var err error 189 rc.SrvPriority, rc.SrvWeight, rc.SrvPort, rc.Target, err = models.SplitCombinedSrvValue(r.Value) 190 if err != nil { 191 panic(fmt.Sprintf("gandi.convert bad srv value format: %#v (%s)", r.Value, err)) 192 } 193 // no-op 194 case "MX": 195 var err error 196 rc.MxPreference, rc.Target, err = models.SplitCombinedMxValue(r.Value) 197 if err != nil { 198 panic(fmt.Sprintf("gandi.convert bad mx value format: %#v", r.Value)) 199 } 200 default: 201 panic(fmt.Sprintf("gandi.convert unimplemented rtype %v", r.Type)) 202 // We panic so that we quickly find any switch statements 203 // that have not been updated for a new RR type. 204 } 205 return rc 206 }