diff --git a/build/generate/featureMatrix.go b/build/generate/featureMatrix.go new file mode 100644 index 000000000..d0e16b04f --- /dev/null +++ b/build/generate/featureMatrix.go @@ -0,0 +1,137 @@ +package main + +import ( + "bytes" + "html/template" + "io/ioutil" + "sort" + + "github.com/StackExchange/dnscontrol/providers" + _ "github.com/StackExchange/dnscontrol/providers/_all" +) + +func generateFeatureMatrix() error { + allNames := map[string]bool{} + for n := range providers.RegistrarTypes { + allNames[n] = true + } + for n := range providers.DNSProviderTypes { + allNames[n] = true + } + providerTypes := []string{} + for n := range allNames { + providerTypes = append(providerTypes, n) + } + sort.Strings(providerTypes) + matrix := &FeatureMatrix{ + Providers: map[string]FeatureMap{}, + Features: []FeatureDef{ + {"Official Support", "This means the provider is actively used at Stack Exchange, bugs are more likely to be fixed, and failing integration tests will block a release. See below for details"}, + {"Registrar", "The provider has registrar capabilities to set nameservers for zones"}, + {"DNS Provider", "Can manage and serve DNS zones"}, + {"ALIAS", "Provider supports some kind of ALIAS, ANAME or flattened CNAME record type"}, + {"SRV", "Driver has explicitly implemented SRV record management"}, + {"PTR", "Provider supports adding PTR records for reverse lookup zones"}, + {"CAA", "Provider can manage CAA records"}, + + {"dual host", "This provider is recommended for use in 'dual hosting' scenarios. Usually this means the provider allows full control over the apex NS records"}, + {"create-domains", "This means the provider can automatically create domains that do not currently exist on your account. The 'dnscontrol create-domains' command will initialize any missing domains"}, + {"no_purge", "indicates you can use NO_PURGE macro to prevent deleting records not managed by dnscontrol. A few providers that generate the entire zone from scratch have a problem implementing this."}, + }, + } + for _, p := range providerTypes { + if p == "NONE" { + continue + } + fm := FeatureMap{} + notes := providers.Notes[p] + if notes == nil { + notes = providers.DocumentationNotes{} + } + setCap := func(name string, cap providers.Capability) { + if notes[cap] != nil { + fm[name] = notes[cap] + return + } + fm.SetSimple(name, true, func() bool { return providers.ProviderHasCabability(p, cap) }) + } + setDoc := func(name string, cap providers.Capability) { + if notes[cap] != nil { + fm[name] = notes[cap] + } + } + setDoc("Official Support", providers.DocOfficiallySupported) + fm.SetSimple("Registrar", false, func() bool { return providers.RegistrarTypes[p] != nil }) + fm.SetSimple("DNS Provider", false, func() bool { return providers.DNSProviderTypes[p] != nil }) + setCap("ALIAS", providers.CanUseAlias) + setCap("SRV", providers.CanUseSRV) + setCap("PTR", providers.CanUsePTR) + setCap("CAA", providers.CanUseCAA) + setDoc("dual host", providers.DocDualHost) + setDoc("create-domains", providers.DocCreateDomains) + + // no purge is a freaky double negative + cap := providers.CantUseNOPURGE + if notes[cap] != nil { + fm["no_purge"] = notes[cap] + } else { + fm.SetSimple("no_purge", false, func() bool { return !providers.ProviderHasCabability(p, cap) }) + } + matrix.Providers[p] = fm + } + buf := &bytes.Buffer{} + err := tmpl.Execute(buf, matrix) + if err != nil { + return err + } + return ioutil.WriteFile("docs/_includes/matrix.html", buf.Bytes(), 0644) +} + +type FeatureDef struct { + Name, Desc string +} +type FeatureMap map[string]*providers.DocumentationNote + +func (fm FeatureMap) SetSimple(name string, unknownsAllowed bool, f func() bool) { + if f() { + fm[name] = &providers.DocumentationNote{HasFeature: true} + } else if !unknownsAllowed { + fm[name] = &providers.DocumentationNote{HasFeature: false} + } +} + +type FeatureMatrix struct { + Features []FeatureDef + Providers map[string]FeatureMap +} + +var tmpl = template.Must(template.New("").Funcs(template.FuncMap{ + "safe": func(s string) template.HTML { return template.HTML(s) }, +}).Parse(` + {% comment %} + Matrix generated by build/generate/featureMatrix.go. DO NOT HAND EDIT! +{% endcomment %}{{$providers := .Providers}} + + + + + {{range $key,$val := $providers}} + {{end -}} + + + + {{range .Features}}{{$name := .Name}} + + {{range $pname, $features := $providers}}{{$f := index $features $name}}{{if $f -}} + + {{- else}}{{end}} + {{end -}} + + {{end -}} + +
{{$key}}
{{$name}} + +
+`)) diff --git a/build/generate/generate.go b/build/generate/generate.go index 764e7d7c0..a97d24325 100644 --- a/build/generate/generate.go +++ b/build/generate/generate.go @@ -1,6 +1,8 @@ package main import ( + "log" + "github.com/mjibson/esc/embed" ) @@ -14,4 +16,8 @@ func main() { Files: []string{`pkg/js/helpers.js`}, } embed.Run(conf) + + if err := generateFeatureMatrix(); err != nil { + log.Fatal(err) + } } diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html new file mode 100644 index 000000000..243c39e21 --- /dev/null +++ b/docs/_includes/matrix.html @@ -0,0 +1,334 @@ + + {% comment %} + Matrix generated by build/generate/featureMatrix.go. DO NOT HAND EDIT! +{% endcomment %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ACTIVEDIRECTORY_PS
BIND
CLOUDFLAREAPI
DIGITALOCEAN
DNSIMPLE
GANDI
GCLOUD
NAMECHEAP
NAMEDOTCOM
NS1
ROUTE53
Official Support + + + + + + + + + + + + + + + + + + + + + +
Registrar + + + + + + + + + + + + + + + + + + + + + +
DNS Provider + + + + + + + + + + + + + + + + + + + + + +
ALIAS + + + + + +
SRV + + + + + + + + + + + + + + + +
PTR + + + + + + + + + + + + + +
CAA + + + + + + + +
dual host + + + + + + + + + + + + + + + +
create-domains + + + + + + + + + + + + + + + + + + + + + +
no_purge + + + + + + + + + + + + + + + + + + + + + +
diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index 988d6e616..370a6be2f 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -5,11 +5,11 @@ crossorigin="anonymous"> + - @@ -45,9 +45,6 @@ {{ content}} -{% comment %} Live reload script. Dev mode only {% endcomment %} {% unless site.github %} - -{% endunless %} {% comment %} This script makes the examples collapse appropriately {% endcomment %} diff --git a/docs/css/site.css b/docs/css/site.css index 2034864e5..609921331 100644 --- a/docs/css/site.css +++ b/docs/css/site.css @@ -12,4 +12,65 @@ body { padding-bottom: 50px; -} \ No newline at end of file +} + +.table-header-rotated { + border-collapse: collapse; + } + + .table-header-rotated td { + width: 30px; + } + + .table-header-rotated th { + padding: 5px 10px; + } + + .table-header-rotated td { + text-align: center; + padding: 10px 5px; + border: 1px solid #ccc; + } + + .table-header-rotated th.rotate { + height: 140px; + white-space: nowrap; + } + + .table-header-rotated th.rotate>div { + -webkit-transform: translate(25px, 51px) rotate(315deg); + -ms-transform: translate(25px, 51px) rotate(315deg); + transform: translate(25px, 51px) rotate(315deg); + width: 30px; + } + + .table-header-rotated th.rotate>div>span { + border-bottom: 1px solid #ccc; + padding: 5px 10px; + } + + .table-header-rotated th.row-header { + padding: 0 10px; + border-bottom: 1px solid #ccc; + } + .success { + background-color: #dff0d8; + } + .warning { + background-color: #faf2cc; + } + .danger { + background-color: #f2dede; + } + .info { + background-color: #d9edf7; + } + .fa { + font-size: 150%; + } + .has-tooltip:before{ + text-decoration: underline; + } + .dim { + color: #eeeeee + } \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 073df565f..bd7c3f921 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,3 +36,4 @@ Dnscontrol is a platform for seamlessly managing your dns configuration across a ### [Writing Providers]({{site.github.url}}/writing-providers) ### [Adding new DNS record types]({{site.github.url}}/adding-new-rtypes) + diff --git a/docs/provider-list.md b/docs/provider-list.md index 418a89855..2557d8dcd 100644 --- a/docs/provider-list.md +++ b/docs/provider-list.md @@ -16,17 +16,26 @@ layout: default {% endfor %} +

Provider Features

+ +

The table below shows various features supported, or not supported by DNSControl providers. + Underlined items have tooltips for more detailed explanation. This table is automatically generated + from metadata supplied by the provider when they register themselves inside dnscontrol. +

+

+ An empty space may indicate the feature is not supported by a provider, or it may simply mean + the feature has not been investigated and implemented yet. If a feature you need is missing from + a provider that supports it, we'd love your contribution to ensure it works correctly and add it to this matrix. +

+

If a feature is definitively not supported for whatever reason, we would also like a PR to clarify why it is not supported, and fill in this entire matrix.

+
+
+ +{% include matrix.html %} + + ### Providers with "official support" -The following providers have official support: - -* ACTIVEDIRECTORY_PS -* BIND -* CLOUDFLAREAPI -* GCLOUD -* NAMEDOTCOM -* ROUTE53 - Official support means: * New releases will block if any of these providers do not pass integration tests. @@ -50,10 +59,11 @@ provided to help community members support their code independently. Maintainers of contributed providers: -* digital ocean @Deraen -* dnsimple @aeden +* digital ocean @Deraen +* dnsimple @aeden * gandi @TomOnTime * namecheap @captncraig +* ns1 @captncraig * OVH @Oprax ### Requested providers @@ -62,7 +72,6 @@ We have received requests for the following providers. If you would like to cont code to support this provider, please re-open the issue. We'd be glad to help in any way. - + \ No newline at end of file diff --git a/main.go b/main.go index 99d0654db..a7bce1c38 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,7 @@ import ( _ "github.com/StackExchange/dnscontrol/providers/_all" ) -//go:generate go run build/generate/generate.go +//go:generate go run build/generate/generate.go build/generate/featureMatrix.go func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) diff --git a/providers/activedir/activedirProvider.go b/providers/activedir/activedirProvider.go index a6a1e7731..9bd031ecd 100644 --- a/providers/activedir/activedirProvider.go +++ b/providers/activedir/activedirProvider.go @@ -16,10 +16,20 @@ type adProvider struct { psLog string } +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Cannot("This driver does not manage NS records, so should not be used for dual-host scenarios"), + providers.DocCreateDomains: providers.Cannot("AD depends on the zone already existing on the dns server"), + providers.DocOfficiallySupported: providers.Can(), + providers.CanUseAlias: providers.Cannot(), + providers.CanUseSRV: providers.Cannot(), + providers.CanUsePTR: providers.Cannot(), + providers.CanUseCAA: providers.Cannot(), +} + // Register with the dnscontrol system. // This establishes the name (all caps), and the function to call to initialize it. func init() { - providers.RegisterDomainServiceProviderType("ACTIVEDIRECTORY_PS", newDNS) + providers.RegisterDomainServiceProviderType("ACTIVEDIRECTORY_PS", newDNS, docNotes) } func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go index 06d4a1d50..49027c534 100644 --- a/providers/bind/bindProvider.go +++ b/providers/bind/bindProvider.go @@ -30,6 +30,12 @@ import ( "github.com/StackExchange/dnscontrol/providers/diff" ) +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Can(), + providers.DocCreateDomains: providers.Can("Driver just maintains list of zone files. It should automatically add missing ones."), + providers.DocOfficiallySupported: providers.Can(), +} + func initBind(config map[string]string, providermeta json.RawMessage) (providers.DNSServiceProvider, error) { // config -- the key/values from creds.json // meta -- the json blob from NewReq('name', 'TYPE', meta) @@ -50,7 +56,8 @@ func initBind(config map[string]string, providermeta json.RawMessage) (providers } func init() { - providers.RegisterDomainServiceProviderType("BIND", initBind, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA, providers.CantUseNOPURGE) + providers.RegisterDomainServiceProviderType("BIND", initBind, providers.CanUsePTR, + providers.CanUseSRV, providers.CanUseCAA, providers.CantUseNOPURGE, docNotes) } type SoaInfo struct { diff --git a/providers/capabilities.go b/providers/capabilities.go new file mode 100644 index 000000000..5aaafa7b6 --- /dev/null +++ b/providers/capabilities.go @@ -0,0 +1,97 @@ +package providers + +import ( + "log" + "strings" +) + +//Capability is a bitmasked set of "features" that a provider supports. Only use constants from this package. +type Capability uint32 + +const ( + // CanUseAlias indicates the provider support ALIAS records (or flattened CNAMES). Up to the provider to translate them to the appropriate record type. + // If you add something to this list, you probably want to add it to pkg/normalize/validate.go checkProviderCapabilities() or somewhere near there. + CanUseAlias Capability = iota + // CanUsePTR indicates the provider can handle PTR records + CanUsePTR + // CanUseSRV indicates the provider can handle SRV records + CanUseSRV + // CanUseCAA indicates the provider can handle CAA records + CanUseCAA + // CantUseNOPURGE indicates NO_PURGE is broken for this provider. To make it + // work would require complex emulation of an incremental update mechanism, + // so it is easier to simply mark this feature as not working for this + // provider. + CantUseNOPURGE + + // DocOfficiallySupported means it is actively used and maintained by stack exchange + DocOfficiallySupported + // DocDualHost means provider allows full management of apex NS records, so we can safely dual-host with anothe provider + DocDualHost + // DocCreateDomains means provider can add domains with the `dnscontrol create-domains` command + DocCreateDomains +) + +var providerCapabilities = map[string]map[Capability]bool{} + +func ProviderHasCabability(pType string, cap Capability) bool { + if providerCapabilities[pType] == nil { + return false + } + return providerCapabilities[pType][cap] +} + +// DocumentationNote is a way for providers to give more detail about what features they support. +type DocumentationNote struct { + HasFeature bool + Comment string +} + +// DocumentationNotes is a full list of notes for a single provider +type DocumentationNotes map[Capability]*DocumentationNote + +// ProviderMetadata is a common interface for DocumentationNotes and Capability to be used interchangably +type ProviderMetadata interface{} + +// Notes is a collection of all documentation notes, keyed by provider type +var Notes = map[string]DocumentationNotes{} + +func unwrapProviderCapabilities(pName string, meta []ProviderMetadata) { + for _, pm := range meta { + switch x := pm.(type) { + case Capability: + if providerCapabilities[pName] == nil { + providerCapabilities[pName] = map[Capability]bool{} + } + providerCapabilities[pName][x] = true + case DocumentationNotes: + if Notes[pName] == nil { + Notes[pName] = DocumentationNotes{} + } + for k, v := range x { + Notes[pName][k] = v + } + default: + log.Fatalf("Unrecognized ProviderMetadata type: %T", pm) + } + + } +} + +// Can is a small helper for concisely creating Documentation Notes +// comments are variadic for easy ommission, so you generally should pass 0 or 1 only. +func Can(comments ...string) *DocumentationNote { + return &DocumentationNote{ + HasFeature: true, + Comment: strings.Join(comments, " "), + } +} + +// Cannot is a small helper for concisely creating Documentation Notes +// comments are variadic for easy ommission, so you generally should pass 0 or 1 only. +func Cannot(comments ...string) *DocumentationNote { + return &DocumentationNote{ + HasFeature: false, + Comment: strings.Join(comments, " "), + } +} diff --git a/providers/cloudflare/cloudflareProvider.go b/providers/cloudflare/cloudflareProvider.go index 6f23c6fac..ca9da707b 100644 --- a/providers/cloudflare/cloudflareProvider.go +++ b/providers/cloudflare/cloudflareProvider.go @@ -33,8 +33,15 @@ Domain level metadata available: - ip_conversions */ +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Cannot("Cloudflare will not work well in situations where it is not the only DNS server"), + providers.DocCreateDomains: providers.Can(), + providers.DocOfficiallySupported: providers.Can(), + providers.CanUseAlias: providers.Can("CF automatically flattens CNAME records into A records dynamically"), +} + func init() { - providers.RegisterDomainServiceProviderType("CLOUDFLAREAPI", newCloudflare, providers.CanUseSRV, providers.CanUseAlias) + providers.RegisterDomainServiceProviderType("CLOUDFLAREAPI", newCloudflare, providers.CanUseSRV, providers.CanUseAlias, docNotes) providers.RegisterCustomRecordType("CF_REDIRECT", "CLOUDFLAREAPI", "") providers.RegisterCustomRecordType("CF_TEMP_REDIRECT", "CLOUDFLAREAPI", "") } diff --git a/providers/digitalocean/digitaloceanProvider.go b/providers/digitalocean/digitaloceanProvider.go index ba9f67221..6737682bb 100644 --- a/providers/digitalocean/digitaloceanProvider.go +++ b/providers/digitalocean/digitaloceanProvider.go @@ -60,8 +60,13 @@ func newDo(m map[string]string, metadata json.RawMessage) (providers.DNSServiceP return api, nil } +var docNotes = providers.DocumentationNotes{ + providers.DocCreateDomains: providers.Can(), + providers.DocOfficiallySupported: providers.Cannot(), +} + func init() { - providers.RegisterDomainServiceProviderType("DIGITALOCEAN", newDo, providers.CanUseSRV) + providers.RegisterDomainServiceProviderType("DIGITALOCEAN", newDo, providers.CanUseSRV, docNotes) } func (api *DoApi) EnsureDomainExists(domain string) error { diff --git a/providers/dnsimple/dnsimpleProvider.go b/providers/dnsimple/dnsimpleProvider.go index b25c14f2c..9d302bd4a 100644 --- a/providers/dnsimple/dnsimpleProvider.go +++ b/providers/dnsimple/dnsimpleProvider.go @@ -15,6 +15,17 @@ import ( dnsimpleapi "github.com/dnsimple/dnsimple-go/dnsimple" ) +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Cannot("DNSimple does not allow sufficient control over the apex NS records"), + providers.DocCreateDomains: providers.Cannot(), + providers.DocOfficiallySupported: providers.Cannot(), +} + +func init() { + providers.RegisterRegistrarType("DNSIMPLE", newReg) + providers.RegisterDomainServiceProviderType("DNSIMPLE", newDsp, providers.CanUsePTR, docNotes) +} + const stateRegistered = "registered" var defaultNameServerNames = []string{ @@ -313,11 +324,6 @@ func newProvider(m map[string]string, metadata json.RawMessage) (*DnsimpleApi, e return api, nil } -func init() { - providers.RegisterRegistrarType("DNSIMPLE", newReg) - providers.RegisterDomainServiceProviderType("DNSIMPLE", newDsp, providers.CanUsePTR) -} - // remove all non-dnsimple NS records from our desired state. // if any are found, print a warning func removeOtherNS(dc *models.DomainConfig) { diff --git a/providers/gandi/gandiProvider.go b/providers/gandi/gandiProvider.go index 24a6106e8..5885d8b31 100644 --- a/providers/gandi/gandiProvider.go +++ b/providers/gandi/gandiProvider.go @@ -25,6 +25,16 @@ Info required in `creds.json`: */ +var docNotes = providers.DocumentationNotes{ + providers.DocCreateDomains: providers.Cannot("Can only manage domains registered through their service"), + providers.DocOfficiallySupported: providers.Cannot(), +} + +func init() { + providers.RegisterDomainServiceProviderType("GANDI", newGandi, providers.CanUsePTR, + providers.CanUseSRV, docNotes, providers.CantUseNOPURGE) +} + type GandiApi struct { ApiKey string domainIndex map[string]int64 // Map of domainname to index @@ -145,7 +155,3 @@ func newGandi(m map[string]string, metadata json.RawMessage) (providers.DNSServi return api, nil } - -func init() { - providers.RegisterDomainServiceProviderType("GANDI", newGandi, providers.CanUsePTR, providers.CanUseSRV) -} diff --git a/providers/google/google.go b/providers/google/google.go index 566105c72..9e455444c 100644 --- a/providers/google/google.go +++ b/providers/google/google.go @@ -14,8 +14,14 @@ import ( "github.com/StackExchange/dnscontrol/providers/diff" ) +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Can(), + providers.DocCreateDomains: providers.Can(), + providers.DocOfficiallySupported: providers.Can(), +} + func init() { - providers.RegisterDomainServiceProviderType("GCLOUD", New, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA) + providers.RegisterDomainServiceProviderType("GCLOUD", New, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA, docNotes) } type gcloud struct { diff --git a/providers/namecheap/namecheap.go b/providers/namecheap/namecheap.go index 863ec4075..0bd792781 100644 --- a/providers/namecheap/namecheap.go +++ b/providers/namecheap/namecheap.go @@ -16,8 +16,13 @@ type Namecheap struct { client *nc.Client } +var docNotes = providers.DocumentationNotes{ + providers.DocCreateDomains: providers.Cannot("Requires domain registered through their service"), + providers.DocOfficiallySupported: providers.Cannot(), +} + func init() { - providers.RegisterRegistrarType("NAMECHEAP", newReg) + providers.RegisterRegistrarType("NAMECHEAP", newReg, docNotes) // NOTE(tlim): If in the future the DNS Service Provider is implemented, // most likely it will require providers.CantUseNOPURGE. } diff --git a/providers/namedotcom/namedotcomProvider.go b/providers/namedotcom/namedotcomProvider.go index 054b85cac..7447b658c 100644 --- a/providers/namedotcom/namedotcomProvider.go +++ b/providers/namedotcom/namedotcomProvider.go @@ -19,6 +19,13 @@ type nameDotCom struct { APIKey string `json:"apikey"` } +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Cannot("Apex NS records not editable"), + providers.DocCreateDomains: providers.Cannot("New domains require registration"), + providers.DocOfficiallySupported: providers.Can(), + providers.CanUsePTR: providers.Cannot("PTR records are not supported https://www.name.com/support/articles/205188508-Reverse-DNS-records (2017-05-08)"), +} + func newReg(conf map[string]string) (providers.Registrar, error) { return newProvider(conf) } @@ -41,8 +48,7 @@ func newProvider(conf map[string]string) (*nameDotCom, error) { func init() { providers.RegisterRegistrarType("NAMEDOTCOM", newReg) - providers.RegisterDomainServiceProviderType("NAMEDOTCOM", newDsp, providers.CanUseAlias, providers.CanUseSRV) - // PTR records are not supported https://www.name.com/support/articles/205188508-Reverse-DNS-records (2017-05-08) + providers.RegisterDomainServiceProviderType("NAMEDOTCOM", newDsp, providers.CanUseAlias, providers.CanUseSRV, docNotes) } /// diff --git a/providers/ns1/ns1provider.go b/providers/ns1/ns1provider.go index b0eca66ba..abbb5994c 100644 --- a/providers/ns1/ns1provider.go +++ b/providers/ns1/ns1provider.go @@ -18,8 +18,14 @@ import ( "gopkg.in/ns1/ns1-go.v2/rest/model/dns" ) +var docNotes = providers.DocumentationNotes{ + providers.DocCreateDomains: providers.Cannot(), + providers.DocOfficiallySupported: providers.Cannot(), + providers.DocDualHost: providers.Can(), +} + func init() { - providers.RegisterDomainServiceProviderType("NS1", newProvider) + providers.RegisterDomainServiceProviderType("NS1", newProvider, docNotes) } type nsone struct { diff --git a/providers/providers.go b/providers/providers.go index d4b9b91c7..4ebfda3a3 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -28,61 +28,33 @@ type DomainCreator interface { //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) -var registrarTypes = map[string]RegistrarInitializer{} +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) -var dspTypes = map[string]DspInitializer{} -var dspCapabilities = map[string]Capability{} - -//Capability is a bitmasked set of "features" that a provider supports. Only use constants from this package. -type Capability uint32 - -const ( - // CanUseAlias indicates the provider support ALIAS records (or flattened CNAMES). Up to the provider to translate them to the appropriate record type. - // If you add something to this list, you probably want to add it to pkg/normalize/validate.go checkProviderCapabilities() or somewhere near there. - CanUseAlias Capability = 1 << iota - // CanUsePTR indicates the provider can handle PTR records - CanUsePTR - // CanUseSRV indicates the provider can handle SRV records - CanUseSRV - // CanUseCAA indicates the provider can handle CAA records - CanUseCAA - // CantUseNOPURGE indicates NO_PURGE is broken for this provider. To make it - // work would require complex emulation of an incremental update mechanism, - // so it is easier to simply mark this feature as not working for this - // provider. - CantUseNOPURGE -) - -func ProviderHasCabability(pType string, cap Capability) bool { - return dspCapabilities[pType]&cap != 0 -} +var DNSProviderTypes = map[string]DspInitializer{} //RegisterRegistrarType adds a registrar type to the registry by providing a suitable initialization function. -func RegisterRegistrarType(name string, init RegistrarInitializer) { - if _, ok := registrarTypes[name]; ok { +func RegisterRegistrarType(name string, init RegistrarInitializer, pm ...ProviderMetadata) { + if _, ok := RegistrarTypes[name]; ok { log.Fatalf("Cannot register registrar type %s multiple times", name) } - registrarTypes[name] = init + RegistrarTypes[name] = init + unwrapProviderCapabilities(name, pm) } //RegisterDomainServiceProviderType adds a dsp to the registry with the given initialization function. -func RegisterDomainServiceProviderType(name string, init DspInitializer, caps ...Capability) { - if _, ok := dspTypes[name]; ok { +func RegisterDomainServiceProviderType(name string, init DspInitializer, pm ...ProviderMetadata) { + if _, ok := DNSProviderTypes[name]; ok { log.Fatalf("Cannot register registrar type %s multiple times", name) } - var abilities Capability - for _, c := range caps { - abilities |= c - } - dspTypes[name] = init - dspCapabilities[name] = abilities + DNSProviderTypes[name] = init + unwrapProviderCapabilities(name, pm) } func createRegistrar(rType string, config map[string]string) (Registrar, error) { - initer, ok := registrarTypes[rType] + initer, ok := RegistrarTypes[rType] if !ok { return nil, fmt.Errorf("Registrar type %s not declared.", rType) } @@ -90,7 +62,7 @@ func createRegistrar(rType string, config map[string]string) (Registrar, error) } func CreateDNSProvider(dType string, config map[string]string, meta json.RawMessage) (DNSServiceProvider, error) { - initer, ok := dspTypes[dType] + initer, ok := DNSProviderTypes[dType] if !ok { return nil, fmt.Errorf("DSP type %s not declared", dType) } diff --git a/providers/route53/route53Provider.go b/providers/route53/route53Provider.go index 1016325d2..f669d58f8 100644 --- a/providers/route53/route53Provider.go +++ b/providers/route53/route53Provider.go @@ -56,8 +56,14 @@ func newRoute53(m map[string]string, metadata json.RawMessage) (*route53Provider return api, nil } +var docNotes = providers.DocumentationNotes{ + providers.DocDualHost: providers.Can(), + providers.DocCreateDomains: providers.Can(), + providers.DocOfficiallySupported: providers.Can(), +} + func init() { - providers.RegisterDomainServiceProviderType("ROUTE53", newRoute53Dsp, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA) + providers.RegisterDomainServiceProviderType("ROUTE53", newRoute53Dsp, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA, docNotes) providers.RegisterRegistrarType("ROUTE53", newRoute53Reg) }