2017-09-13 22:00:41 +08:00
package commands
import (
"encoding/json"
"fmt"
"os"
"sort"
"strings"
2020-02-04 01:44:11 +08:00
"github.com/urfave/cli/v2"
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
2020-04-15 04:47:30 +08:00
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
2017-09-13 22:00:41 +08:00
)
// categories of commands
const (
catMain = "\b main" // screwed up to alphebatize first
catDebug = "debug"
catUtils = "utility"
)
2020-02-04 01:44:11 +08:00
var commands = [ ] * cli . Command { }
2017-09-13 22:00:41 +08:00
var version string
func cmd ( cat string , c * cli . Command ) bool {
c . Category = cat
2020-02-04 01:44:11 +08:00
commands = append ( commands , c )
2017-09-13 22:00:41 +08:00
return true
}
var _ = cmd ( catDebug , & cli . Command {
Name : "version" ,
Usage : "Print version information" ,
2020-02-04 01:44:11 +08:00
Action : func ( c * cli . Context ) error {
2020-02-22 02:23:30 +08:00
_ , err := fmt . Println ( version )
return err
2017-09-13 22:00:41 +08:00
} ,
} )
// Run will execute the CLI
2017-09-15 21:59:43 +08:00
func Run ( v string ) int {
2017-09-13 22:00:41 +08:00
version = v
app := cli . NewApp ( )
app . Version = version
app . Name = "dnscontrol"
app . HideVersion = true
app . Usage = "dnscontrol is a compiler and DSL for managing dns zones"
2018-10-09 04:10:44 +08:00
app . Flags = [ ] cli . Flag {
2020-02-04 01:44:11 +08:00
& cli . BoolFlag {
2018-10-09 04:10:44 +08:00
Name : "v" ,
Usage : "Enable detailed logging" ,
Destination : & printer . DefaultPrinter . Verbose ,
} ,
}
2017-09-13 22:00:41 +08:00
sort . Sort ( cli . CommandsByName ( commands ) )
app . Commands = commands
app . EnableBashCompletion = true
2017-09-15 21:59:43 +08:00
if err := app . Run ( os . Args ) ; err != nil {
return 1
}
return 0
2017-09-13 22:00:41 +08:00
}
// Shared config types
// GetDNSConfigArgs contains what we need to get a valid dns config.
// Could come from parsing js, or from stored json
type GetDNSConfigArgs struct {
ExecuteDSLArgs
JSONFile string
}
func ( args * GetDNSConfigArgs ) flags ( ) [ ] cli . Flag {
return append ( args . ExecuteDSLArgs . flags ( ) ,
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Destination : & args . JSONFile ,
Name : "ir" ,
Usage : "Read IR (json) directly from this file. Do not process DSL at all" ,
} ,
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Destination : & args . JSONFile ,
Name : "json" ,
Hidden : true ,
Usage : "same as -ir. only here for backwards compatibility, hence hidden" ,
} ,
)
}
2018-02-02 00:45:53 +08:00
// GetDNSConfig reads the json-formatted IR file. Or executes javascript. All depending on flags provided.
2017-09-13 22:00:41 +08:00
func GetDNSConfig ( args GetDNSConfigArgs ) ( * models . DNSConfig , error ) {
if args . JSONFile != "" {
f , err := os . Open ( args . JSONFile )
if err != nil {
return nil , err
}
defer f . Close ( )
dec := json . NewDecoder ( f )
cfg := & models . DNSConfig { }
if err = dec . Decode ( cfg ) ; err != nil {
return nil , err
}
2018-02-02 00:45:53 +08:00
return preloadProviders ( cfg , nil )
2017-09-13 22:00:41 +08:00
}
2018-02-02 00:45:53 +08:00
return preloadProviders ( ExecuteDSL ( args . ExecuteDSLArgs ) )
}
// the json only contains provider names inside domains. This denormalizes the data for more
// convenient access patterns. Does everything we need to prepare for the validation phase, but
// cannot do anything that requires the credentials file yet.
func preloadProviders ( cfg * models . DNSConfig , err error ) ( * models . DNSConfig , error ) {
if err != nil {
return cfg , err
}
//build name to type maps
cfg . RegistrarsByName = map [ string ] * models . RegistrarConfig { }
cfg . DNSProvidersByName = map [ string ] * models . DNSProviderConfig { }
for _ , reg := range cfg . Registrars {
cfg . RegistrarsByName [ reg . Name ] = reg
}
for _ , p := range cfg . DNSProviders {
cfg . DNSProvidersByName [ p . Name ] = p
}
2019-05-21 03:39:19 +08:00
// make registrar and dns provider shims. Include name, type, and other metadata, but can't instantiate
2018-02-02 00:45:53 +08:00
// driver until we load creds in later
for _ , d := range cfg . Domains {
reg , ok := cfg . RegistrarsByName [ d . RegistrarName ]
if ! ok {
2020-08-31 07:52:37 +08:00
return nil , fmt . Errorf ( "registrar named %s expected for %s, but never registered" , d . RegistrarName , d . Name )
2018-02-02 00:45:53 +08:00
}
d . RegistrarInstance = & models . RegistrarInstance {
ProviderBase : models . ProviderBase {
Name : reg . Name ,
ProviderType : reg . Type ,
} ,
}
for pName , n := range d . DNSProviderNames {
prov , ok := cfg . DNSProvidersByName [ pName ]
if ! ok {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
return nil , fmt . Errorf ( "DNS Provider named %s expected for %s, but never registered" , pName , d . Name )
2018-02-02 00:45:53 +08:00
}
d . DNSProviderInstances = append ( d . DNSProviderInstances , & models . DNSProviderInstance {
ProviderBase : models . ProviderBase {
Name : pName ,
ProviderType : prov . Type ,
} ,
NumberOfNameservers : n ,
} )
}
// sort so everything is deterministic
sort . Slice ( d . DNSProviderInstances , func ( i , j int ) bool {
return d . DNSProviderInstances [ i ] . Name < d . DNSProviderInstances [ j ] . Name
} )
}
return cfg , nil
2017-09-13 22:00:41 +08:00
}
// ExecuteDSLArgs are used anytime we need to read and execute dnscontrol DSL
type ExecuteDSLArgs struct {
JSFile string
JSONFile string
DevMode bool
}
func ( args * ExecuteDSLArgs ) flags ( ) [ ] cli . Flag {
return [ ] cli . Flag {
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Name : "config" ,
Value : "dnsconfig.js" ,
Destination : & args . JSFile ,
Usage : "File containing dns config in javascript DSL" ,
} ,
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Name : "js" ,
Value : "dnsconfig.js" ,
Hidden : true ,
Destination : & args . JSFile ,
Usage : "same as config. for back compatibility" ,
} ,
2020-02-04 01:44:11 +08:00
& cli . BoolFlag {
2017-09-13 22:00:41 +08:00
Name : "dev" ,
Destination : & args . DevMode ,
Usage : "Use helpers.js from disk instead of embedded copy" ,
} ,
}
}
// PrintJSONArgs are used anytime a command may print some json
type PrintJSONArgs struct {
Pretty bool
Output string
}
func ( args * PrintJSONArgs ) flags ( ) [ ] cli . Flag {
return [ ] cli . Flag {
2020-02-04 01:44:11 +08:00
& cli . BoolFlag {
2017-09-13 22:00:41 +08:00
Name : "pretty" ,
Destination : & args . Pretty ,
Usage : "Pretty print IR JSON" ,
} ,
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Name : "out" ,
Destination : & args . Output ,
Usage : "File to write IR JSON to (default stdout)" ,
} ,
}
}
2018-01-10 01:53:16 +08:00
// GetCredentialsArgs encapsulates the flags/args for sub-commands that use the creds.json file.
2017-09-13 22:00:41 +08:00
type GetCredentialsArgs struct {
CredsFile string
}
func ( args * GetCredentialsArgs ) flags ( ) [ ] cli . Flag {
return [ ] cli . Flag {
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Name : "creds" ,
Destination : & args . CredsFile ,
Usage : "Provider credentials JSON file" ,
Value : "creds.json" ,
} ,
}
}
2018-01-10 01:53:16 +08:00
// FilterArgs encapsulates the flags/args for sub-commands that can filter by provider or domain.
2017-09-13 22:00:41 +08:00
type FilterArgs struct {
Providers string
Domains string
}
func ( args * FilterArgs ) flags ( ) [ ] cli . Flag {
return [ ] cli . Flag {
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Name : "providers" ,
Destination : & args . Providers ,
Usage : ` Providers to enable (comma separated list); default is all. Can exclude individual providers from default by adding '"_exclude_from_defaults": "true"' to the credentials file for a provider ` ,
Value : "" ,
} ,
2020-02-04 01:44:11 +08:00
& cli . StringFlag {
2017-09-13 22:00:41 +08:00
Name : "domains" ,
Destination : & args . Domains ,
Usage : ` Comma separated list of domain names to include ` ,
Value : "" ,
} ,
}
}
2018-02-02 00:45:53 +08:00
func ( args * FilterArgs ) shouldRunProvider ( name string , dc * models . DomainConfig ) bool {
2017-09-13 22:00:41 +08:00
if args . Providers == "all" {
return true
}
if args . Providers == "" {
2018-02-02 00:45:53 +08:00
for _ , pri := range dc . DNSProviderInstances {
if pri . Name == name {
return pri . IsDefault
2017-09-13 22:00:41 +08:00
}
}
return true
}
for _ , prov := range strings . Split ( args . Providers , "," ) {
2018-02-02 00:45:53 +08:00
if prov == name {
2017-09-13 22:00:41 +08:00
return true
}
}
return false
}
func ( args * FilterArgs ) shouldRunDomain ( d string ) bool {
if args . Domains == "" {
return true
}
for _ , dom := range strings . Split ( args . Domains , "," ) {
if dom == d {
return true
}
}
return false
}