-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add upstream dns validation (#102)
## Issue #84 ## Description - added service for validating that at least a certain number of upstream DNS servers are configured - added unit tests - expanded integration tests to include new rule --------- Signed-off-by: Artur Shad Nik <[email protected]> Co-authored-by: Tyler Gillson <[email protected]>
- Loading branch information
1 parent
ba024df
commit 50b317e
Showing
6 changed files
with
223 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Package utils provides utility functions for the MAAS validator | ||
package utils | ||
|
||
import ( | ||
"fmt" | ||
|
||
vapi "github.com/validator-labs/validator/api/v1alpha1" | ||
vapiconstants "github.com/validator-labs/validator/pkg/constants" | ||
"github.com/validator-labs/validator/pkg/types" | ||
"github.com/validator-labs/validator/pkg/util" | ||
) | ||
|
||
// BuildValidationResult builds a default ValidationResult for a given validation type | ||
func BuildValidationResult(ruleName, ruleType string) *types.ValidationRuleResult { | ||
state := vapi.ValidationSucceeded | ||
latestCondition := vapi.DefaultValidationCondition() | ||
latestCondition.Details = make([]string, 0) | ||
latestCondition.Failures = make([]string, 0) | ||
latestCondition.Message = fmt.Sprintf("All %s checks passed", ruleType) | ||
latestCondition.ValidationRule = fmt.Sprintf("%s-%s", vapiconstants.ValidationRulePrefix, util.Sanitize(ruleName)) | ||
latestCondition.ValidationType = ruleType | ||
return &types.ValidationRuleResult{Condition: &latestCondition, State: &state} | ||
} | ||
|
||
// UpdateResult updates a ValidationRuleResult with a list of errors and details | ||
func UpdateResult(vr *types.ValidationRuleResult, errs []error, errMsg string, details ...string) { | ||
if len(errs) > 0 { | ||
vr.State = util.Ptr(vapi.ValidationFailed) | ||
vr.Condition.Message = errMsg | ||
for _, err := range errs { | ||
vr.Condition.Failures = append(vr.Condition.Failures, err.Error()) | ||
} | ||
} | ||
vr.Condition.Details = append(vr.Condition.Details, details...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Package dns contains the logic for validating MAAS instance DNS rules | ||
package dns | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/canonical/gomaasclient/api" | ||
"github.com/go-logr/logr" | ||
|
||
"github.com/validator-labs/validator-plugin-maas/api/v1alpha1" | ||
"github.com/validator-labs/validator-plugin-maas/internal/constants" | ||
"github.com/validator-labs/validator-plugin-maas/internal/utils" | ||
"github.com/validator-labs/validator/pkg/types" | ||
) | ||
|
||
// UpstreamDNSRulesService is the service for validating MAAS instance upstream DNS rules | ||
type UpstreamDNSRulesService struct { | ||
log logr.Logger | ||
api api.MAASServer | ||
} | ||
|
||
// NewUpstreamDNSRulesService creates a new UpstreamDNSRulesService | ||
func NewUpstreamDNSRulesService(log logr.Logger, api api.MAASServer) *UpstreamDNSRulesService { | ||
return &UpstreamDNSRulesService{ | ||
log: log, | ||
api: api, | ||
} | ||
} | ||
|
||
// ReconcileMaasInstanceUpstreamDNSRules reconciles a MAAS instance upstream DNS rule | ||
func (s *UpstreamDNSRulesService) ReconcileMaasInstanceUpstreamDNSRules(rule v1alpha1.UpstreamDNSRule) (*types.ValidationRuleResult, error) { | ||
|
||
vr := utils.BuildValidationResult(rule.Name, constants.ValidationTypeUDNS) | ||
|
||
details, errs := s.findDNSServers(rule.NumDNSServers) | ||
|
||
utils.UpdateResult(vr, errs, constants.ErrUDNSNotConfigured, details...) | ||
|
||
if len(errs) > 0 { | ||
return vr, errs[0] | ||
} | ||
|
||
return vr, nil | ||
} | ||
|
||
func (s *UpstreamDNSRulesService) findDNSServers(expected int) ([]string, []error) { | ||
details := make([]string, 0) | ||
errs := make([]error, 0) | ||
|
||
ns, err := s.api.Get("upstream_dns") | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
nameservers := strings.Split(string(ns), " ") | ||
numServers := len(nameservers) | ||
|
||
if nameservers[0] == "" { | ||
numServers = 0 | ||
} | ||
|
||
if numServers < expected { | ||
errs = append(errs, fmt.Errorf("expected %d DNS server(s), got %d", expected, numServers)) | ||
} else { | ||
details = append(details, fmt.Sprintf("Found %d DNS server(s)", len(nameservers))) | ||
} | ||
return details, errs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package dns | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/canonical/gomaasclient/api" | ||
"github.com/go-logr/logr" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/validator-labs/validator-plugin-maas/api/v1alpha1" | ||
) | ||
|
||
type DummyMAASServer struct { | ||
api.MAASServer | ||
upstreamDNS string | ||
} | ||
|
||
func (d *DummyMAASServer) Get(string) ([]byte, error) { | ||
return []byte(d.upstreamDNS), nil | ||
} | ||
|
||
func TestReconcileMaasInstanceImageRule(t *testing.T) { | ||
|
||
testCases := []struct { | ||
Name string | ||
ruleService *UpstreamDNSRulesService | ||
upstreamDNSRules []v1alpha1.UpstreamDNSRule | ||
errors []string | ||
details []string | ||
}{ | ||
{ | ||
Name: "Enough DNS servers are found in MAAS", | ||
ruleService: NewUpstreamDNSRulesService( | ||
logr.Logger{}, | ||
&DummyMAASServer{ | ||
upstreamDNS: "8.8.8.8", | ||
}, | ||
), | ||
upstreamDNSRules: []v1alpha1.UpstreamDNSRule{ | ||
{Name: "Upstream DNS rule 1", NumDNSServers: 1}, | ||
}, | ||
errors: nil, | ||
details: []string{"Found 1 DNS server(s)"}, | ||
}, | ||
{ | ||
Name: "Not enough DNS servers are found in MAAS", | ||
ruleService: NewUpstreamDNSRulesService( | ||
logr.Logger{}, | ||
&DummyMAASServer{ | ||
upstreamDNS: "8.8.8.8", | ||
}), | ||
upstreamDNSRules: []v1alpha1.UpstreamDNSRule{ | ||
{Name: "Upstream DNS rule 2", NumDNSServers: 2}, | ||
}, | ||
errors: []string{"expected 2 DNS server(s), got 1"}, | ||
details: nil, | ||
}, | ||
{ | ||
Name: "No DNS servers are found in MAAS", | ||
ruleService: NewUpstreamDNSRulesService( | ||
logr.Logger{}, | ||
&DummyMAASServer{ | ||
upstreamDNS: "", | ||
}), | ||
upstreamDNSRules: []v1alpha1.UpstreamDNSRule{ | ||
{Name: "Upstream DNS rule 3", NumDNSServers: 1}, | ||
}, | ||
errors: []string{"expected 1 DNS server(s), got 0"}, | ||
details: nil, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.Name, func(t *testing.T) { | ||
var details []string | ||
var errors []string | ||
|
||
for _, rule := range tc.upstreamDNSRules { | ||
vr, _ := tc.ruleService.ReconcileMaasInstanceUpstreamDNSRules(rule) | ||
details = append(details, vr.Condition.Details...) | ||
errors = append(errors, vr.Condition.Failures...) | ||
} | ||
|
||
assert.Equal(t, len(tc.errors), len(errors), "Number of errors should match") | ||
for _, expectedError := range tc.errors { | ||
assert.Contains(t, errors, expectedError, "Expected error should be present") | ||
} | ||
assert.Equal(t, len(tc.details), len(details), "Number of details should match") | ||
for _, expectedDetail := range tc.details { | ||
assert.Contains(t, details, expectedDetail, "Expected detail should be present") | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters