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