mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
NEW FEATURE: IGNORE() (diff2 only) (#2388)
Co-authored-by: Jeffrey Cafferata <jeffrey@jcid.nl>
This commit is contained in:
@@ -201,7 +201,7 @@ func ByZone(existing models.Records, dc *models.DomainConfig, compFunc Comparabl
|
||||
// byHelper does 90% of the work for the By*() calls.
|
||||
func byHelper(fn func(cc *CompareConfig) ChangeList, existing models.Records, dc *models.DomainConfig, compFunc ComparableFunc) (ChangeList, error) {
|
||||
|
||||
// Process NO_PURGE/ENSURE_ABSENT and UNMANAGED/IGNORE_*.
|
||||
// Process NO_PURGE/ENSURE_ABSENT and IGNORE*().
|
||||
desired, msgs, err := handsoff(
|
||||
dc.Name,
|
||||
existing, dc.Records, dc.EnsureAbsent,
|
||||
|
@@ -2,7 +2,7 @@ package diff2
|
||||
|
||||
// This file implements the features that tell DNSControl "hands off"
|
||||
// foreign-controlled (or shared-control) DNS records. i.e. the
|
||||
// NO_PURGE, ENSURE_ABSENT, IGNORE_*, and UNMANAGED features.
|
||||
// NO_PURGE, ENSURE_ABSENT and IGNORE*() features.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
/*
|
||||
|
||||
# How do NO_PURGE, IGNORE_*, ENSURE_ABSENT and friends work?
|
||||
# How do NO_PURGE, IGNORE*() and ENSURE_ABSENT work?
|
||||
|
||||
## Terminology:
|
||||
|
||||
@@ -33,19 +33,19 @@ and 1 way to make exceptions.
|
||||
* Existing records (matched on label:rtype) will be modified.
|
||||
* FYI: This means you can't have a label with two A records, one controlled
|
||||
by DNSControl and one controlled by an external system.
|
||||
* UNMANAGED(labelglob, typelist, targetglob):
|
||||
* IGNORE(labelglob, typelist, targetglob):
|
||||
* "If an existing record matches this pattern, don't touch it!""
|
||||
* IGNORE_NAME(foo, bar) is the same as UNMANAGED(foo, bar, "*")
|
||||
* IGNORE_TARGET(foo) is the same as UNMANAGED("*", "*", foo)
|
||||
* IGNORE_NAME(foo, bar) is the same as IGNORE(foo, bar, "*")
|
||||
* IGNORE_TARGET(foo) is the same as IGNORE("*", "*", foo)
|
||||
* FYI: You CAN have a label with two A records, one controlled by
|
||||
DNSControl and one controlled by an external system. DNSControl would
|
||||
need to have an UNMANAGED() statement with a targetglob that matches
|
||||
need to have an IGNORE() statement with a targetglob that matches
|
||||
the external system's target values.
|
||||
* ENSURE_ABSENT: Override NO_PURGE for specific records. i.e. delete them even
|
||||
though NO_PURGE is enabled.
|
||||
* If any of these records are in desired (matched on
|
||||
label:rtype:target), remove them. This takes priority over
|
||||
NO_PURGE/UNMANAGED/IGNORE*.
|
||||
NO_PURGE/IGNORE*().
|
||||
|
||||
## Implementation premise
|
||||
|
||||
@@ -66,12 +66,12 @@ RecordSet at once, you shouldn't NOT update the record.
|
||||
|
||||
Here is how we intend to implement these features:
|
||||
|
||||
UNMANAGED is implemented as:
|
||||
* Take the list of existing records. If any match one of the UNMANAGED glob
|
||||
IGNORE() is implemented as:
|
||||
* Take the list of existing records. If any match one of the IGNORE glob
|
||||
patterns, add it to the "ignored list".
|
||||
* If any item on the "ignored list" is also in "desired" (match on
|
||||
label:rtype), output a warning (defeault) or declare an error (if
|
||||
DISABLE_UNMANAGED_SAFETY_CHECK is true).
|
||||
DISABLE_IGNORE_SAFETY_CHECK is true).
|
||||
* When we're done, add the "ignore list" records to desired.
|
||||
|
||||
NO_PURGE + ENSURE_ABSENT is implemented as:
|
||||
@@ -84,7 +84,7 @@ The actual implementation combines this all into one loop:
|
||||
foreach rec in existing:
|
||||
if rec matches_any_unmanaged_pattern:
|
||||
if rec in desired:
|
||||
if "DISABLE_UNMANAGED_SAFETY_CHECK" is false:
|
||||
if "DISABLE_IGNORE_SAFETY_CHECK" is false:
|
||||
Display a warning.
|
||||
else
|
||||
Return an error.
|
||||
@@ -100,7 +100,7 @@ The actual implementation combines this all into one loop:
|
||||
|
||||
const maxReport = 5
|
||||
|
||||
// handsoff processes the IGNORE_*/UNMANAGED/NO_PURGE/ENSURE_ABSENT features.
|
||||
// handsoff processes the IGNORE*()//NO_PURGE/ENSURE_ABSENT features.
|
||||
func handsoff(
|
||||
domain string,
|
||||
existing, desired, absences models.Records,
|
||||
@@ -116,7 +116,7 @@ func handsoff(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Process UNMANAGE/IGNORE_* and NO_PURGE features:
|
||||
// Process IGNORE*() and NO_PURGE features:
|
||||
ignorable, foreign := processIgnoreAndNoPurge(domain, existing, desired, absences, unmanagedConfigs, noPurge)
|
||||
if len(foreign) != 0 {
|
||||
msgs = append(msgs, fmt.Sprintf("%d records not being deleted because of NO_PURGE:", len(foreign)))
|
||||
@@ -134,9 +134,9 @@ func handsoff(
|
||||
for _, r := range conflicts {
|
||||
msgs = append(msgs, fmt.Sprintf(" %s %s %s", r.GetLabelFQDN(), r.Type, r.GetTargetCombined()))
|
||||
}
|
||||
if unmanagedSafely {
|
||||
if !unmanagedSafely {
|
||||
return nil, nil, fmt.Errorf(strings.Join(msgs, "\n") +
|
||||
"ERROR: Unsafe to continue. Add DISABLE_UNMANAGED_SAFETY_CHECK to D() to override")
|
||||
"\nERROR: Unsafe to continue. Add DISABLE_IGNORE_SAFETY_CHECK to D() to override")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,14 +166,16 @@ func reportSkips(recs models.Records, full bool) []string {
|
||||
return msgs
|
||||
}
|
||||
|
||||
// processIgnoreAndNoPurge processes the IGNORE_*()/UNMANAGED() and NO_PURGE/ENSURE_ABSENT_REC() features.
|
||||
// processIgnoreAndNoPurge processes the IGNORE_*() and NO_PURGE/ENSURE_ABSENT() features.
|
||||
func processIgnoreAndNoPurge(domain string, existing, desired, absences models.Records, unmanagedConfigs []*models.UnmanagedConfig, noPurge bool) (models.Records, models.Records) {
|
||||
var ignorable, foreign models.Records
|
||||
desiredDB := models.NewRecordDBFromRecords(desired, domain)
|
||||
absentDB := models.NewRecordDBFromRecords(absences, domain)
|
||||
compileUnmanagedConfigs(unmanagedConfigs)
|
||||
for _, rec := range existing {
|
||||
if matchAny(unmanagedConfigs, rec) {
|
||||
isMatch := matchAny(unmanagedConfigs, rec)
|
||||
//fmt.Printf("DEBUG: matchAny returned: %v\n", isMatch)
|
||||
if isMatch {
|
||||
ignorable = append(ignorable, rec)
|
||||
} else {
|
||||
if noPurge {
|
||||
@@ -240,6 +242,7 @@ func compileUnmanagedConfigs(configs []*models.UnmanagedConfig) error {
|
||||
|
||||
// matchAny returns true if rec matches any of the uconfigs.
|
||||
func matchAny(uconfigs []*models.UnmanagedConfig, rec *models.RecordConfig) bool {
|
||||
//fmt.Printf("DEBUG: matchAny(%s, %q, %q, %q)\n", models.DebugUnmanagedConfig(uconfigs), rec.NameFQDN, rec.Type, rec.GetTargetField())
|
||||
for _, uc := range uconfigs {
|
||||
if matchLabel(uc.LabelGlob, rec.GetLabel()) &&
|
||||
matchType(uc.RTypeMap, rec.Type) &&
|
||||
|
Reference in New Issue
Block a user