NEW FEATURE: Moving provider TYPE from dnsconfig.js to creds.json (#1500)

Fixes https://github.com/StackExchange/dnscontrol/issues/1457

* New-style creds.json implememented backwards compatible

* Update tests

* Update docs

* Assume new-style TYPE
This commit is contained in:
Tom Limoncelli 2022-05-08 14:23:45 -04:00 committed by GitHub
parent bbecce74bd
commit 9e6d642e35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 949 additions and 108 deletions

View file

@ -22,11 +22,21 @@ var _ = cmd(catUtils, func() *cli.Command {
Action: func(ctx *cli.Context) error {
if ctx.NArg() < 3 {
return cli.Exit("Arguments should be: credskey providername zone(s) (Ex: r53 ROUTE53 example.com)", 1)
}
args.CredName = ctx.Args().Get(0)
args.ProviderName = ctx.Args().Get(1)
arg1 := ctx.Args().Get(1)
args.ProviderName = arg1
// In v4.0, skip the first args.ZoneNames if it it equals "-".
args.ZoneNames = ctx.Args().Slice()[2:]
if arg1 != "" && arg1 != "-" {
// NB(tlim): In v4.0 this "if" can be removed.
fmt.Fprintf(os.Stderr, "WARNING: To retain compatibility in future versions, please change %q to %q. See %q\n",
arg1, "-",
"https://stackexchange.github.io/dnscontrol/get-zones.html",
)
}
return exit(GetZone(args))
},
Flags: args.flags(),
@ -73,12 +83,21 @@ var _ = cmd(catUtils, func() *cli.Command {
Name: "check-creds",
Usage: "Do a small operation to verify credentials (stand-alone)",
Action: func(ctx *cli.Context) error {
if ctx.NArg() != 2 {
return cli.Exit("Arguments should be: credskey providername (Ex: r53 ROUTE53)", 1)
var arg0, arg1 string
// This takes one or two command-line args.
// Starting in v3.16: Using it with 2 args will generate a warning.
// Starting in v4.0: Using it with 2 args might be an error.
if ctx.NArg() == 1 {
arg0 = ctx.Args().Get(0)
arg1 = ""
} else if ctx.NArg() == 2 {
arg0 = ctx.Args().Get(0)
arg1 = ctx.Args().Get(1)
} else {
return cli.Exit("Arguments should be: credskey [providername] (Ex: r53 ROUTE53)", 1)
}
args.CredName = ctx.Args().Get(0)
args.ProviderName = ctx.Args().Get(1)
args.CredName = arg0
args.ProviderName = arg1
args.ZoneNames = []string{"all"}
args.OutputFormat = "nameonly"
return exit(GetZone(args))
@ -95,8 +114,9 @@ ARGUMENTS:
provider: The name of the provider (second parameter to NewDnsProvider() in dnsconfig.js)
EXAMPLES:
dnscontrol get-zones myr53 ROUTE53
dnscontrol get-zones --out=/dev/null myr53 ROUTE53`,
dnscontrol check-creds myr53 ROUTE53 # Pre v3.16, or pre-v4.0 for backwards-compatibility
dnscontrol check-creds myr53
dnscontrol check-creds --out=/dev/null myr53 && echo Success`,
}
}())
@ -104,7 +124,7 @@ EXAMPLES:
type GetZoneArgs struct {
GetCredentialsArgs // Args related to creds.json
CredName string // key in creds.json
ProviderName string // provider name: BIND, GANDI_V5, etc or "-"
ProviderName string // provider type: BIND, GANDI_V5, etc or "-" (NB(tlim): In 4.0, this field goes away.)
ZoneNames []string // The zones to get
OutputFormat string // Output format
OutputFile string // Filename to send output ("" means stdout)
@ -144,7 +164,7 @@ func GetZone(args GetZoneArgs) error {
}
provider, err := providers.CreateDNSProvider(args.ProviderName, providerConfigs[args.CredName], nil)
if err != nil {
return fmt.Errorf("failed GetZone CreateDNSProvider: %w", err)
return fmt.Errorf("failed GetZone CDP: %w", err)
}
// decide which zones we need to convert

View file

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"os"
"strings"
"github.com/urfave/cli/v2"
@ -99,10 +100,6 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
if err != nil {
return err
}
errs := normalize.ValidateAndNormalizeConfig(cfg)
if PrintValidationErrors(errs) {
return fmt.Errorf("exiting due to validation errors")
}
providerConfigs, err := credsfile.LoadProviderConfigs(args.CredsFile)
if err != nil {
return err
@ -111,6 +108,11 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
if err != nil {
return err
}
errs := normalize.ValidateAndNormalizeConfig(cfg)
if PrintValidationErrors(errs) {
return fmt.Errorf("exiting due to validation errors")
}
anyErrors := false
totalCorrections := 0
DomainLoop:
@ -200,6 +202,16 @@ func InitializeProviders(cfg *models.DNSConfig, providerConfigs map[string]map[s
isNonDefault[name] = true
}
}
// Populate provider type ids based on values from creds.json:
msgs, err := populateProviderTypes(cfg, providerConfigs)
if len(msgs) != 0 {
fmt.Fprintln(os.Stderr, strings.Join(msgs, "\n"))
}
if err != nil {
return
}
registrars := map[string]providers.Registrar{}
dnsProviders := map[string]providers.DNSServiceProvider{}
for _, d := range cfg.Domains {
@ -229,6 +241,200 @@ func InitializeProviders(cfg *models.DNSConfig, providerConfigs map[string]map[s
return
}
// providerTypeFieldName is the name of the field in creds.json that specifies the provider type id.
const providerTypeFieldName = "TYPE"
// url is the documentation URL to list in the warnings related to missing provider type ids.
const url = "https://stackexchange.github.io/dnscontrol/creds-json"
// populateProviderTypes scans a DNSConfig for blank provider types and fills them in based on providerConfigs.
// That is, if the provider type is "-" or "", we take that as an flag
// that means this value should be replaced by the type found in creds.json.
func populateProviderTypes(cfg *models.DNSConfig, providerConfigs map[string]map[string]string) ([]string, error) {
var msgs []string
for i := range cfg.Registrars {
pType := cfg.Registrars[i].Type
pName := cfg.Registrars[i].Name
nt, warnMsg, err := refineProviderType(pName, pType, providerConfigs[pName], "NewRegistrar")
cfg.Registrars[i].Type = nt
if warnMsg != "" {
msgs = append(msgs, warnMsg)
}
if err != nil {
return msgs, err
}
}
for i := range cfg.DNSProviders {
pName := cfg.DNSProviders[i].Name
pType := cfg.DNSProviders[i].Type
nt, warnMsg, err := refineProviderType(pName, pType, providerConfigs[pName], "NewDnsProvider")
cfg.DNSProviders[i].Type = nt
if warnMsg != "" {
msgs = append(msgs, warnMsg)
}
if err != nil {
return msgs, err
}
}
// Update these fields set by // commands/commands.go:preloadProviders().
// This is probably a layering violation. That said, the
// fundamental problem here is that we're storing the provider
// instances by string name, not by a pointer to a struct. We
// should clean that up someday.
for _, domain := range cfg.Domains { // For each domain..
for _, provider := range domain.DNSProviderInstances { // For each provider...
pName := provider.ProviderBase.Name
pType := provider.ProviderBase.ProviderType
nt, warnMsg, err := refineProviderType(pName, pType, providerConfigs[pName], "NewDnsProvider")
provider.ProviderBase.ProviderType = nt
if warnMsg != "" {
msgs = append(msgs, warnMsg)
}
if err != nil {
return msgs, err
}
}
p := domain.RegistrarInstance
pName := p.Name
pType := p.ProviderType
nt, warnMsg, err := refineProviderType(pName, pType, providerConfigs[pName], "NewRegistrar")
p.ProviderType = nt
if warnMsg != "" {
msgs = append(msgs, warnMsg)
}
if err != nil {
return msgs, err
}
}
return uniqueStrings(msgs), nil
}
// uniqueStrings takes an unsorted slice of strings and returns the
// unique strings, in the order they first appeared in the list.
func uniqueStrings(stringSlice []string) []string {
keys := make(map[string]bool)
list := []string{}
for _, entry := range stringSlice {
if _, ok := keys[entry]; !ok {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
func refineProviderType(credEntryName string, t string, credFields map[string]string, source string) (replacementType string, warnMsg string, err error) {
// t="" and t="-" are processed the same. Standardize on "-" to reduce the number of cases to check.
if t == "" {
t = "-"
}
// Use cases:
//
// type credsType
// ---- ---------
// - or "" GANDI lookup worked. Nothing to say.
// - or "" - or "" ERROR "creds.json has invalid or missing data"
// GANDI "" WARNING "Working but.... Please fix as follows..."
// GANDI GANDI INFO "working but unneeded: clean up as follows..."
// GANDI NAMEDOT ERROR "error mismatched: please fix as follows..."
// ERROR: Invalid.
// WARNING: Required change to remain compatible with 4.0
// INFO: Post-4.0 cleanups or other non-required changes.
if t != "-" {
// Old-style, dnsconfig.js specifies the type explicitly.
// This is supported but we suggest updates for future compatibility.
// If credFields is nil, that means there was no entry in creds.json:
if credFields == nil {
// Warn the user to update creds.json in preparation for 4.0:
// In 4.0 this should be an error. We could default to a
// provider such as "NONE" but I suspect it would be confusing
// to users to see references to a provider name that they did
// not specify.
return t, fmt.Sprintf(`WARNING: For future compatibility, add this entry creds.json: %q: { %q: %q }, (See %s#missing)`,
credEntryName, providerTypeFieldName, t,
url,
), nil
}
switch ct := credFields[providerTypeFieldName]; ct {
case "":
// Warn the user to update creds.json in preparation for 4.0:
// In 4.0 this should be an error.
return t, fmt.Sprintf(`WARNING: For future compatibility, update the %q entry in creds.json by adding: %q: %q, (See %s#missing)`,
credEntryName,
providerTypeFieldName, t,
url,
), nil
case "-":
// This should never happen. The user is specifying "-" in a place that it shouldn't be used.
return "-", "", fmt.Errorf(`ERROR: creds.json entry %q has invalid %q value %q (See %s#hyphen)`,
credEntryName, providerTypeFieldName, ct,
url,
)
case t:
// creds.json file is compatible with and dnsconfig.js can be updated.
return ct, fmt.Sprintf(`INFO: In dnsconfig.js %s(%q, %q) can be simplified to %s(%q) (See %s#cleanup)`,
source, credEntryName, t,
source, credEntryName,
url,
), nil
default:
// creds.json lists a TYPE but it doesn't match what's in dnsconfig.js!
return t, "", fmt.Errorf(`ERROR: Mismatch found! creds.json entry %q has %q set to %q but dnsconfig.js specifies %s(%q, %q) (See %s#mismatch)`,
credEntryName,
providerTypeFieldName, ct,
source, credEntryName, t,
url,
)
}
}
// t == "-"
// New-style, dnsconfig.js does not specify the type (t == "") or a
// command line tool accepted "-" as a positional argument for
// backwards compatibility.
// If credFields is nil, that means there was no entry in creds.json:
if credFields == nil {
return "", "", fmt.Errorf(`ERROR: creds.json is missing an entry called %q. Suggestion: %q: { %q: %q }, (See %s#missing)`,
credEntryName,
credEntryName, providerTypeFieldName, "FILL_IN_PROVIDER_TYPE",
url,
)
}
// New-style, dnsconfig.js doesn't specifies the type. It will be
// looked up in creds.json.
switch ct := credFields[providerTypeFieldName]; ct {
case "":
return ct, "", fmt.Errorf(`ERROR: creds.json entry %q is missing: %q: %q, (See %s#fixcreds)`,
credEntryName,
providerTypeFieldName, "FILL_IN_PROVIDER_TYPE",
url,
)
case "-":
// This should never happen. The user is confused and specified "-" in the wrong place!
return "-", "", fmt.Errorf(`ERROR: creds.json entry %q has invalid %q value %q (See %s#hyphen)`,
credEntryName,
providerTypeFieldName, ct,
url,
)
default:
// use the value in creds.json (this should be the normal case)
return ct, "", nil
}
}
func printOrRunCorrections(domain string, provider string, corrections []*models.Correction, out printer.CLI, push bool, interactive bool, notifier notifications.Notifier) (anyErrors bool) {
anyErrors = false
if len(corrections) == 0 {

View file

@ -0,0 +1,58 @@
package commands
import (
"strings"
"testing"
)
func Test_refineProviderType(t *testing.T) {
var mapEmpty map[string]string
mapTypeMissing := map[string]string{"otherfield": "othervalue"}
mapTypeFoo := map[string]string{"TYPE": "FOO"}
mapTypeBar := map[string]string{"TYPE": "BAR"}
mapTypeHyphen := map[string]string{"TYPE": "-"}
type args struct {
t string
credFields map[string]string
}
tests := []struct {
name string
args args
wantReplacementType string
wantWarnMsgPrefix string
wantErr bool
}{
{"fooEmp", args{"FOO", mapEmpty}, "FOO", "WARN", false}, // 3.x: Provide compatibility suggestion. 4.0: hard error
{"fooMis", args{"FOO", mapTypeMissing}, "FOO", "WARN", false}, // 3.x: Provide compatibility suggestion. 4.0: hard error
{"fooHyp", args{"FOO", mapTypeHyphen}, "-", "", true}, // Error: Invalid creds.json data.
{"fooFoo", args{"FOO", mapTypeFoo}, "FOO", "INFO", false}, // Suggest cleanup.
{"fooBar", args{"FOO", mapTypeBar}, "FOO", "", true}, // Error: Mismatched!
{"hypEmp", args{"-", mapEmpty}, "", "", true}, // Hard error. creds.json entry is missing type.
{"hypMis", args{"-", mapTypeMissing}, "", "", true}, // Hard error. creds.json entry is missing type.
{"hypHyp", args{"-", mapTypeHyphen}, "-", "", true}, // Hard error: Invalid creds.json data.
{"hypFoo", args{"-", mapTypeFoo}, "FOO", "", false}, // normal
{"hypBar", args{"-", mapTypeBar}, "BAR", "", false}, // normal
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr && (tt.wantWarnMsgPrefix != "") {
t.Error("refineProviderType() bad test data. Prefix should be \"\" if wantErr is set")
}
gotReplacementType, gotWarnMsg, err := refineProviderType("foo", tt.args.t, tt.args.credFields, "FOO")
if !strings.HasPrefix(gotWarnMsg, tt.wantWarnMsgPrefix) {
t.Errorf("refineProviderType() gotWarnMsg = %q, wanted prefix %q", gotWarnMsg, tt.wantWarnMsgPrefix)
}
if (err != nil) != tt.wantErr {
t.Errorf("refineProviderType() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotReplacementType != tt.wantReplacementType {
t.Errorf("refineProviderType() gotReplacementType = %q, want %q (warn,msg)=(%q,%s)", gotReplacementType, tt.wantReplacementType, gotWarnMsg, err)
}
})
}
}

View file

@ -19,8 +19,8 @@ Modifier arguments are processed according to type as follows:
{% capture example %}
```js
var REGISTRAR = NewRegistrar("name.com", "NAMEDOTCOM");
var r53 = NewDnsProvider("R53","ROUTE53");
var REGISTRAR = NewRegistrar("name.com");
var r53 = NewDnsProvider("R53");
// simple domain
D("example.com", REGISTRAR, DnsProvider(r53),
@ -60,9 +60,9 @@ To differentiate the different domains, specify the domains as
{% capture example %}
```js
var REG = NewRegistrar("Third-Party", "NONE");
var DNS_INSIDE = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
var DNS_OUTSIDE = NewDnsProvider("bind", "BIND");
var REG = NewRegistrar("Third-Party");
var DNS_INSIDE = NewDnsProvider("Cloudflare");
var DNS_OUTSIDE = NewDnsProvider("bind");
D("example.com!inside", REG, DnsProvider(DNS_INSIDE),
A("www", "10.10.10.10")

View file

@ -9,7 +9,7 @@ arguments passed as if they were the first modifiers in the argument list.
{% capture example %}
```js
var COMMON = NewDnsProvider("foo","BIND");
var COMMON = NewDnsProvider("foo");
// we want to create backup zone files for all domains, but not actually register them.
// also create a default TTL
DEFAULTS( DnsProvider(COMMON,0), DefaultTTL(1000));

View file

@ -20,8 +20,8 @@ Otherwise the syntax of `FETCH` is the same as `fetch`.
{% capture example %}
```js
var REG_NONE = NewRegistrar('none', 'NONE');
var DNS_BIND = NewDnsProvider('bind', 'BIND');
var REG_NONE = NewRegistrar('none');
var DNS_BIND = NewDnsProvider('bind');
D('example.com', REG_NONE, DnsProvider(DNS_BIND), [
A('@', '1.2.3.4'),

View file

@ -7,20 +7,36 @@ parameters:
return: string
---
NewDnsProvider registers a new DNS Service Provider. The name can be any string value you would like to use.
The type must match a valid dns provider type identifier (see [provider page.]({{site.github.url}}/provider-list))
NewDnsProvider activates a DNS Service Provider (DSP) specified in creds.json.
A DSP stores a DNS zone's records and provides DNS service for the zone (i.e.
answers on port 53 to queries related to the zone).
Metadata is an optional object, that will only be used by certain providers. See [individual provider docs]({{site.github.url}}/provider-list) for specific details.
* `name` must match the name of an entry in `creds.json`.
* `type` specifies a valid DNS provider type identifier listed on the [provider page.]({{site.github.url}}/provider-list).
* Starting with v3.16, the type is optional. If it is absent, the `TYPE` field in `creds.json` is used instead. You can leave it out. (Thanks to JavaScript magic, you can leave it out even when there are more fields).
* Starting with v4.0, specifying the type may be an error. Please add the `TYPE` field to `creds.json` and remove this parameter from `dnsconfig.js` to prepare.
* `meta` is a way to send additional parameters to the provider. It is optional and only certain providers use it. See the [individual provider docs]({{site.github.url}}/provider-list) for details.
This function will return the name as a string so that you may assign it to a variable to use inside [D](#D) directives.
This function will return an opaque string that should be assigned to a variable name for use in [D](#D) directives.
Prior to v3.16:
{% capture example %}
```js
var REGISTRAR = NewRegistrar("name.com", "NAMEDOTCOM");
var R53 = NewDnsProvider("r53", "ROUTE53");
var REG_MYNDC = NewRegistrar("mynamedotcom", "NAMEDOTCOM");
var DNS_MYAWS = NewDnsProvider("myaws", "ROUTE53");
D("example.com", REGISTRAR, DnsProvider(R53), A("@","1.2.3.4"));
D("example.com", REG_MYNDC, DnsProvider(DNS_MYAWS),
A("@","1.2.3.4")
);
```
{% endcapture %}
{% include example.html content=example %}
In v3.16 and later:
```js
var REG_MYNDC = NewRegistrar("mynamedotcom");
var DNS_MYAWS = NewDnsProvider("myaws");
D("example.com", REG_MYNDC, DnsProvider(DNS_MYAWS),
A("@","1.2.3.4")
);
```

View file

@ -7,20 +7,36 @@ parameters:
return: string
---
NewRegistrar registers a registrar provider. The name can be any string value you would like to use.
The type must match a valid registrar provider type identifier (see [provider page.]({{site.github.url}}/provider-list))
NewRegistrar activates a Registrar Provider specified in `creds.json`.
A registrar maintains the domain's registration and delegation (i.e. the
nameservers for the domain). DNSControl only manages the delegation.
Metadata is an optional object, that will only be used by certain providers. See [individual provider docs]({{site.github.url}}/provider-list) for specific details.
* `name` must match the name of an entry in `creds.json`.
* `type` specifies a valid DNS provider type identifier listed on the [provider page.]({{site.github.url}}/provider-list).
* Starting with v3.16, the type is optional. If it is absent, the `TYPE` field in `creds.json` is used instead. You can leave it out. (Thanks to JavaScript magic, you can leave it out even when there are more fields).
* Starting with v4.0, specifying the type may be an error. Please add the `TYPE` field to `creds.json` and remove this parameter from `dnsconfig.js` to prepare.
* `meta` is a way to send additional parameters to the provider. It is optional and only certain providers use it. See the [individual provider docs]({{site.github.url}}/provider-list) for details.
This function will return the name as a string so that you may assign it to a variable to use inside [D](#D) directives.
This function will return an opaque string that should be assigned to a variable name for use in [D](#D) directives.
Prior to v3.16:
{% capture example %}
```js
var REGISTRAR = NewRegistrar("name.com", "NAMEDOTCOM");
var r53 = NewDnsProvider("R53","ROUTE53");
var REG_MYNDC = NewRegistrar("mynamedotcom", "NAMEDOTCOM");
var DNS_MYAWS = NewDnsProvider("myaws", "ROUTE53");
D("example.com", REGISTRAR, DnsProvider(r53), A("@","1.2.3.4"));
D("example.com", REG_MYNDC, DnsProvider(DNS_MYAWS),
A("@","1.2.3.4")
);
```
{% endcapture %}
{% include example.html content=example %}
In v3.16 and later:
```js
var REG_MYNDC = NewRegistrar("mynamedotcom");
var DNS_MYAWS = NewDnsProvider("myaws");
D("example.com", REG_MYNDC, DnsProvider(DNS_MYAWS),
A("@","1.2.3.4")
);
```

View file

@ -1,7 +1,9 @@
{
"bind": {
"TYPE": "BIND"
},
"r53_ACCOUNTNAME": {
"TYPE": "ROUTE53",
"KeyId": "change_to_your_keyid",
"SecretKey": "change_to_your_secretkey",
"Token": "optional_sts_token"

View file

@ -4,8 +4,8 @@
// Providers:
var REG_NONE = NewRegistrar('none', 'NONE'); // No registrar.
var DNS_BIND = NewDnsProvider('bind', 'BIND'); // ISC BIND.
var REG_NONE = NewRegistrar('none'); // No registrar.
var DNS_BIND = NewDnsProvider('bind'); // ISC BIND.
// Domains:

View file

@ -8,8 +8,7 @@ title: Check-Creds subcommand
This is a stand-alone utility to help verify entries in `creds.json`.
The command does a trivia operation to verify credentials. If
successful, a list of zones will be output. If not, hopefully you see
verbose error messages.
successful, a list of zones will be output (which may be an empty list). If the credentials or other problems prevent this operation from executing, the exit code will be non-zero and hopefully verbose error messages will be output.
Syntax:
@ -22,14 +21,23 @@ ARGUMENTS:
credkey: The name used in creds.json (first parameter to NewDnsProvider() in dnsconfig.js)
provider: The name of the provider (second parameter to NewDnsProvider() in dnsconfig.js)
Starting in v3.16, "provider" is optional. If it is omitted (or the placeholder value `-` is used), the `TYPE` specified in `creds.json` will be used instead. A warning will be displayed with advice on how to remain compatible with v4.0.
Starting in v4.0, the "provider" argument is expected to go away.
EXAMPLES:
dnscontrol check-creds myr53 ROUTE53
Starting in v3.16:
dnscontrol check-creds myr53
dnscontrol check-creds myr53 -
dnscontrol check-creds myr53 ROUTE53
Starting in v4.0:
dnscontrol check-creds myr53
This command is the same as:
dnscontrol get-zones --out=/dev/null myr53 ROUTE53
This command is the same as `get-zones` with `--format=nameonly`
# Developer Note
This command is not implemented for all providers.
To add this to a provider, implement the get-zones subcommand
To add this to a provider, implement the get-zones subcommand.

View file

@ -13,14 +13,17 @@ Here's a sample file:
```json
{
"cloudflare_tal": {
"TYPE": "CLOUDFLAREAPI",
"apikey": "REDACTED",
"apiuser": "REDACTED"
},
"inside": {
"TYPE": "BIND",
"directory": "inzones",
"filenameformat": "db_%T%?_%D"
},
"hexonet": {
"TYPE": "HEXONET",
"apilogin": "$HEXONET_APILOGIN",
"apipassword": "$HEXONET_APIPASSWORD",
"debugmode": "$HEXONET_DEBUGMODE",
@ -29,7 +32,7 @@ Here's a sample file:
}
```
# Format
## Format
* Primary keys: (e.g. `cloudflare_tal`, `inside`, `hexonet`)
* ...refer to the first parameter in the `NewRegistrar()` or `NewDnsProvider()` functions in a dnsconfig.js file.
@ -43,7 +46,130 @@ Here's a sample file:
* ...may include any JSON string value including the empty string.
* If a subkey starts with `$`, it is taken as an env variable. In the above example, `$HEXONET_APILOGIN` would be replaced by the value of the environment variable `HEXONET_APILOGIN` or the empty string if no such environment variable exists.
# Using a different name
## New in v3.16:
The special subkey "TYPE" is used to indicate the provider type (NONE,
CLOUDFLAREAPI, GCLOUD, etc).
Prior to v3.16, the provider type is specified as the second argument
to `NewRegistrar()` and `NewDnsProvider()` in `dnsconfig.js` or as a
command-line argument in tools such as `dnscontrol get-zones`.
Starting in v3.16, `NewRegistrar()`, and `NewDnsProvider()` no longer
require the provider type to be specified. It may be specified for
backwards compatibility, but a warning will be generated with a
suggestion of how to upgrade to the 4.0 format. Likewise,
command-line tools no longer require the provider type to be
specified, but for backwards compatibility one may specify `-` since
the parameter is positional.
In 4.0, DNSControl will require the "TYPE" subkey in each `creds.json`
entry. Command line tools will have a backwards-incompatible change to
remove the provider-type as a positional argument. Prior to 4.0, the
various commands will output warnings and suggestions to avoid
compatibility issues during the transition.
## Error messages
### Missing
Message: `WARNING: For future compatibility, add this entry creds.json:...`
Message: `WARNING: For future compatibility, update the ... entry in creds.json by adding:...`
These messages indicates that this provider is not mentioned in `creds.json`. In v4.0
all providers used in `dnsconfig.js` will require an entry in `creds.json`.
For a smooth transition, please update your `creds.json` file now.
Here is the minimal entry required:
```json
{
"entryName": {
"TYPE": "FILL_IN"
}
}
```
### hyphen
Message: `ERROR: creds.json entry ... has invalid ... value ...`
This indicates the entry for `creds.json` has a TYPE value that is
invalid i.e. it is the empty string or a hyphen (`-`).
The fix is to correct the `TYPE` parameter in the `creds.json` entry.
Change it to one of the all caps identifiers in [the service provider list](https://stackexchange.github.io/dnscontrol/provider-list).
### cleanup
Message: `INFO: In dnsconfig.js New*(..., ...) can be simplified to New*(...)`
This message indicates that the same provider name is specified in
`dnsconfig.js` and `creds.json` and offers a suggestion for reducing
the redundancy.
The fix is to update `dnsconfig.js` as suggested in the error.
Usually this is to simply remove the second parameter to the function.
Examples:
```
OLD: var REG_THING = NewRegistrar("thing", "THING");
NEW: var REG_THING = NewRegistrar("thing");
OLD: var REG_THING = NewRegistrar("thing", "THING", { settings: "value" } );
NEW: var REG_THING = NewRegistrar("thing", { settings: "value" } );
OLD: var DNS_MYGANDI = NewDnsProvider("mygandi", "GANDI_V5");
NEW: var DNS_MYGANDI = NewDnsProvider("mygandi");
OLD: var DNS_MYGANDI = NewDnsProvider("mygandi", "GANDI_V5", { settings: "value" } );
NEW: var DNS_MYGANDI = NewDnsProvider("mygandi", { settings: "value" } );
```
Starting with v3.16 use of an OLD format will trigger warnings with suggestions on how to adopt the NEW format.
Starting with v4.0 support for the OLD format may be reported as an error.
Please adopt the NEW format when your installation has eliminated any use of DNSControl pre-3.16.
### mismatch
Message: `ERROR: Mismatch found! creds.json entry ... has ... set to ... but dnsconfig.js specifies New*(..., ...)`
This indicates that the provider type specifed in `creds.json` does not match the one specifed in `dnsconfig.js` or on the command line.
The fix is to change one to match the other.
### fixcreds
Message: `ERROR: creds.json entry ... is missing ...: ...`
However no `TYPE` subkey was found in an entry in `creds.json`.
In 3.16 forward, it is required if new-style `NewRegistrar()` or `NewDnsProvider()` was used.
In 4.0 this is required.
The fix is to add a `TYPE` subkey to the `creds.json` entry.
### hyphen
Message: `ERROR: creds.json entry ... has invalid ... value ...`
This indicates that the type `-` was specified in a `TYPE` value in
`creds.json`. There is no provider named `-` therefore that is
invalid. Perhaps you meant to specify a `-` on a command-line tool?
The fix is to change the `TYPE` subkey entry in `creds.json` from `-` to
a valid service provider identifier, as listed
in [the service provider list](https://stackexchange.github.io/dnscontrol/provider-list).
## Using a different file name
The `--creds` flag allows you to specify a different file name.
@ -55,7 +181,7 @@ The `--creds` flag allows you to specify a different file name.
* Exceptions: The `x` bit is not checked if the filename ends with `.yaml`, `.yml` or `.json`.
* Windows: Executing an external script isn't supported. There's no code that prevents it from trying, but it isn't supported.
# Don't store secrets in a Git repo!
## Don't store secrets in a Git repo!
Do NOT store secrets in a Git repository. That is not secure. For example,
storing the example `cloudflare_tal` is insecure because anyone with access to

View file

@ -68,6 +68,10 @@ zones at the provider.
provider: The name of the provider (second parameter to NewDnsProvider() in dnsconfig.js)
zone: One or more zones (domains) to download; or "all".
As of v3.16, `provider` can be `-` to indicate that the provider name is listed in `creds.json` in the `TYPE` field. Doing this will be backwards compatible with an (otherwise) breaking change due in v4.0.
As of v4.0 (BREAKING CHANGE), you must not specify `provider`. That value is found in the `TYPE` field of the credkey's `creds.json` file. For backwards compatibility, if the first `zone` is `-`, it will be skipped.
FORMATS:
--format=js dnsconfig.js format (not perfect, just a decent first draft)
--format=djs js with disco commas (leading commas)
@ -92,12 +96,35 @@ The `--ttl` flag only applies to zone/js/djs formats.
dnscontrol get-zones gmain GANDI_V5 example.comn other.com
dnscontrol get-zones cfmain CLOUDFLAREAPI all
dnscontrol get-zones --format=tsv bind BIND example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud GCLOUD example.com`,
dnscontrol get-zones --format=djs --out=draft.js glcoud GCLOUD example.com
As of v3.16:
# NOTE: When "-" appears as the 2nd argument, it is assumed that the
# creds.json entry has a field TYPE with the provider's type name.
dnscontrol get-zones gmain GANDI_V5 example.comn other.com
dnscontrol get-zones gmain - example.comn other.com
dnscontrol get-zones cfmain CLOUDFLAREAPI all
dnscontrol get-zones cfmain - all
dnscontrol get-zones --format=tsv bind BIND example.com
dnscontrol get-zones --format=tsv bind - example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud GCLOUD example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud - example.com
As of v4.0:
dnscontrol get-zones gmain example.comn other.com
dnscontrol get-zones cfmain all
dnscontrol get-zones --format=tsv bind example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud example.com
# For backwards compatibility, these are valid until at least v5.0
dnscontrol get-zones gmain - example.comn other.com
dnscontrol get-zones cfmain - all
dnscontrol get-zones --format=tsv bind - example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud - example.com
Read a zonefile, generate a JS file, then use the JS file to see how
different it is from the zonefile:
dnscontrol get-zone --format=djs -out=foo.djs bind BIND example.org
dnscontrol get-zone --format=djs -out=foo.djs bind - example.org
dnscontrol preview --config foo.js
# Developer Notes

View file

@ -9,7 +9,7 @@ title: Getting Started
## From source
DNSControl can be built with Go version 1.16 or higher.
DNSControl can be built with Go version 1.18 or higher.
The `go get` command will download the source, compile it, and
install `dnscontrol` in your `$GOBIN` directory.
@ -48,13 +48,6 @@ Create a directory where you'll be storing your configuration files.
We highly recommend storing these files in a Git repo, but for
simple tests anything will do.
Note: Do **not** store your creds.json file in Git unencrypted.
That is unsafe. Add `creds.json` to your
`.gitignore` file as a precaution. This file should be encrypted
using something
like [git-crypt](https://www.agwa.name/projects/git-crypt) or
[Blackbox](https://github.com/StackExchange/blackbox).
Create a subdirectory called `zones` in the same directory as the
configuration files. (`mkdir zones`). `zones` is where the BIND
provider writes the zonefiles it creates. Even if you don't
@ -75,8 +68,8 @@ The file looks like:
```js
// Providers:
var REG_NONE = NewRegistrar('none', 'NONE'); // No registrar.
var DNS_BIND = NewDnsProvider('bind', 'BIND'); // ISC BIND.
var REG_NONE = NewRegistrar('none'); // No registrar.
var DNS_BIND = NewDnsProvider('bind'); // ISC BIND.
// Domains:
@ -85,30 +78,43 @@ D('example.com', REG_NONE, DnsProvider(DNS_BIND),
);
```
You may modify this file to match your particular providers and domains. See [the javascript docs]({{site.github.url}}/js) and [the provider docs]({{site.github.url}}/provider-list) for more details.
If you are using other providers, you will likely need to make a `creds.json` file with api tokens and other account information. For example, to use both name.com and Cloudflare, you would have:
Modify this file to match your particular providers and domains. See [the dnsconfig docs]({{site.github.url}}/js) and [the provider docs]({{site.github.url}}/provider-list) for more details.
```js
Create a file called `creds.json` for storing provider configurations (API tokens and other account information).
For example, to use both name.com and Cloudflare, you would have:
```json
{
"cloudflare":{ // provider name to be used in dnsconfig.js
"apitoken": "token" // API token
"cloudflare": { // The provider name used in dnsconfig.js
"TYPE": "CLOUDFLAREAPI", // The provider type identifier
"accountid": "your-cloudflare-account-id", // credentials
"apitoken": "your-cloudflare-api-token" // credentials
},
"namecom":{ // provider name to be used in dnsconfig.js
"apikey": "key", // API Key
"apiuser": "username" // username for name.com
}
"namecom": { // The provider name used in dnsconfig.js
"TYPE": "NAMEDOTCOM", // The provider type identifier
"apikey": "key", // credentials
"apiuser": "username" // credentials
},
"none": { "TYPE": "NONE" } // The no-op provider
}
```
Note: Do **not** store your creds.json file in Git unencrypted.
That is unsafe. Add `creds.json` to your
`.gitignore` file as a precaution. This file should be encrypted
using something
like [git-crypt](https://www.agwa.name/projects/git-crypt) or
[Blackbox](https://github.com/StackExchange/blackbox).
There are 2 types of providers:
A "Registrar" is who you register the domain with. Start with
`REG_NONE`, which is a provider that never talks to or updates the
`NONE`, which is a provider that never talks to or updates the
registrar. You can define your registrar later when you want to
use advanced features.
The `DnsProvider` is the service that actually provides DNS service
(port 53) and may be the same or different company. Even if both
A "DnsProvider" is the service that actually provides DNS service
(port 53) and may be the same or different as the registrar. Even if both
your Registrar and DnsProvider are the same company, two different
definitions must be included in `dnsconfig.js`.
@ -128,15 +134,17 @@ The file looks like:
```js
{
"bind": {
"TYPE": "BIND"
},
"r53_ACCOUNTNAME": {
"r53_accountname": {
"TYPE": "ROUTE53",
"KeyId": "change_to_your_keyid",
"SecretKey": "change_to_your_secretkey"
}
}
```
Ignore the `r53_ACCOUNTNAME` section. It is a placeholder and will be ignored. You
Ignore the `r53_accountname` section. It is a placeholder and will be ignored. You
can use it later when you define your first set of API credentials.
Note that `creds.json` is a JSON file. JSON is very strict about commas
@ -148,7 +156,7 @@ Python:
jq:
jq < creds.json
jq . < creds.json
FYI: `creds.json` fields can be read from an environment variable. The field must begin with a `$` followed by the variable name. No other text. For example:

View file

@ -20,20 +20,20 @@ All the examples use the variables. Substitute your own.
// ========== Registrars:
// A typical registrar.
var REG_NAMECOM = NewRegistrar("namedotcom_main", "NAMEDOTCOM");
var REG_NAMECOM = NewRegistrar("namedotcom_main");
// The "NONE" registrar is a "fake" registrar.
// This is useful if the registrar is not supported by DNSControl,
// or if you don't want to control the domain's delegation.
var REG_THIRDPARTY = NewRegistrar("ThirdParty", "NONE");
var REG_THIRDPARTY = NewRegistrar("ThirdParty");
// ========== DNS Providers:
var DNS_NAMECOM = NewDnsProvider("namedotcom_main", "NAMEDOTCOM");
var DNS_AWS = NewDnsProvider("aws_main", "ROUTE53");
var DNS_GOOGLE = NewDnsProvider("gcp_main", "GCLOUD");
var DNS_CLOUDFLARE = NewDnsProvider("cloudflare_main", "CLOUDFLAREAPI");
var DNS_BIND = NewDnsProvider("bind", "BIND");
var DNS_NAMECOM = NewDnsProvider("namedotcom_main");
var DNS_AWS = NewDnsProvider("aws_main");
var DNS_GOOGLE = NewDnsProvider("gcp_main");
var DNS_CLOUDFLARE = NewDnsProvider("cloudflare_main");
var DNS_BIND = NewDnsProvider("bind");
```
# Typical Delegations
@ -226,7 +226,7 @@ Sometimes you just want to know if something changes!
See the <a href="{{site.github.url}}/providers/doh">DNS-over-HTTPS Provider</a> documentation for more info.
```js
var REG_MONITOR = NewRegistrar('DNS-over-HTTPS', 'DNSOVERHTTPS');
var REG_MONITOR = NewRegistrar('DNS-over-HTTPS');
D("example1.com", REG_MONITOR,
NAMESERVER("ns1.example1.com."),

220
docs/v316.md Normal file
View file

@ -0,0 +1,220 @@
---
layout: default
title: Converting to v3.16
---
# creds.json file format change
**Feel free to skip to "How do I convert?" if you don't care about the details.**
Starting in v3.16 the "provider type identifier" (PTI) will be located in
`creds.json` instead of `dnsconfig.js`. The PTI is the all caps string like
`ROUTE53` or `CLOUDFLAREAPI` used to identify a provider's type.
V3.16 will enable a syntax that is backwards and forwards compatible. The old
syntax will be removed in v4.0. There's no planned release date for v4.0 but
it is expected to be after Dec 31, 2022.
The change was discussed
in [Request for Comments: Include the provider type in creds.json, remove it from dnsconfig.js](https://github.com/StackExchange/dnscontrol/issues/1457) where we decided "Plan A" would be selected.
# What does this mean to you?
In a nutshell, `NewRegistrar()` and `NewDnsProvider()` will lose the 2nd
parameter:
OLD dnsconfig.js:
```js
var REG_GANDI = NewRegistrar("gandi", "GANDI_V5");
var DSP_CF = NewDnsProvider("cloudflare_tal", "CLOUDFLAREAPI");
```
NEW dnsconfig.js:
```js
var REG_GANDI = NewRegistrar("gandi");
var DSP_CF = NewDnsProvider("cloudflare_tal");
```
The second paramter (`GANDI_V5` and `CLOUDFLAREAPI` in the
above examples) has moved to `creds.json` instead. It will be in a `TYPE`
field, which all providers have. It can appear in both places for backwards compatibility for now.
NEW creds.json:
```json
{
"gandi": {
"TYPE": "GANDI_V5", << NEW
"apikey": "reacted"
},
"cloudflare_tal": {
"TYPE": "CLOUDFLAREAPI", << NEW
"apikey": "reacted",
"apiuser": "reacted"
}
}
```
In the past, a provider didn't need an entry in `creds.json` if there were no credentials to be stored. Starting in v4.0 all providers must have an entry in `creds.json`. To aid the transition, starting in v3.16 warnings will appear on stdout that direct you to convert to the new format.
Also to help in the conversion, if no provider named "none" or "bind" exist, ones will be added for you. They look like this:
```json
{
"none": { "TYPE": "NONE" },
"bind": { "TYPE": "BIND" }
}
```
# Command line tools
How does this affect command line tools?
The following subcommands require the PTI a parameter on the command line:
* `get-zone`
* `get-zones`
* `check-creds`
In 3.16, that parameter can be changed to `-` as a placeholder, or removed
entirely if it is the last parameter on the command line. When you omit this
parameter, DNSControl will look find the value in `creds.json` instead.
In 4.0, that parameter will be removed, though a `-` is permitted for backwards compatibility.
In other words, if you add the `TYPE` field to `creds.json`, you no longer need
to specify it on the command line. You can specify `-` instead, or leave it
out entirely starting in v4.0.
For check-creds:
```
Starting in v3.16 these forms are valid:
dnscontrol check-creds myr53
dnscontrol check-creds myr53 -
dnscontrol check-creds myr53 ROUTE53
Starting in v4.0 this is the only valid form:
dnscontrol check-creds myr53
# For backwards compatibility, these are valid until at least v5.0:
dnscontrol check-creds myr53 -
```
For get-zones/get-zone:
```
Starting in v3.16 these forms are valid:
dnscontrol get-zones gmain GANDI_V5 example.comn other.com
dnscontrol get-zones gmain - example.comn other.com
dnscontrol get-zones cfmain CLOUDFLAREAPI all
dnscontrol get-zones cfmain - all
dnscontrol get-zones --format=tsv bind BIND example.com
dnscontrol get-zones --format=tsv bind - example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud GCLOUD example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud - example.com
Starting in v4.0 these forms are valid:
dnscontrol get-zones gmain example.comn other.com
dnscontrol get-zones cfmain all
dnscontrol get-zones --format=tsv bind example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud example.com
# For backwards compatibility, these are valid until at least v5.0:
dnscontrol get-zones gmain - example.comn other.com
dnscontrol get-zones cfmain - all
dnscontrol get-zones --format=tsv bind - example.com
dnscontrol get-zones --format=djs --out=draft.js glcoud - example.com
```
# How do I convert?
## Step 1: Upgrade
Upgrade to v3.16 or later. If DNSControl is used in many places, do not
continue until they are all at v3.16 or later.
## Step 2: Edit creds.json
Now that all uses of DNSControl are on v3.16 or later...
For each `creds.json` entry, add a field "TYPE" set to the provider type
identifier. This is the all-caps name such as `ROUTE53`, `GCLOUD`, or
`CLOUDFLAREAPI`.
For example, here is a new-style `creds.json` file with `TYPE` fields added:
```json
{
"bind_inside": {
"TYPE": "BIND", << ADDED
"directory": "inzones"
},
"cloudflare_tal": {
"TYPE": "CLOUDFLAREAPI", << ADDED
"apikey": "redacted",
"apiuser": "redacted"
},
"gandi": {
"TYPE": "GANDI_V5", << ADDED
"apikey": "redacted"
},
```
## Step 3: Cross-check
Run `dnscontrol preview` as one normally would. Fix any errors, warnings, or informational messages that appear on stdout.
Here are some examples:
```
WARNING: For future compatibility, update the "namedotcom_main" entry in `creds.json` by adding: "TYPE": "NAMEDOTCOM", (See https://stackexchange.github.io/dnscontrol/creds-json#missing)
```
```
ERROR: Mismatch found! creds.json entry "namedotcom_main" has "TYPE" set to "ROUTE53" but dnsconfig.js specifies New*("namedotcom_main", "NAMEDOTCOM") (See https://stackexchange.github.io/dnscontrol/creds-json#mismatch)
```
After you correct some warnings, you may receive information messages like:
```
INFO: In dnsconfig.js New*("namedotcom_main", "NAMEDOTCOM") can be simplified to New*("namedotcom_main") (See https://stackexchange.github.io/dnscontrol/creds-json#cleanup)
```
Those messages will disappear as you update `dnsconfig.js` in the next step.
## Step 4: Edit dnsconfig.js
Remove the 2nd parameter to any NewDnsProvider() or NewRegistrar() functions.
OLD:
```js
var REG_NAMEDOTCOM_TAL = NewRegistrar("namedotcom_tal", "NAMEDOTCOM");
var DNS_GANDI_TAL = NewDnsProvider("gandi_v5_tal", "GANDI_V5");
```
NEW:
```js
var REG_NAMEDOTCOM_TAL = NewRegistrar("namedotcom_tal");
var DNS_GANDI_TAL = NewDnsProvider("gandi_v5_tal");
```
Again, run `dnscontrol preview` to verify you setup still works as expected.
## Step 5: Update any shell scripts
Any shell scripts or documentation that uses the subcommands `get-zone`,
`get-zones` or `check-creds` should be updated. The "provider type" parameter
should be changed to `-`. If it is the last parameter on the command, it can
be removed.
It's unlikely you have scripts that use these commands. However you may have
documentation that refers to them and needs to be updated.
## Step 4: Test
Run `dnscontrol preview` as one normally would. Fix any errors, warnings, or informational messages that appear on stdout.
## Step 5: Done!
That's it!

View file

@ -14,7 +14,6 @@ import (
"github.com/DisposaBoy/JsonConfigReader"
"github.com/TomOnTime/utfutil"
"golang.org/x/exp/maps"
)
func quotedList(l []string) string {
@ -76,13 +75,16 @@ func LoadProviderConfigs(fname string) (map[string]map[string]string, error) {
return nil, err
}
ckeys := keysWithColons(maps.Keys(results))
if len(ckeys) != 0 {
fmt.Printf(`WARNING: In the future, colons in cred entry names will have meaning.`+
` Our best advice is to remove the colons for now to avoid future compatibility issues.`+
` Specifically these keys: %v`+"\n",
quotedList(ckeys))
// For backwards compatibility, insert NONE and BIND entries if
// they do not exist. These are the only providers that previously
// did not require entries in creds.json prior to v4.0.
if _, ok := results["none"]; !ok {
results["none"] = map[string]string{"TYPE": "NONE"}
}
if _, ok := results["bind"]; !ok {
results["bind"] = map[string]string{"TYPE": "BIND"}
}
return results, nil
}

View file

@ -1,8 +1,5 @@
'use strict';
// If you edit this file, you must run `go generate` to embed this
// file in the source code.
// If you are heavily debugging this code, the "-dev" flag will
// read this file directly instead of using the output of
// `go generate`. You'll still need to run `go generate` before
@ -36,7 +33,27 @@ function getConfiguredDomains() {
return conf.domain_names;
}
function NewRegistrar(name, type, meta) {
// NewRegistrar returns an registrar object.
// For backwards compatibility, it accepts (name), (name, meta),
// (name, type), (name, type, meta).
function NewRegistrar() {
// For backwards compatibility, this is a wrapper around the legacy
// version of this function.
switch (arguments.length) {
case 1:
return oldNewRegistrar(arguments[0], "-")
case 2:
// x = NewRegistrar("myThing", "THING")
// x = NewRegistrar("myThing", { metakey: metavalue } )
if (typeof arguments[1] === 'object') {
return oldNewRegistrar(arguments[0], "-", arguments[1])
}
break;
default: // do nothing
}
return oldNewRegistrar.apply(null, arguments)
}
function oldNewRegistrar(name, type, meta) {
if (type) {
type == 'MANUAL';
}
@ -46,6 +63,23 @@ function NewRegistrar(name, type, meta) {
}
function NewDnsProvider(name, type, meta) {
// For backwards compatibility, this is a wrapper around the legacy
// version of this function.
switch (arguments.length) {
case 1:
return oldNewDnsProvider(arguments[0], "-")
case 2:
// x = NewDnsProvider("myThing", "THING")
// x = NewDnsProvider("myThing", { metakey: metavalue } )
if (typeof arguments[1] === 'object') {
return oldNewDnsProvider(arguments[0], "-", arguments[1])
}
break;
default: // do nothing
}
return oldNewDnsProvider.apply(null, arguments)
}
function oldNewDnsProvider(name, type, meta) {
if (typeof meta === 'object' && 'ip_conversions' in meta) {
meta.ip_conversions = format_tt(meta.ip_conversions);
}

View file

@ -54,6 +54,13 @@ func TestParsedFiles(t *testing.T) {
// Initialize any DNS providers mentioned.
for _, dProv := range conf.DNSProviders {
var pcfg = map[string]string{}
if dProv.Type == "-" {
// Pretend any "look up provider type in creds.json" results
// in a provider type that actually exists.
dProv.Type = "CLOUDFLAREAPI"
}
// Fake out any provider's validation tests.
switch dProv.Type {
case "CLOUDFLAREAPI":

View file

@ -0,0 +1,31 @@
// Test old-style and new-style New*() functions.
var REG1 = NewRegistrar("foo1");
var CF1 = NewDnsProvider("dns1");
var REG2a = NewRegistrar("foo2a", "NONE");
var CF2a = NewDnsProvider("dns2a", "CLOUDFLAREAPI");
var REG2b = NewRegistrar("foo2b", {
regmetakey: "reg2b"
});
var CF2b = NewDnsProvider("dns2b", {
dnsmetakey: "dns2b"
});
var REG3 = NewRegistrar("foo3", "MANUAL", {
regmetakey: "reg3"
});
var CF3 = NewDnsProvider("dns3", "CLOUDFLAREAPI", {
dnsmetakey: "dns3"
});
var REG1h = NewRegistrar("foo1h", "-");
var CF1h = NewDnsProvider("dns1h", "-");
var REG2bh = NewRegistrar("foo2bh", "-", {
regmetakey: "reg2bh"
});
var CF2bh = NewDnsProvider("dns2bh", "-", {
dnsmetakey: "dns2bh"
});

View file

@ -0,0 +1,19 @@
{
"dns_providers": [
{ "name": "dns1", "type": "CLOUDFLAREAPI" },
{ "name": "dns2a", "type": "CLOUDFLAREAPI" },
{ "name": "dns2b", "type": "CLOUDFLAREAPI", "meta": { "dnsmetakey": "dns2b" } },
{ "name": "dns3", "type": "CLOUDFLAREAPI", "meta": { "dnsmetakey": "dns3" } },
{ "name": "dns1h", "type": "CLOUDFLAREAPI" },
{ "name": "dns2bh", "type": "CLOUDFLAREAPI", "meta": { "dnsmetakey": "dns2bh" } }
],
"domains": [],
"registrars": [
{ "name": "foo1", "type": "-" },
{ "name": "foo2a", "type": "NONE" },
{ "name": "foo2b", "type": "-", "meta": { "regmetakey": "reg2b" } },
{ "name": "foo3", "type": "MANUAL", "meta": { "regmetakey": "reg3" } },
{ "name": "foo1h", "type": "-" },
{ "name": "foo2bh", "type": "-", "meta": { "regmetakey": "reg2bh" } }
]
}

View file

@ -464,7 +464,7 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
// At this point we've munged anything that needs to be munged, and
// validated anything that can be globally validated.
// Let's ask // the provider if there are any records they can't handle.
// Let's ask the provider if there are any records they can't handle.
for _, domain := range config.Domains { // For each domain..
for _, provider := range domain.DNSProviderInstances { // For each provider...
if err := providers.AuditRecords(provider.ProviderBase.ProviderType, domain.Records); err != nil {

View file

@ -57,7 +57,7 @@ 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 %s multiple times", name)
log.Fatalf("Cannot register registrar type %q multiple times", name)
}
RegistrarTypes[name] = init
unwrapProviderCapabilities(name, pm)
@ -66,7 +66,7 @@ func RegisterRegistrarType(name string, init RegistrarInitializer, pm ...Provide
// 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 %s multiple times", name)
log.Fatalf("Cannot register registrar type %q multiple times", name)
}
DNSProviderTypes[name] = fns
unwrapProviderCapabilities(name, pm)
@ -74,30 +74,71 @@ func RegisterDomainServiceProviderType(name string, fns DspFuncs, pm ...Provider
// 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("registrar type %s not declared", rType)
return nil, fmt.Errorf("No such registrar type: %q", rType)
}
return initer(config)
}
// CreateDNSProvider initializes a dns provider instance from given credentials.
func CreateDNSProvider(dType string, config map[string]string, meta json.RawMessage) (DNSServiceProvider, error) {
p, ok := DNSProviderTypes[dType]
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("DSP type %s not declared", dType)
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 fmt.Errorf("DSP type %s not declared", dType)
return fmt.Errorf("Unknown DNS service provider type: %q", dType)
}
if p.RecordAuditor == nil {
return fmt.Errorf("DSP type %s has no RecordAuditor", dType)
return fmt.Errorf("DNS service provider type %q has no RecordAuditor", dType)
}
return p.RecordAuditor(rcs)
}