mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
* Maint: run generate for missing documentation Apparently current master is missing some generated documentation. * Populate ovh zones cache as early as possible (#412) We are caching the OVH zones in GetNameservers. It turns out it isn’t a good idea, because GetNameServers will not be called if the user selects no name servers for a given domain by using for example: ``` D(‘my domain’, DnsProvider(ovh, 0)) { } ``` The subsequent GetDomainCorrections would automatically fail with an unknown domain error, because the zones cache hasn’t been filled in. To solve the issue, the ovh provider now populates the zones cache during initialisation.
211 lines
4.8 KiB
Go
211 lines
4.8 KiB
Go
package ovh
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/StackExchange/dnscontrol/models"
|
|
"github.com/StackExchange/dnscontrol/providers"
|
|
"github.com/StackExchange/dnscontrol/providers/diff"
|
|
"github.com/pkg/errors"
|
|
"github.com/xlucas/go-ovh/ovh"
|
|
)
|
|
|
|
type ovhProvider struct {
|
|
client *ovh.Client
|
|
zones map[string]bool
|
|
}
|
|
|
|
var features = providers.DocumentationNotes{
|
|
providers.CanUseAlias: providers.Cannot(),
|
|
providers.CanUseCAA: providers.Cannot(),
|
|
providers.CanUsePTR: providers.Cannot(),
|
|
providers.CanUseSRV: providers.Can(),
|
|
providers.CanUseTLSA: providers.Can(),
|
|
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
|
|
providers.DocDualHost: providers.Can(),
|
|
providers.DocOfficiallySupported: 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
|
|
}
|
|
|
|
ovh := &ovhProvider{client: c}
|
|
if err := ovh.fetchZones(); err != nil {
|
|
return nil, err
|
|
}
|
|
return ovh, 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, features)
|
|
}
|
|
|
|
func (c *ovhProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
|
_, ok := c.zones[domain]
|
|
if !ok {
|
|
return nil, errors.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 {
|
|
rec := nativeToRecord(r, dc.Name)
|
|
if rec != nil {
|
|
actual = append(actual, rec)
|
|
}
|
|
}
|
|
|
|
// Normalize
|
|
models.PostProcessRecords(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 nativeToRecord(r *Record, origin string) *models.RecordConfig {
|
|
if r.FieldType == "SOA" {
|
|
return nil
|
|
}
|
|
rec := &models.RecordConfig{
|
|
TTL: uint32(r.TTL),
|
|
Original: r,
|
|
}
|
|
|
|
rtype := r.FieldType
|
|
|
|
// ovh uses a custom type for SPF and DKIM
|
|
if rtype == "SPF" || rtype == "DKIM" {
|
|
rtype = "TXT"
|
|
}
|
|
|
|
rec.SetLabel(r.SubDomain, origin)
|
|
if err := rec.PopulateFromString(rtype, r.Target, origin); err != nil {
|
|
panic(errors.Wrap(err, "unparsable record received from ovh"))
|
|
}
|
|
|
|
// ovh default is 3600
|
|
if rec.TTL == 0 {
|
|
rec.TTL = 3600
|
|
}
|
|
|
|
return rec
|
|
}
|
|
|
|
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
|
|
}
|