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  }