Skip to content

Commit

Permalink
Pass down path through validators (#331)
Browse files Browse the repository at this point in the history
- Pass the path down to the validation methods so they don't need to know
where in the struct they are placed.
- Also use .Index() and .Key() instead of fmt.Sprintf to indicate where
in a slice/map we are.
  • Loading branch information
lkysow authored Sep 23, 2020
1 parent 1e9a70b commit aa64895
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 15 deletions.
26 changes: 17 additions & 9 deletions api/v1alpha1/servicedefaults_types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package v1alpha1

import (
"fmt"
"strings"

capi "github.com/hashicorp/consul/api"
Expand Down Expand Up @@ -127,10 +126,12 @@ func (in *ServiceDefaults) ToConsul() capi.ConfigEntry {
// returns an error which lists all invalid fields in the resource spec.
func (in *ServiceDefaults) Validate() error {
var allErrs field.ErrorList
if err := in.Spec.MeshGateway.validate(); err != nil {
path := field.NewPath("spec")

if err := in.Spec.MeshGateway.validate(path.Child("meshGateway")); err != nil {
allErrs = append(allErrs, err)
}
allErrs = append(allErrs, in.Spec.Expose.validate()...)
allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...)

if len(allErrs) > 0 {
return apierrors.NewInvalid(
Expand Down Expand Up @@ -226,15 +227,22 @@ func (e ExposeConfig) toConsul() capi.ExposeConfig {
}
}

func (e ExposeConfig) validate() []*field.Error {
func (e ExposeConfig) validate(path *field.Path) []*field.Error {
var errs field.ErrorList
protocols := []string{"http", "http2"}
for i, path := range e.Paths {
if path.Path != "" && !strings.HasPrefix(path.Path, "/") {
errs = append(errs, field.Invalid(field.NewPath("spec").Child("expose").Child(fmt.Sprintf("paths[%d]", i)).Child("path"), path.Path, `must begin with a '/'`))
for i, pathCfg := range e.Paths {
indexPath := path.Child("paths").Index(i)
if pathCfg.Path != "" && !strings.HasPrefix(pathCfg.Path, "/") {
errs = append(errs, field.Invalid(
indexPath.Child("path"),
pathCfg.Path,
`must begin with a '/'`))
}
if !sliceContains(protocols, path.Protocol) {
errs = append(errs, field.Invalid(field.NewPath("spec").Child("expose").Child(fmt.Sprintf("paths[%d]", i)).Child("protocol"), path.Protocol, notInSliceMessage(protocols)))
if !sliceContains(protocols, pathCfg.Protocol) {
errs = append(errs, field.Invalid(
indexPath.Child("protocol"),
pathCfg.Protocol,
notInSliceMessage(protocols)))
}
}
return errs
Expand Down
7 changes: 3 additions & 4 deletions api/v1alpha1/serviceresolver_types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package v1alpha1

import (
"fmt"
"reflect"
"sort"
"time"
Expand Down Expand Up @@ -112,6 +111,7 @@ func (in *ServiceResolver) MatchesConsul(candidate capi.ConfigEntry) bool {

func (in *ServiceResolver) Validate() error {
var errs field.ErrorList
path := field.NewPath("spec")

// Iterate through failover map keys in sorted order so tests are
// deterministic.
Expand All @@ -122,7 +122,7 @@ func (in *ServiceResolver) Validate() error {
sort.Strings(keys)
for _, k := range keys {
f := in.Spec.Failover[k]
if err := f.validate(k); err != nil {
if err := f.validate(path.Child("failover").Key(k)); err != nil {
errs = append(errs, err)
}
}
Expand All @@ -135,9 +135,8 @@ func (in *ServiceResolver) Validate() error {
return nil
}

func (in *ServiceResolverFailover) validate(key string) *field.Error {
func (in *ServiceResolverFailover) validate(path *field.Path) *field.Error {
if in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 {
path := field.NewPath("spec").Child(fmt.Sprintf("failover[%s]", key))
// NOTE: We're passing "{}" here as our value because we know that the
// error is we have an empty object.
return field.Invalid(path, "{}",
Expand Down
4 changes: 2 additions & 2 deletions api/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ func (m MeshGatewayConfig) toConsul() capi.MeshGatewayConfig {
}
}

func (m MeshGatewayConfig) validate() *field.Error {
func (m MeshGatewayConfig) validate(path *field.Path) *field.Error {
modes := []string{"remote", "local", "none", ""}
if !sliceContains(modes, m.Mode) {
return field.Invalid(field.NewPath("spec").Child("meshGateway").Child("mode"), m.Mode, notInSliceMessage(modes))
return field.Invalid(path.Child("mode"), m.Mode, notInSliceMessage(modes))
}
return nil
}
Expand Down

0 comments on commit aa64895

Please sign in to comment.