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  }