github.com/philhug/dnscontrol@v0.2.4-0.20180625181521-921fa9849001/providers/ovh/protocol.go (about)

     1  package ovh
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/StackExchange/dnscontrol/models"
     7  	"github.com/miekg/dns/dnsutil"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  // Void an empty structure.
    12  type Void struct {
    13  }
    14  
    15  // fetchDomainList gets list of zones for account
    16  func (c *ovhProvider) fetchZones() error {
    17  	if c.zones != nil {
    18  		return nil
    19  	}
    20  	c.zones = map[string]bool{}
    21  
    22  	var response []string
    23  
    24  	err := c.client.Call("GET", "/domain/zone", nil, &response)
    25  
    26  	if err != nil {
    27  		return err
    28  	}
    29  
    30  	for _, d := range response {
    31  		c.zones[d] = true
    32  	}
    33  	return nil
    34  }
    35  
    36  // Zone describes the attributes of a DNS zone.
    37  type Zone struct {
    38  	LastUpdate      string   `json:"lastUpdate,omitempty"`
    39  	HasDNSAnycast   bool     `json:"hasDNSAnycast,omitempty"`
    40  	NameServers     []string `json:"nameServers"`
    41  	DNSSecSupported bool     `json:"dnssecSupported"`
    42  }
    43  
    44  // get info about a zone.
    45  func (c *ovhProvider) fetchZone(fqdn string) (*Zone, error) {
    46  	var response Zone
    47  
    48  	err := c.client.Call("GET", "/domain/zone/"+fqdn, nil, &response)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return &response, nil
    54  }
    55  
    56  // Record describes a DNS record.
    57  type Record struct {
    58  	Target    string `json:"target,omitempty"`
    59  	Zone      string `json:"zone,omitempty"`
    60  	TTL       uint32 `json:"ttl,omitempty"`
    61  	FieldType string `json:"fieldType,omitempty"`
    62  	ID        int64  `json:"id,omitempty"`
    63  	SubDomain string `json:"subDomain,omitempty"`
    64  }
    65  
    66  type records struct {
    67  	recordsID []int
    68  }
    69  
    70  func (c *ovhProvider) fetchRecords(fqdn string) ([]*Record, error) {
    71  	var recordIds []int
    72  
    73  	err := c.client.Call("GET", "/domain/zone/"+fqdn+"/record", nil, &recordIds)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	records := make([]*Record, len(recordIds))
    79  	for i, id := range recordIds {
    80  		r, err := c.fecthRecord(fqdn, id)
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  		records[i] = r
    85  	}
    86  
    87  	return records, nil
    88  }
    89  
    90  func (c *ovhProvider) fecthRecord(fqdn string, id int) (*Record, error) {
    91  	var response Record
    92  
    93  	err := c.client.Call("GET", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, id), nil, &response)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return &response, nil
    98  }
    99  
   100  // Returns a function that can be invoked to delete a record in a zone.
   101  func (c *ovhProvider) deleteRecordFunc(id int64, fqdn string) func() error {
   102  	return func() error {
   103  		err := c.client.Call("DELETE", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, id), nil, nil)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		return nil
   108  	}
   109  }
   110  
   111  // Returns a function that can be invoked to create a record in a zone.
   112  func (c *ovhProvider) createRecordFunc(rc *models.RecordConfig, fqdn string) func() error {
   113  	return func() error {
   114  		record := Record{
   115  			SubDomain: dnsutil.TrimDomainName(rc.GetLabelFQDN(), fqdn),
   116  			FieldType: rc.Type,
   117  			Target:    rc.GetTargetCombined(),
   118  			TTL:       rc.TTL,
   119  		}
   120  		if record.SubDomain == "@" {
   121  			record.SubDomain = ""
   122  		}
   123  		var response Record
   124  		err := c.client.Call("POST", fmt.Sprintf("/domain/zone/%s/record", fqdn), &record, &response)
   125  		return err
   126  	}
   127  }
   128  
   129  // Returns a function that can be invoked to update a record in a zone.
   130  func (c *ovhProvider) updateRecordFunc(old *Record, rc *models.RecordConfig, fqdn string) func() error {
   131  	return func() error {
   132  		record := Record{
   133  			SubDomain: rc.GetLabel(),
   134  			FieldType: rc.Type,
   135  			Target:    rc.GetTargetCombined(),
   136  			TTL:       rc.TTL,
   137  			Zone:      fqdn,
   138  			ID:        old.ID,
   139  		}
   140  		if record.SubDomain == "@" {
   141  			record.SubDomain = ""
   142  		}
   143  
   144  		return c.client.Call("PUT", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, old.ID), &record, &Void{})
   145  	}
   146  }
   147  
   148  func (c *ovhProvider) refreshZone(fqdn string) error {
   149  	return c.client.Call("POST", fmt.Sprintf("/domain/zone/%s/refresh", fqdn), nil, &Void{})
   150  }
   151  
   152  // fetch the NS OVH attributed to this zone (which is distinct from fetchRealNS which
   153  // get the exact NS stored at the registrar
   154  func (c *ovhProvider) fetchNS(fqdn string) ([]string, error) {
   155  	zone, err := c.fetchZone(fqdn)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	return zone.NameServers, nil
   161  }
   162  
   163  // CurrentNameServer stores information about nameservers.
   164  type CurrentNameServer struct {
   165  	ToDelete bool   `json:"toDelete,omitempty"`
   166  	IP       string `json:"ip,omitempty"`
   167  	IsUsed   bool   `json:"isUsed,omitempty"`
   168  	ID       int    `json:"id,omitempty"`
   169  	Host     string `json:"host,omitempty"`
   170  }
   171  
   172  // Retrieve the NS currently being deployed to the registrar
   173  func (c *ovhProvider) fetchRegistrarNS(fqdn string) ([]string, error) {
   174  	var nameServersID []int
   175  	err := c.client.Call("GET", "/domain/"+fqdn+"/nameServer", nil, &nameServersID)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	var nameServers []string
   181  	for _, id := range nameServersID {
   182  		var ns CurrentNameServer
   183  		err = c.client.Call("GET", fmt.Sprintf("/domain/%s/nameServer/%d", fqdn, id), nil, &ns)
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  
   188  		// skip NS that we asked for deletion
   189  		if ns.ToDelete {
   190  			continue
   191  		}
   192  		nameServers = append(nameServers, ns.Host)
   193  	}
   194  
   195  	return nameServers, nil
   196  }
   197  
   198  // DomainNS describes a domain's NS in ovh's protocol.
   199  type DomainNS struct {
   200  	Host string `json:"host,omitempty"`
   201  	IP   string `json:"ip,omitempty"`
   202  }
   203  
   204  // UpdateNS describes a list of nameservers in ovh's protocol.
   205  type UpdateNS struct {
   206  	NameServers []DomainNS `json:"nameServers"`
   207  }
   208  
   209  // Task describes a task in ovh's protocol.
   210  type Task struct {
   211  	Function      string `json:"function,omitempty"`
   212  	Status        string `json:"status,omitempty"`
   213  	CanAccelerate bool   `json:"canAccelerate,omitempty"`
   214  	LastUpdate    string `json:"lastUpdate,omitempty"`
   215  	CreationDate  string `json:"creationDate,omitempty"`
   216  	Comment       string `json:"comment,omitempty"`
   217  	TodoDate      string `json:"todoDate,omitempty"`
   218  	ID            int64  `json:"id,omitempty"`
   219  	CanCancel     bool   `json:"canCancel,omitempty"`
   220  	DoneDate      string `json:"doneDate,omitempty"`
   221  	CanRelaunch   bool   `json:"canRelaunch,omitempty"`
   222  }
   223  
   224  // Domain describes a domain in ovh's protocol.
   225  type Domain struct {
   226  	NameServerType     string `json:"nameServerType,omitempty"`
   227  	TransferLockStatus string `json:"transferLockStatus,omitempty"`
   228  }
   229  
   230  func (c *ovhProvider) updateNS(fqdn string, ns []string) error {
   231  	// we first need to make sure we can edit the NS
   232  	// by default zones are in "hosted" mode meaning they default
   233  	// to OVH default NS. In this mode, the NS can't be updated.
   234  	domain := Domain{NameServerType: "external"}
   235  	err := c.client.Call("PUT", fmt.Sprintf("/domain/%s", fqdn), &domain, &Void{})
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	var newNs []DomainNS
   241  	for _, n := range ns {
   242  		newNs = append(newNs, DomainNS{
   243  			Host: n,
   244  		})
   245  	}
   246  
   247  	update := UpdateNS{
   248  		NameServers: newNs,
   249  	}
   250  	var task Task
   251  	err = c.client.Call("POST", fmt.Sprintf("/domain/%s/nameServers/update", fqdn), &update, &task)
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	if task.Status == "error" {
   257  		return errors.Errorf("API error while updating ns for %s: %s", fqdn, task.Comment)
   258  	}
   259  
   260  	// we don't wait for the task execution. One of the reason is that
   261  	// NS modification can take time in the registrar, the other is that every task
   262  	// in OVH is usually executed a few minutes after they have been registered.
   263  	// We count on the fact that `GetNameservers` uses the registrar API to get
   264  	// a coherent view (including pending modifications) of the registered NS.
   265  
   266  	return nil
   267  }