2017-09-13 22:00:41 +08:00
package commands
import (
2023-09-15 01:15:54 +08:00
"encoding/json"
2017-09-13 22:00:41 +08:00
"fmt"
"os"
2022-05-09 02:23:45 +08:00
"strings"
2023-03-20 03:30:19 +08:00
"sync"
2017-09-13 22:00:41 +08:00
2023-03-09 14:22:12 +08:00
"golang.org/x/net/idna"
2023-05-21 01:21:45 +08:00
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/bindserial"
"github.com/StackExchange/dnscontrol/v4/pkg/credsfile"
"github.com/StackExchange/dnscontrol/v4/pkg/nameservers"
"github.com/StackExchange/dnscontrol/v4/pkg/normalize"
"github.com/StackExchange/dnscontrol/v4/pkg/notifications"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
2024-04-04 04:01:55 +08:00
"github.com/StackExchange/dnscontrol/v4/pkg/rfc4183"
2023-05-21 01:21:45 +08:00
"github.com/StackExchange/dnscontrol/v4/pkg/zonerecs"
"github.com/StackExchange/dnscontrol/v4/providers"
2022-08-15 08:46:56 +08:00
"github.com/urfave/cli/v2"
2022-06-09 02:53:16 +08:00
"golang.org/x/exp/slices"
2017-09-13 22:00:41 +08:00
)
var _ = cmd ( catMain , func ( ) * cli . Command {
var args PreviewArgs
return & cli . Command {
Name : "preview" ,
Usage : "read live configuration and identify changes to be made, without applying them" ,
Action : func ( ctx * cli . Context ) error {
return exit ( Preview ( args ) )
} ,
Flags : args . flags ( ) ,
}
} ( ) )
// PreviewArgs contains all data/flags needed to run preview, independently of CLI
type PreviewArgs struct {
GetDNSConfigArgs
GetCredentialsArgs
FilterArgs
2019-06-27 13:21:23 +08:00
Notify bool
2019-02-14 09:11:26 +08:00
WarnChanges bool
2022-06-09 02:53:16 +08:00
NoPopulate bool
2022-08-21 08:59:02 +08:00
Full bool
2017-09-13 22:00:41 +08:00
}
2023-12-05 06:45:25 +08:00
// ReportItem is a record of corrections for a particular domain/provider/registrar.
2023-09-15 01:15:54 +08:00
type ReportItem struct {
Domain string ` json:"domain" `
Corrections int ` json:"corrections" `
Provider string ` json:"provider,omitempty" `
Registrar string ` json:"registrar,omitempty" `
}
2017-09-13 22:00:41 +08:00
func ( args * PreviewArgs ) flags ( ) [ ] cli . Flag {
flags := args . GetDNSConfigArgs . flags ( )
flags = append ( flags , args . GetCredentialsArgs . flags ( ) ... )
flags = append ( flags , args . FilterArgs . flags ( ) ... )
2020-02-04 01:44:11 +08:00
flags = append ( flags , & cli . BoolFlag {
2018-01-12 00:15:19 +08:00
Name : "notify" ,
Destination : & args . Notify ,
Usage : ` set to true to send notifications to configured destinations ` ,
} )
2020-02-04 01:44:11 +08:00
flags = append ( flags , & cli . BoolFlag {
2019-02-14 09:11:26 +08:00
Name : "expect-no-changes" ,
Destination : & args . WarnChanges ,
Usage : ` set to true for non-zero return code if there are changes ` ,
} )
2022-06-09 02:53:16 +08:00
flags = append ( flags , & cli . BoolFlag {
Name : "no-populate" ,
Destination : & args . NoPopulate ,
Usage : ` Use this flag to not auto-create non-existing zones at the provider ` ,
} )
2022-08-21 08:59:02 +08:00
flags = append ( flags , & cli . BoolFlag {
Name : "full" ,
Destination : & args . Full ,
Usage : ` Add headings, providers names, notifications of no changes, etc ` ,
} )
2023-12-14 01:32:39 +08:00
flags = append ( flags , & cli . IntFlag {
Name : "reportmax" ,
Hidden : true ,
Usage : ` Limit the IGNORE/NO_PURGE report to this many lines (Expermental. Will change in the future.) ` ,
Action : func ( ctx * cli . Context , max int ) error {
printer . MaxReport = max
return nil
} ,
} )
2023-04-26 07:00:11 +08:00
flags = append ( flags , & cli . Int64Flag {
Name : "bindserial" ,
Destination : & bindserial . ForcedValue ,
Usage : ` Force BIND serial numbers to this value (for reproducibility) ` ,
} )
2017-09-13 22:00:41 +08:00
return flags
}
var _ = cmd ( catMain , func ( ) * cli . Command {
var args PushArgs
return & cli . Command {
Name : "push" ,
Usage : "identify changes to be made, and perform them" ,
Action : func ( ctx * cli . Context ) error {
return exit ( Push ( args ) )
} ,
Flags : args . flags ( ) ,
}
} ( ) )
2018-01-10 01:53:16 +08:00
// PushArgs contains all data/flags needed to run push, independently of CLI
2017-09-13 22:00:41 +08:00
type PushArgs struct {
PreviewArgs
Interactive bool
2023-09-15 01:15:54 +08:00
Report string
2017-09-13 22:00:41 +08:00
}
func ( args * PushArgs ) flags ( ) [ ] cli . Flag {
flags := args . PreviewArgs . flags ( )
2020-02-04 01:44:11 +08:00
flags = append ( flags , & cli . BoolFlag {
2017-09-13 22:00:41 +08:00
Name : "i" ,
Destination : & args . Interactive ,
Usage : "Interactive. Confirm or Exclude each correction before they run" ,
} )
2023-09-15 01:15:54 +08:00
flags = append ( flags , & cli . StringFlag {
Name : "report" ,
Destination : & args . Report ,
Usage : ` Generate a machine-parseable report of performed corrections. ` ,
} )
2017-09-13 22:00:41 +08:00
return flags
}
2018-01-10 01:53:16 +08:00
// Preview implements the preview subcommand.
2017-09-13 22:00:41 +08:00
func Preview ( args PreviewArgs ) error {
2023-09-15 01:15:54 +08:00
return run ( args , false , false , printer . DefaultPrinter , nil )
2017-09-13 22:00:41 +08:00
}
2018-01-10 01:53:16 +08:00
// Push implements the push subcommand.
2017-09-13 22:00:41 +08:00
func Push ( args PushArgs ) error {
2023-09-15 01:15:54 +08:00
return run ( args . PreviewArgs , true , args . Interactive , printer . DefaultPrinter , & args . Report )
2017-09-13 22:00:41 +08:00
}
2023-10-23 01:56:13 +08:00
var obsoleteDiff2FlagUsed = false
2017-09-13 22:00:41 +08:00
// run is the main routine common to preview/push
2023-09-15 01:15:54 +08:00
func run ( args PreviewArgs , push bool , interactive bool , out printer . CLI , report * string ) error {
2017-09-13 22:00:41 +08:00
// TODO: make truly CLI independent. Perhaps return results on a channel as they occur
2022-08-21 08:59:02 +08:00
// This is a hack until we have the new printer replacement.
printer . SkinnyReport = ! args . Full
2023-10-23 01:56:13 +08:00
if obsoleteDiff2FlagUsed {
printer . Println ( "WARNING: Please remove obsolete --diff2 flag. This will be an error in v5 or later. See https://github.com/StackExchange/dnscontrol/issues/2262" )
2023-04-17 02:46:39 +08:00
}
2017-09-13 22:00:41 +08:00
cfg , err := GetDNSConfig ( args . GetDNSConfigArgs )
if err != nil {
return err
}
2022-05-04 23:02:36 +08:00
providerConfigs , err := credsfile . LoadProviderConfigs ( args . CredsFile )
if err != nil {
return err
}
notifier , err := InitializeProviders ( cfg , providerConfigs , args . Notify )
2017-09-13 22:00:41 +08:00
if err != nil {
return err
}
2022-05-09 02:23:45 +08:00
errs := normalize . ValidateAndNormalizeConfig ( cfg )
if PrintValidationErrors ( errs ) {
return fmt . Errorf ( "exiting due to validation errors" )
}
2017-09-13 22:00:41 +08:00
anyErrors := false
totalCorrections := 0
2023-03-01 21:38:13 +08:00
2023-03-20 03:30:19 +08:00
// create a WaitGroup with the length of domains for the anonymous functions (later goroutines) to wait for
var wg sync . WaitGroup
wg . Add ( len ( cfg . Domains ) )
2023-09-15 01:15:54 +08:00
var reportItems [ ] ReportItem
2023-04-15 03:22:23 +08:00
// For each domain in dnsconfig.js...
2023-03-20 03:30:19 +08:00
for _ , domain := range cfg . Domains {
// Run preview or push operations per domain as anonymous function, in preparation for the later use of goroutines.
// For now running this code is still sequential.
// Please note that at the end of this anonymous function there is a } (domain) which executes this function actually
func ( domain * models . DomainConfig ) {
defer wg . Done ( ) // defer notify WaitGroup this anonymous function has finished
2023-05-03 01:04:59 +08:00
uniquename := domain . GetUniqueName ( )
if ! args . shouldRunDomain ( uniquename ) {
2023-03-20 03:30:19 +08:00
return
}
2023-04-15 03:22:23 +08:00
err = domain . Punycode ( )
if err != nil {
return
}
// Correct the domain...
2023-05-03 01:04:59 +08:00
out . StartDomain ( uniquename )
2023-03-20 03:30:19 +08:00
var providersWithExistingZone [ ] * models . DNSProviderInstance
2023-04-15 03:22:23 +08:00
/// For each DSP...
2023-03-20 03:30:19 +08:00
for _ , provider := range domain . DNSProviderInstances {
if ! args . NoPopulate {
// preview run: check if zone is already there, if not print a warning
if lister , ok := provider . Driver . ( providers . ZoneLister ) ; ok && ! push {
zones , err := lister . ListZones ( )
if err != nil {
2023-08-09 00:02:49 +08:00
out . Errorf ( "ERROR: %s\n" , err . Error ( ) )
2023-03-20 03:30:19 +08:00
return
}
aceZoneName , _ := idna . ToASCII ( domain . Name )
if ! slices . Contains ( zones , aceZoneName ) {
2023-04-15 03:22:23 +08:00
//out.Warnf("DEBUG: zones: %v\n", zones)
//out.Warnf("DEBUG: Name: %v\n", domain.Name)
2023-03-20 03:30:19 +08:00
out . Warnf ( "Zone '%s' does not exist in the '%s' profile and will be added automatically.\n" , domain . Name , provider . Name )
continue // continue with next provider, as we can not determine corrections without an existing zone
}
} else if creator , ok := provider . Driver . ( providers . ZoneCreator ) ; ok && push {
// this is the actual push, ensure domain exists at DSP
if err := creator . EnsureZoneExists ( domain . Name ) ; err != nil {
out . Warnf ( "Error creating domain: %s\n" , err )
2023-12-10 23:29:09 +08:00
anyErrors = true
2023-03-20 03:30:19 +08:00
continue // continue with next provider, as we couldn't create this one
}
2022-06-09 02:53:16 +08:00
}
}
2023-03-20 03:30:19 +08:00
providersWithExistingZone = append ( providersWithExistingZone , provider )
2022-06-09 02:53:16 +08:00
}
2023-04-15 03:22:23 +08:00
// Correct the registrar...
2024-03-28 01:54:36 +08:00
nsList , err := nameservers . DetermineNameserversForProviders ( domain , providersWithExistingZone , false )
2017-09-13 22:00:41 +08:00
if err != nil {
2023-08-09 00:02:49 +08:00
out . Errorf ( "ERROR: %s\n" , err . Error ( ) )
2023-03-20 03:30:19 +08:00
return
2017-09-13 22:00:41 +08:00
}
2023-03-20 03:30:19 +08:00
domain . Nameservers = nsList
nameservers . AddNSRecords ( domain )
for _ , provider := range providersWithExistingZone {
2023-04-15 03:22:23 +08:00
shouldrun := args . shouldRunProvider ( provider . Name , domain )
2023-03-20 03:30:19 +08:00
out . StartDNSProvider ( provider . Name , ! shouldrun )
if ! shouldrun {
continue
}
2021-03-08 02:19:22 +08:00
2024-09-17 00:20:30 +08:00
reports , corrections , actualChangeCount , err := zonerecs . CorrectZoneRecords ( provider . Driver , domain )
out . EndProvider ( provider . Name , actualChangeCount , err )
2023-03-20 03:30:19 +08:00
if err != nil {
anyErrors = true
return
}
2024-09-17 00:20:30 +08:00
totalCorrections += actualChangeCount
2023-10-23 01:56:13 +08:00
printReports ( domain . Name , provider . Name , reports , out , push , notifier )
2023-09-15 01:15:54 +08:00
reportItems = append ( reportItems , ReportItem {
Domain : domain . Name ,
2024-09-17 00:20:30 +08:00
Corrections : actualChangeCount ,
2023-09-15 01:15:54 +08:00
Provider : provider . Name ,
} )
2023-03-20 03:30:19 +08:00
anyErrors = printOrRunCorrections ( domain . Name , provider . Name , corrections , out , push , interactive , notifier ) || anyErrors
}
2023-04-15 03:22:23 +08:00
//
2023-03-20 03:30:19 +08:00
run := args . shouldRunProvider ( domain . RegistrarName , domain )
out . StartRegistrar ( domain . RegistrarName , ! run )
if ! run {
return
}
if len ( domain . Nameservers ) == 0 && domain . Metadata [ "no_ns" ] != "true" {
out . Warnf ( "No nameservers declared; skipping registrar. Add {no_ns:'true'} to force.\n" )
return
}
2023-04-15 03:22:23 +08:00
corrections , err := domain . RegistrarInstance . Driver . GetRegistrarCorrections ( domain )
2023-03-20 03:30:19 +08:00
out . EndProvider ( domain . RegistrarName , len ( corrections ) , err )
2017-09-13 22:00:41 +08:00
if err != nil {
anyErrors = true
2023-03-20 03:30:19 +08:00
return
2017-09-13 22:00:41 +08:00
}
totalCorrections += len ( corrections )
2023-09-15 01:15:54 +08:00
reportItems = append ( reportItems , ReportItem {
Domain : domain . Name ,
Corrections : len ( corrections ) ,
Registrar : domain . RegistrarName ,
} )
2023-03-20 03:30:19 +08:00
anyErrors = printOrRunCorrections ( domain . Name , domain . RegistrarName , corrections , out , push , interactive , notifier ) || anyErrors
} ( domain )
2017-09-13 22:00:41 +08:00
}
2023-03-20 03:30:19 +08:00
wg . Wait ( ) // wait for all anonymous functions to finish
2017-09-13 22:00:41 +08:00
if os . Getenv ( "TEAMCITY_VERSION" ) != "" {
fmt . Fprintf ( os . Stderr , "##teamcity[buildStatus status='SUCCESS' text='%d corrections']" , totalCorrections )
}
2024-04-04 04:01:55 +08:00
rfc4183 . PrintWarning ( )
2018-01-12 00:15:19 +08:00
notifier . Done ( )
2018-10-09 04:10:44 +08:00
out . Printf ( "Done. %d corrections.\n" , totalCorrections )
2017-09-13 22:00:41 +08:00
if anyErrors {
2020-08-31 07:52:37 +08:00
return fmt . Errorf ( "completed with errors" )
2017-09-13 22:00:41 +08:00
}
2019-02-14 09:11:26 +08:00
if totalCorrections != 0 && args . WarnChanges {
2020-08-31 07:52:37 +08:00
return fmt . Errorf ( "there are pending changes" )
2019-02-14 09:11:26 +08:00
}
2023-09-17 03:52:59 +08:00
if report != nil && * report != "" {
2023-09-15 01:15:54 +08:00
f , err := os . OpenFile ( * report , os . O_CREATE | os . O_WRONLY | os . O_TRUNC , 0644 )
if err != nil {
return err
}
defer f . Close ( )
b , err := json . MarshalIndent ( reportItems , "" , " " )
if err != nil {
return err
}
if _ , err := f . Write ( b ) ; err != nil {
return err
}
}
2017-09-13 22:00:41 +08:00
return nil
}
2022-05-04 23:02:36 +08:00
// InitializeProviders takes (fully processed) configuration and instantiates all providers and returns them.
func InitializeProviders ( cfg * models . DNSConfig , providerConfigs map [ string ] map [ string ] string , notifyFlag bool ) ( notify notifications . Notifier , err error ) {
2018-01-12 00:15:19 +08:00
var notificationCfg map [ string ] string
defer func ( ) {
notify = notifications . Init ( notificationCfg )
} ( )
if notifyFlag {
notificationCfg = providerConfigs [ "notifications" ]
}
2018-02-02 00:45:53 +08:00
isNonDefault := map [ string ] bool { }
2017-09-13 22:00:41 +08:00
for name , vals := range providerConfigs {
// add "_exclude_from_defaults":"true" to a provider to exclude it from being run unless
// -providers=all or -providers=name
if vals [ "_exclude_from_defaults" ] == "true" {
2018-02-02 00:45:53 +08:00
isNonDefault [ name ] = true
2017-09-13 22:00:41 +08:00
}
}
2022-05-09 02:23:45 +08:00
// 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
}
2018-02-02 00:45:53 +08:00
registrars := map [ string ] providers . Registrar { }
dnsProviders := map [ string ] providers . DNSServiceProvider { }
for _ , d := range cfg . Domains {
if registrars [ d . RegistrarName ] == nil {
rCfg := cfg . RegistrarsByName [ d . RegistrarName ]
r , err := providers . CreateRegistrar ( rCfg . Type , providerConfigs [ d . RegistrarName ] )
if err != nil {
return nil , err
}
registrars [ d . RegistrarName ] = r
}
d . RegistrarInstance . Driver = registrars [ d . RegistrarName ]
d . RegistrarInstance . IsDefault = ! isNonDefault [ d . RegistrarName ]
for _ , pInst := range d . DNSProviderInstances {
if dnsProviders [ pInst . Name ] == nil {
dCfg := cfg . DNSProvidersByName [ pInst . Name ]
prov , err := providers . CreateDNSProvider ( dCfg . Type , providerConfigs [ dCfg . Name ] , dCfg . Metadata )
if err != nil {
return nil , err
}
dnsProviders [ pInst . Name ] = prov
}
pInst . Driver = dnsProviders [ pInst . Name ]
pInst . IsDefault = ! isNonDefault [ pInst . Name ]
}
2017-09-13 22:00:41 +08:00
}
return
}
2022-05-09 02:23:45 +08:00
// 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.
2023-01-30 02:14:22 +08:00
const url = "https://docs.dnscontrol.org/commands/creds-json"
2022-05-09 02:23:45 +08:00
// 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
}
}
2018-01-12 00:15:19 +08:00
func printOrRunCorrections ( domain string , provider string , corrections [ ] * models . Correction , out printer . CLI , push bool , interactive bool , notifier notifications . Notifier ) ( anyErrors bool ) {
2017-09-13 22:00:41 +08:00
anyErrors = false
if len ( corrections ) == 0 {
return false
}
for i , correction := range corrections {
out . PrintCorrection ( i , correction )
2018-01-12 00:15:19 +08:00
var err error
2017-09-13 22:00:41 +08:00
if push {
if interactive && ! out . PromptToRun ( ) {
continue
}
2023-03-09 14:22:12 +08:00
if correction . F != nil {
err = correction . F ( )
out . EndCorrection ( err )
if err != nil {
anyErrors = true
}
2017-09-13 22:00:41 +08:00
}
}
2018-01-12 00:15:19 +08:00
notifier . Notify ( domain , provider , correction . Msg , err , ! push )
2017-09-13 22:00:41 +08:00
}
return anyErrors
}
2023-05-16 22:42:08 +08:00
func printReports ( domain string , provider string , reports [ ] * models . Correction , out printer . CLI , push bool , notifier notifications . Notifier ) ( anyErrors bool ) {
anyErrors = false
if len ( reports ) == 0 {
return false
}
for i , report := range reports {
out . PrintReport ( i , report )
notifier . Notify ( domain , provider , report . Msg , nil , ! push )
}
return anyErrors
}