1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00
Files
stackexchange-dnscontrol/providers/ovh/ovhProvider.go
Brice Figureau e44dde52e2 New Provider: OVH DNS Provider (#143) (#175)
* OVH DNS Provider (#143)

This adds the OVH Provider along with its documentation.

Unfortunately we can't set this DNS provider to support `CanUsePTR`,
because OVH only supports setting PTR target on the Arpa zone.

* OVH Registrar provider (#143)

This implements OVH as a registrar provider.
Note that NS modifications are done in a "best effort" mode, as the
provider doesn't wait for the modifications to be fully applied
(the operation that can take a long time).

* Allow support for dual providers scenarios

Since OVH released their APIv6, it is now possible to update
zone apex NS records, opening the door to complete dual providers
scenarii.

This change implements apex NS management in an OVH zone.
2017-11-10 11:02:34 -08:00

203 lines
4.7 KiB
Go

package ovh
import (
"encoding/json"
"fmt"
"github.com/StackExchange/dnscontrol/models"
"github.com/StackExchange/dnscontrol/providers"
"github.com/StackExchange/dnscontrol/providers/diff"
"github.com/miekg/dns/dnsutil"
"github.com/xlucas/go-ovh/ovh"
"sort"
"strings"
)
type ovhProvider struct {
client *ovh.Client
zones map[string]bool
}
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Can(),
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
providers.DocOfficiallySupported: providers.Cannot(),
providers.CanUseAlias: providers.Cannot(),
providers.CanUseTLSA: providers.Can(),
providers.CanUseCAA: providers.Cannot(),
providers.CanUsePTR: providers.Cannot(),
}
func newOVH(m map[string]string, metadata json.RawMessage) (*ovhProvider, error) {
appKey, appSecretKey, consumerKey := m["app-key"], m["app-secret-key"], m["consumer-key"]
c := ovh.NewClient(ovh.ENDPOINT_EU_OVHCOM, appKey, appSecretKey, consumerKey, false)
// Check for time lag
if err := c.PollTimeshift(); err != nil {
return nil, err
}
return &ovhProvider{client: c}, nil
}
func newDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
return newOVH(conf, metadata)
}
func newReg(conf map[string]string) (providers.Registrar, error) {
return newOVH(conf, nil)
}
func init() {
providers.RegisterRegistrarType("OVH", newReg)
providers.RegisterDomainServiceProviderType("OVH", newDsp, providers.CanUseSRV, providers.CanUseTLSA, docNotes)
}
func (c *ovhProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
if err := c.fetchZones(); err != nil {
return nil, err
}
_, ok := c.zones[domain]
if !ok {
return nil, fmt.Errorf("%s not listed in zones for ovh account", domain)
}
ns, err := c.fetchNS(domain)
if err != nil {
return nil, err
}
return models.StringsToNameservers(ns), nil
}
type errNoExist struct {
domain string
}
func (e errNoExist) Error() string {
return fmt.Sprintf("Domain %s not found in your ovh account", e.domain)
}
func (c *ovhProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
dc.CombineMXs()
if !c.zones[dc.Name] {
return nil, errNoExist{dc.Name}
}
records, err := c.fetchRecords(dc.Name)
if err != nil {
return nil, err
}
var actual []*models.RecordConfig
for _, r := range records {
if r.FieldType == "SOA" {
continue
}
if r.SubDomain == "" {
r.SubDomain = "@"
}
// ovh uses a custom type for SPF and DKIM
if r.FieldType == "SPF" || r.FieldType == "DKIM" {
r.FieldType = "TXT"
}
// ovh default is 3600
if r.TTL == 0 {
r.TTL = 3600
}
rec := &models.RecordConfig{
NameFQDN: dnsutil.AddOrigin(r.SubDomain, dc.Name),
Name: r.SubDomain,
Type: r.FieldType,
Target: r.Target,
TTL: uint32(r.TTL),
CombinedTarget: true,
Original: r,
}
actual = append(actual, rec)
}
// Normalize
models.Downcase(actual)
differ := diff.New(dc)
_, create, delete, modify := differ.IncrementalDiff(actual)
corrections := []*models.Correction{}
for _, del := range delete {
rec := del.Existing.Original.(*Record)
corrections = append(corrections, &models.Correction{
Msg: del.String(),
F: c.deleteRecordFunc(rec.Id, dc.Name),
})
}
for _, cre := range create {
rec := cre.Desired
corrections = append(corrections, &models.Correction{
Msg: cre.String(),
F: c.createRecordFunc(rec, dc.Name),
})
}
for _, mod := range modify {
oldR := mod.Existing.Original.(*Record)
newR := mod.Desired
corrections = append(corrections, &models.Correction{
Msg: mod.String(),
F: c.updateRecordFunc(oldR, newR, dc.Name),
})
}
if len(corrections) > 0 {
corrections = append(corrections, &models.Correction{
Msg: "REFRESH zone " + dc.Name,
F: func() error {
return c.refreshZone(dc.Name)
},
})
}
return corrections, nil
}
func (c *ovhProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
ns, err := c.fetchRegistrarNS(dc.Name)
if err != nil {
return nil, err
}
sort.Strings(ns)
found := strings.Join(ns, ",")
desiredNs := []string{}
for _, d := range dc.Nameservers {
desiredNs = append(desiredNs, d.Name)
}
sort.Strings(desiredNs)
desired := strings.Join(desiredNs, ",")
if found != desired {
return []*models.Correction{
{
Msg: fmt.Sprintf("Change Nameservers from '%s' to '%s'", found, desired),
F: func() error {
err := c.updateNS(dc.Name, desiredNs)
if err != nil {
return err
}
return nil
}},
}, nil
}
return nil, nil
}