Create a dynamic provider features matrix (#201)

* adding simple provider feature matrix generator

* filling out matrix

* clean output

* dead code

* explanatory text

* explanatory text

* typo

* move stuff around

* clean

* editing
This commit is contained in:
Craig Peterson 2017-09-14 16:13:17 -04:00 committed by GitHub
parent 6ce221df49
commit 3a90435357
21 changed files with 765 additions and 79 deletions

View file

@ -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}}
<table class="table-header-rotated">
<thead>
<tr>
<th></th>
{{range $key,$val := $providers}}<th class="rotate"><div><span>{{$key}}</span></div></th>
{{end -}}
</tr>
</thead>
<tbody>
{{range .Features}}{{$name := .Name}}<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="{{.Desc}}">{{$name}}</th>
{{range $pname, $features := $providers}}{{$f := index $features $name}}{{if $f -}}
<td class="{{if $f.HasFeature}}success{{else}}danger{{end}}"
{{- if $f.Comment}} data-toggle="tooltip" data-container="body" data-placement="top" title="{{$f.Comment}}"{{end}}>
<i class="fa {{if $f.Comment}}has-tooltip {{end}}
{{- if $f.HasFeature}}fa-check text-success{{else}}fa-times text-danger{{end}}" aria-hidden="true"></i>
</td>
{{- else}}<td><i class="fa fa-minus dim"></i></td>{{end}}
{{end -}}
</tr>
{{end -}}
</tbody>
</table>
`))

View file

@ -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)
}
}

334
docs/_includes/matrix.html Normal file
View file

@ -0,0 +1,334 @@
{% comment %}
Matrix generated by build/generate/featureMatrix.go. DO NOT HAND EDIT!
{% endcomment %}
<table class="table-header-rotated">
<thead>
<tr>
<th></th>
<th class="rotate"><div><span>ACTIVEDIRECTORY_PS</span></div></th>
<th class="rotate"><div><span>BIND</span></div></th>
<th class="rotate"><div><span>CLOUDFLAREAPI</span></div></th>
<th class="rotate"><div><span>DIGITALOCEAN</span></div></th>
<th class="rotate"><div><span>DNSIMPLE</span></div></th>
<th class="rotate"><div><span>GANDI</span></div></th>
<th class="rotate"><div><span>GCLOUD</span></div></th>
<th class="rotate"><div><span>NAMECHEAP</span></div></th>
<th class="rotate"><div><span>NAMEDOTCOM</span></div></th>
<th class="rotate"><div><span>NS1</span></div></th>
<th class="rotate"><div><span>ROUTE53</span></div></th>
</tr>
</thead>
<tbody>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This means the provider is actively used at stack exchange, and we offer a stronger guarantee it will work">Official Support</th>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="The provider has registrar capabilities to set nameservers for zones">Registrar</th>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Can manage and serve DNS zones">DNS Provider</th>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports some kind of ALIAS,ANAME or flattened CNAME record type">ALIAS</th>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success" data-toggle="tooltip" data-container="body" data-placement="top" title="CF automatically flattens CNAME records into A records dynamically">
<i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Driver has explicitly implemented SRV record management">SRV</th>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding PTR records for reverse lookup zones">PTR</th>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="PTR records are not supported https://www.name.com/support/articles/205188508-Reverse-DNS-records (2017-05-08)">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage CAA records">CAA</th>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This provider is recommended for use in &#39;dual hosting&#39; scenarios. Usually this means the provider allows full control over the apex NS records">dual host</th>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="This driver does not manage NS records, so should not be used for dual-host scenarios">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Cloudflare will not work well in situations where it is not the only DNS server">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="DNSimple does not allow sufficient control over the apex NS records">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Apex NS records not editable">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This means the provider can automatically create domains that do not currently exist on your account. The &#39;dnscontrol create-domains&#39; command will initialize any missing domains">create-domains</th>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="AD depends on the zone already existing on the dns server">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success" data-toggle="tooltip" data-container="body" data-placement="top" title="Driver just maintains list of zone files. It should automatically add missing ones.">
<i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Can only manage domains registered through their service">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Requires domain registered through their service">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="New domains require registration">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="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.">no_purge</th>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
</tbody>
</table>

View file

@ -5,11 +5,11 @@
crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r"
crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-2.1.4.min.js" integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
<script src="https://use.fontawesome.com/03614296b7.js"></script>
<link rel="stylesheet" href="{{site.github.url}}/css/site.css">
<link rel="stylesheet" href="{{site.github.url}}/css/syntax.css">
@ -45,9 +45,6 @@
{{ content}}
</div>
{% comment %} Live reload script. Dev mode only {% endcomment %} {% unless site.github %}
<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script>
{% endunless %}
{% comment %} This script makes the examples collapse appropriately {% endcomment %}
<script>
@ -56,6 +53,9 @@
$(this).parent().next().collapse('toggle');
$(this).toggleClass('expanded')
});
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
</body>

View file

@ -12,4 +12,65 @@
body {
padding-bottom: 50px;
}
}
.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
}

View file

@ -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)

View file

@ -16,17 +16,26 @@ layout: default
{% endfor %}
</table>
<h2> Provider Features </h2>
<p>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.
</p>
<p>
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.
</p>
<p>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.</p>
<br/>
<br/>
{% 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.
<ul>
<li>AWS R53 (DNS works. Request is to add Registrar support) (<a href="https://github.com/StackExchange/dnscontrol/issues/68">#68</a>)</li>
<li>Azure (<a href="https://github.com/StackExchange/dnscontrol/issues/42">#42</a>)</li>
<li>ClouDNS (<a href="https://github.com/StackExchange/dnscontrol/issues/114">#114</a>)</li>
<li>Dyn (<a href="https://github.com/StackExchange/dnscontrol/issues/61">#61</a>)</li>
@ -71,5 +80,4 @@ code to support this provider, please re-open the issue. We'd be glad to help in
<li>Hurricane Electric (dns.he.net) (<a href="https://github.com/StackExchange/dnscontrol/issues/118">#118</a>)</li>
<li>Linode (<a href="https://github.com/StackExchange/dnscontrol/issues/121">#121</a>)</li>
<li>OVH (<a href="https://github.com/StackExchange/dnscontrol/issues/143">#143</a>)</li>
</ul>
</ul>
</ul>

View file

@ -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)

View file

@ -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) {

View file

@ -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 {

97
providers/capabilities.go Normal file
View file

@ -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, " "),
}
}

View file

@ -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", "")
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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)
}

View file

@ -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 {

View file

@ -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.
}

View file

@ -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)
}
///

View file

@ -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 {

View file

@ -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)
}

View file

@ -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)
}