/
providers.go
206 lines (171 loc) · 7.32 KB
/
providers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package providers
import (
"encoding/json"
"fmt"
"log"
"github.com/StackExchange/dnscontrol/v4/models"
)
// Registrar is an interface for a domain registrar. It can return a list of needed corrections to be applied in the future. Implement this only if the provider is a "registrar" (i.e. can update the NS records of the parent to a domain).
type Registrar interface {
models.Registrar
}
// DNSServiceProvider is able to generate a set of corrections that need to be made to correct records for a domain. Implement this only if the provider is a DNS Service Provider (can update records in a DNS zone).
type DNSServiceProvider interface {
models.DNSProvider
}
// ZoneCreator should be implemented by providers that have the ability to create zones
// (used for automatically creating zones if they don't exist)
type ZoneCreator interface {
EnsureZoneExists(domain string) error
}
// ZoneLister should be implemented by providers that have the
// ability to list the zones they manage. This facilitates using the
// "get-zones" command for "all" zones.
type ZoneLister interface {
ListZones() ([]string, error)
}
// RegistrarInitializer is a function to create a registrar. Function will be passed the unprocessed json payload from the configuration file for the given provider.
type RegistrarInitializer func(map[string]string) (Registrar, error)
// RegistrarTypes stores initializer for each registrar.
var RegistrarTypes = map[string]RegistrarInitializer{}
// DspInitializer is a function to create a DNS service provider. Function will be passed the unprocessed json payload from the configuration file for the given provider.
type DspInitializer func(map[string]string, json.RawMessage) (DNSServiceProvider, error)
// RecordAuditor is a function that verifies that all the records
// are supportable by this provider. It returns a list of errors
// detailing records that this provider can not support.
type RecordAuditor func([]*models.RecordConfig) []error
// DspFuncs lists functions registered with a provider.
type DspFuncs struct {
Initializer DspInitializer
RecordAuditor RecordAuditor
}
// DNSProviderTypes stores initializer for each DSP.
var DNSProviderTypes = map[string]DspFuncs{}
// RegisterRegistrarType adds a registrar type to the registry by providing a suitable initialization function.
func RegisterRegistrarType(name string, init RegistrarInitializer, pm ...ProviderMetadata) {
if _, ok := RegistrarTypes[name]; ok {
log.Fatalf("Cannot register registrar type %q multiple times", name)
}
RegistrarTypes[name] = init
unwrapProviderCapabilities(name, pm)
}
// RegisterDomainServiceProviderType adds a dsp to the registry with the given initialization function.
func RegisterDomainServiceProviderType(name string, fns DspFuncs, pm ...ProviderMetadata) {
if _, ok := DNSProviderTypes[name]; ok {
log.Fatalf("Cannot register registrar type %q multiple times", name)
}
DNSProviderTypes[name] = fns
unwrapProviderCapabilities(name, pm)
}
// CreateRegistrar initializes a registrar instance from given credentials.
func CreateRegistrar(rType string, config map[string]string) (Registrar, error) {
var err error
rType, err = beCompatible(rType, config)
if err != nil {
return nil, err
}
initer, ok := RegistrarTypes[rType]
if !ok {
return nil, fmt.Errorf("no such registrar type: %q", rType)
}
return initer(config)
}
// CreateDNSProvider initializes a dns provider instance from given credentials.
func CreateDNSProvider(providerTypeName string, config map[string]string, meta json.RawMessage) (DNSServiceProvider, error) {
var err error
providerTypeName, err = beCompatible(providerTypeName, config)
if err != nil {
return nil, err
}
p, ok := DNSProviderTypes[providerTypeName]
if !ok {
return nil, fmt.Errorf("no such DNS service provider: %q", providerTypeName)
}
return p.Initializer(config, meta)
}
// beCompatible looks up
func beCompatible(n string, config map[string]string) (string, error) {
// Pre 4.0: If n is a placeholder, substitute the TYPE from creds.json.
// 4.0: Require TYPE from creds.json.
ct := config["TYPE"]
// If a placeholder value was specified...
if n == "" || n == "-" {
// But no TYPE exists in creds.json...
if ct == "" {
return "-", fmt.Errorf("creds.json entry missing TYPE field")
}
// Otherwise, use the value from creds.json.
return ct, nil
}
// Pre 4.0: The user specified the name manually.
// Cross check to detect user-error.
if ct != "" && n != ct {
return "", fmt.Errorf("creds.json entry mismatch: specified=%q TYPE=%q", n, ct)
}
// Seems like the user did it the right way. Return the original value.
return n, nil
// NB(tlim): My hope is that in 4.0 this entire function will simply be the
// following, but I may be wrong:
//return config["TYPE"], nil
}
// AuditRecords calls the RecordAudit function for a provider.
func AuditRecords(dType string, rcs models.Records) []error {
p, ok := DNSProviderTypes[dType]
if !ok {
return []error{fmt.Errorf("unknown DNS service provider type: %q", dType)}
}
if p.RecordAuditor == nil {
return []error{fmt.Errorf("DNS service provider type %q has no RecordAuditor", dType)}
}
return p.RecordAuditor(rcs)
}
// None is a basic provider type that does absolutely nothing. Can be useful as a placeholder for third parties or unimplemented providers.
type None struct{}
// GetRegistrarCorrections returns corrections to update registrars.
func (n None) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
return nil, nil
}
// GetNameservers returns the current nameservers for a domain.
func (n None) GetNameservers(string) ([]*models.Nameserver, error) {
return nil, nil
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (n None) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
return nil, nil
}
// GetZoneRecordsCorrections gets the records of a zone and returns them in RecordConfig format.
func (n None) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
return nil, nil
}
// GetDomainCorrections returns corrections to update a domain.
func (n None) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
return nil, nil
}
var featuresNone = DocumentationNotes{
// The default for unlisted capabilities is 'Cannot'.
// See providers/capabilities.go for the entire list of capabilities.
CanConcur: Can(),
}
func init() {
RegisterRegistrarType("NONE", func(map[string]string) (Registrar, error) {
return None{}, nil
}, featuresNone)
}
// CustomRType stores an rtype that is only valid for this DSP.
type CustomRType struct {
Name string
Provider string
RealType string
}
// RegisterCustomRecordType registers a record type that is only valid for one provider.
// provider is the registered type of provider this is valid with
// name is the record type as it will appear in the js. (should be something like $PROVIDER_FOO)
// realType is the record type it will be replaced with after validation
func RegisterCustomRecordType(name, provider, realType string) {
customRecordTypes[name] = &CustomRType{Name: name, Provider: provider, RealType: realType}
}
// GetCustomRecordType returns a registered custom record type, or nil if none
func GetCustomRecordType(rType string) *CustomRType {
return customRecordTypes[rType]
}
var customRecordTypes = map[string]*CustomRType{}