mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-01-10 17:38:13 +08:00
324 lines
7.7 KiB
Go
324 lines
7.7 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/StackExchange/dnscontrol/v4/providers"
|
|
_ "github.com/StackExchange/dnscontrol/v4/providers/_all"
|
|
"github.com/fbiville/markdown-table-formatter/pkg/markdown"
|
|
)
|
|
|
|
func generateFeatureMatrix() error {
|
|
matrix := matrixData()
|
|
markdownTable, err := markdownTable(matrix)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
replaceInlineContent(
|
|
"documentation/providers.md",
|
|
"<!-- provider-matrix-start -->",
|
|
"<!-- provider-matrix-end -->",
|
|
markdownTable,
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
func markdownTable(matrix *FeatureMatrix) (string, error) {
|
|
var tableHeaders []string
|
|
tableHeaders = append(tableHeaders, "Provider name")
|
|
tableHeaders = append(tableHeaders, matrix.Features...)
|
|
|
|
var tableData [][]string
|
|
for _, providerName := range allProviderNames() {
|
|
featureMap := matrix.Providers[providerName]
|
|
|
|
var tableDataRow []string
|
|
tableDataRow = append(tableDataRow, "[`"+providerName+"`](providers/"+strings.ToLower(providerName)+".md)")
|
|
for _, featureName := range matrix.Features {
|
|
tableDataRow = append(tableDataRow, featureEmoji(featureMap, featureName))
|
|
}
|
|
tableData = append(tableData, tableDataRow)
|
|
}
|
|
|
|
var markdownTable, err = markdown.NewTableFormatterBuilder().
|
|
Build(tableHeaders...).
|
|
Format(tableData)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return markdownTable, nil
|
|
}
|
|
|
|
func featureEmoji(
|
|
featureMap FeatureMap,
|
|
featureName string,
|
|
) string {
|
|
if featureMap[featureName] == nil {
|
|
return "❔"
|
|
}
|
|
|
|
if featureMap[featureName].HasFeature {
|
|
return "✅"
|
|
} else if featureMap[featureName].Unimplemented {
|
|
return "❔"
|
|
}
|
|
return "❌"
|
|
}
|
|
|
|
func matrixData() *FeatureMatrix {
|
|
const (
|
|
OfficialSupport = "Official Support" // vs. community supported
|
|
ProviderDNSProvider = "DNS Provider"
|
|
ProviderRegistrar = "Registrar"
|
|
DomainModifierAlias = "[`ALIAS`](functions/domain/ALIAS.md)"
|
|
DomainModifierCaa = "[`CAA`](functions/domain/CAA.md)"
|
|
DomainModifierDnssec = "[`AUTODNSSEC`](functions/domain/AUTODNSSEC_ON.md)"
|
|
DomainModifierLoc = "[`LOC`](functions/domain/LOC.md)"
|
|
DomainModifierNaptr = "[`NAPTR`](functions/domain/NAPTR.md)"
|
|
DomainModifierPtr = "[`PTR`](functions/domain/PTR.md)"
|
|
DomainModifierSoa = "[`SOA`](functions/domain/SOA.md)"
|
|
DomainModifierSrv = "[`SRV`](functions/domain/SRV.md)"
|
|
DomainModifierSshfp = "[`SSHFP`](functions/domain/SSHFP.md)"
|
|
DomainModifierTlsa = "[`TLSA`](functions/domain/TLSA.md)"
|
|
DomainModifierDs = "[`DS`](functions/domain/DS.md)"
|
|
DomainModifierDhcid = "[`DHCID`](functions/domain/DHCID.md)"
|
|
DualHost = "dual host"
|
|
CreateDomains = "create-domains"
|
|
NoPurge = "[`NO_PURGE`](functions/domain/NO_PURGE.md)"
|
|
GetZones = "get-zones"
|
|
)
|
|
|
|
matrix := &FeatureMatrix{
|
|
Providers: map[string]FeatureMap{},
|
|
Features: []string{
|
|
OfficialSupport,
|
|
ProviderDNSProvider,
|
|
ProviderRegistrar,
|
|
DomainModifierAlias,
|
|
DomainModifierCaa,
|
|
DomainModifierDnssec,
|
|
DomainModifierLoc,
|
|
DomainModifierNaptr,
|
|
DomainModifierPtr,
|
|
DomainModifierSoa,
|
|
DomainModifierSrv,
|
|
DomainModifierSshfp,
|
|
DomainModifierTlsa,
|
|
DomainModifierDs,
|
|
DomainModifierDhcid,
|
|
DualHost,
|
|
CreateDomains,
|
|
NoPurge,
|
|
GetZones,
|
|
},
|
|
}
|
|
|
|
for _, providerName := range allProviderNames() {
|
|
featureMap := FeatureMap{}
|
|
providerNotes := providers.Notes[providerName]
|
|
if providerNotes == nil {
|
|
providerNotes = providers.DocumentationNotes{}
|
|
}
|
|
|
|
setCapability := func(
|
|
featureName string,
|
|
capability providers.Capability,
|
|
) {
|
|
if providerNotes[capability] != nil {
|
|
featureMap[featureName] = providerNotes[capability]
|
|
return
|
|
}
|
|
featureMap.SetSimple(
|
|
featureName,
|
|
true,
|
|
func() bool { return providers.ProviderHasCapability(providerName, capability) },
|
|
)
|
|
}
|
|
|
|
setDocumentation := func(
|
|
featureName string,
|
|
capability providers.Capability,
|
|
defaultNo bool,
|
|
) {
|
|
if providerNotes[capability] != nil {
|
|
featureMap[featureName] = providerNotes[capability]
|
|
} else if defaultNo {
|
|
featureMap[featureName] = &providers.DocumentationNote{
|
|
HasFeature: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
setDocumentation(
|
|
OfficialSupport,
|
|
providers.DocOfficiallySupported,
|
|
true,
|
|
)
|
|
featureMap.SetSimple(
|
|
ProviderDNSProvider,
|
|
false,
|
|
func() bool { return providers.DNSProviderTypes[providerName].Initializer != nil },
|
|
)
|
|
featureMap.SetSimple(
|
|
ProviderRegistrar,
|
|
false,
|
|
func() bool { return providers.RegistrarTypes[providerName] != nil },
|
|
)
|
|
setCapability(
|
|
DomainModifierAlias,
|
|
providers.CanUseAlias,
|
|
)
|
|
setCapability(
|
|
DomainModifierDnssec,
|
|
providers.CanAutoDNSSEC,
|
|
)
|
|
setCapability(
|
|
DomainModifierCaa,
|
|
providers.CanUseCAA,
|
|
)
|
|
setCapability(
|
|
DomainModifierDhcid,
|
|
providers.CanUseDHCID,
|
|
)
|
|
setCapability(
|
|
DomainModifierDs,
|
|
providers.CanUseDS,
|
|
)
|
|
setCapability(
|
|
DomainModifierLoc,
|
|
providers.CanUseLOC,
|
|
)
|
|
setCapability(
|
|
DomainModifierNaptr,
|
|
providers.CanUseNAPTR,
|
|
)
|
|
setCapability(
|
|
DomainModifierPtr,
|
|
providers.CanUsePTR,
|
|
)
|
|
setCapability(
|
|
DomainModifierSoa,
|
|
providers.CanUseSOA,
|
|
)
|
|
setCapability(
|
|
DomainModifierSrv,
|
|
providers.CanUseSRV,
|
|
)
|
|
setCapability(
|
|
DomainModifierSshfp,
|
|
providers.CanUseSSHFP,
|
|
)
|
|
setCapability(
|
|
DomainModifierTlsa,
|
|
providers.CanUseTLSA,
|
|
)
|
|
setCapability(
|
|
GetZones,
|
|
providers.CanGetZones,
|
|
)
|
|
setDocumentation(
|
|
CreateDomains,
|
|
providers.DocCreateDomains,
|
|
true,
|
|
)
|
|
setDocumentation(
|
|
DualHost,
|
|
providers.DocDualHost,
|
|
false,
|
|
)
|
|
|
|
// no purge is a freaky double negative
|
|
cantUseNOPURGE := providers.CantUseNOPURGE
|
|
if providerNotes[cantUseNOPURGE] != nil {
|
|
featureMap[NoPurge] = providerNotes[cantUseNOPURGE]
|
|
} else {
|
|
featureMap.SetSimple(
|
|
NoPurge,
|
|
false,
|
|
func() bool { return !providers.ProviderHasCapability(providerName, cantUseNOPURGE) },
|
|
)
|
|
}
|
|
matrix.Providers[providerName] = featureMap
|
|
}
|
|
return matrix
|
|
}
|
|
|
|
func allProviderNames() []string {
|
|
const ProviderNameNone = "NONE"
|
|
|
|
allProviderNames := map[string]bool{}
|
|
for providerName := range providers.RegistrarTypes {
|
|
if providerName == ProviderNameNone {
|
|
continue
|
|
}
|
|
allProviderNames[providerName] = true
|
|
}
|
|
for providerName := range providers.DNSProviderTypes {
|
|
if providerName == ProviderNameNone {
|
|
continue
|
|
}
|
|
allProviderNames[providerName] = true
|
|
}
|
|
|
|
var allProviderNamesAsString []string
|
|
for providerName := range allProviderNames {
|
|
allProviderNamesAsString = append(allProviderNamesAsString, providerName)
|
|
}
|
|
sort.Strings(allProviderNamesAsString)
|
|
|
|
return allProviderNamesAsString
|
|
}
|
|
|
|
// FeatureMap maps provider names to compliance documentation.
|
|
type FeatureMap map[string]*providers.DocumentationNote
|
|
|
|
// SetSimple configures a provider's setting in featureMap.
|
|
func (featureMap FeatureMap) SetSimple(
|
|
name string,
|
|
unknownsAllowed bool,
|
|
f func() bool,
|
|
) {
|
|
if f() {
|
|
featureMap[name] = &providers.DocumentationNote{HasFeature: true}
|
|
} else if !unknownsAllowed {
|
|
featureMap[name] = &providers.DocumentationNote{HasFeature: false}
|
|
}
|
|
}
|
|
|
|
// FeatureMatrix describes features and which providers support it.
|
|
type FeatureMatrix struct {
|
|
Features []string
|
|
Providers map[string]FeatureMap
|
|
}
|
|
|
|
func replaceInlineContent(
|
|
file string,
|
|
startMarker string,
|
|
endMarker string,
|
|
newContent string,
|
|
) {
|
|
contentBytes, err := os.ReadFile(file)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
content := string(contentBytes)
|
|
|
|
start := strings.Index(content, startMarker)
|
|
end := strings.Index(content, endMarker)
|
|
|
|
newContentString := startMarker + "\n" + newContent + endMarker
|
|
newContentBytes := []byte(newContentString)
|
|
contentBytes = []byte(content)
|
|
contentBytes = append(contentBytes[:start], append(newContentBytes, contentBytes[end+len(endMarker):]...)...)
|
|
|
|
err = os.WriteFile(file, contentBytes, 0644)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|