forked from istio/operator
-
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.
* Dynamic validation Signed-off-by: Serguei Bezverkhi <[email protected]> * Fix lint failure Signed-off-by: Serguei Bezverkhi <[email protected]> * make validation a separate func to be called by type Validation func Signed-off-by: Serguei Bezverkhi <[email protected]> * Add root of Values and root of IstioControlPlaneSpec to pass to Validation Signed-off-by: Serguei Bezverkhi <[email protected]> * Refactor to use the recursion Signed-off-by: Serguei Bezverkhi <[email protected]> * pushing again Signed-off-by: Serguei Bezverkhi <[email protected]> * Address comments Signed-off-by: Serguei Bezverkhi <[email protected]> * Addressing comment Signed-off-by: Serguei Bezverkhi <[email protected]> * Moving validation out of v1alpha2 package Signed-off-by: Serguei Bezverkhi <[email protected]> * Start adding per type validations Signed-off-by: Serguei Bezverkhi <[email protected]> * Address lint messages Signed-off-by: Serguei Bezverkhi <[email protected]> * rebase Signed-off-by: Serguei Bezverkhi <[email protected]> * refactor validate to validate slices Signed-off-by: Serguei Bezverkhi <[email protected]> * Fix lint warning Signed-off-by: Serguei Bezverkhi <[email protected]> * remove rebase leftover Signed-off-by: Serguei Bezverkhi <[email protected]> * Another lint warning Signed-off-by: Serguei Bezverkhi <[email protected]> * Add unit tests Signed-off-by: Serguei Bezverkhi <[email protected]> * Split map and slice processing Signed-off-by: Serguei Bezverkhi <[email protected]> * add License to test file Signed-off-by: Serguei Bezverkhi <[email protected]> * Lint error Signed-off-by: Serguei Bezverkhi <[email protected]> * Addressing comments Signed-off-by: Serguei Bezverkhi <[email protected]> * Addressing comments Signed-off-by: Serguei Bezverkhi <[email protected]> * Addressing lint error Signed-off-by: Serguei Bezverkhi <[email protected]> * Addressing lint error Signed-off-by: Serguei Bezverkhi <[email protected]>
- Loading branch information
1 parent
a119f42
commit f93c2a8
Showing
3 changed files
with
207 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright 2019 Istio Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package validation | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
|
||
"istio.io/operator/pkg/apis/istio/v1alpha2" | ||
"istio.io/operator/pkg/util" | ||
) | ||
|
||
const ( | ||
validationMethodName = "Validate" | ||
) | ||
|
||
// ValidateConfig calls validation func for every defined element in Values | ||
func ValidateConfig(failOnMissingValidation bool, values *v1alpha2.Values, icpls *v1alpha2.IstioControlPlaneSpec) util.Errors { | ||
var validationErrors util.Errors | ||
validationErrors = util.AppendErrs(validationErrors, validateSubTypes(reflect.ValueOf(values).Elem(), failOnMissingValidation, values, icpls)) | ||
|
||
return validationErrors | ||
} | ||
|
||
func validateSubTypes(e reflect.Value, failOnMissingValidation bool, values *v1alpha2.Values, icpls *v1alpha2.IstioControlPlaneSpec) util.Errors { | ||
// Dealing with receiver pointer and receiver value | ||
ptr := e | ||
k := e.Kind() | ||
if k == reflect.Ptr || k == reflect.Interface { | ||
e = e.Elem() | ||
} | ||
// check for method on value | ||
method := e.MethodByName(validationMethodName) | ||
if !method.IsValid() { | ||
method = ptr.MethodByName(validationMethodName) | ||
} | ||
|
||
var validationErrors util.Errors | ||
if util.IsNilOrInvalidValue(method) { | ||
if failOnMissingValidation { | ||
validationErrors = append(validationErrors, fmt.Errorf("type %s is missing Validation method", e.Type().String())) | ||
} | ||
} else { | ||
r := method.Call([]reflect.Value{reflect.ValueOf(failOnMissingValidation), reflect.ValueOf(values), reflect.ValueOf(icpls)})[0].Interface().(util.Errors) | ||
if len(r) != 0 { | ||
validationErrors = append(validationErrors, r...) | ||
} | ||
} | ||
// If it is not a struct nothing to do, returning previously collected validation errors | ||
if e.Kind() != reflect.Struct { | ||
return validationErrors | ||
} | ||
for i := 0; i < e.NumField(); i++ { | ||
// Corner case of a slice of something, if something is defined type, then process it recursiveley. | ||
if e.Field(i).Kind() == reflect.Slice { | ||
validationErrors = append(validationErrors, processSlice(e.Field(i), failOnMissingValidation, values, icpls)...) | ||
continue | ||
} | ||
if e.Field(i).Kind() == reflect.Map { | ||
validationErrors = append(validationErrors, processMap(e.Field(i), failOnMissingValidation, values, icpls)...) | ||
continue | ||
} | ||
// Validation is not required if it is not a defined type | ||
if e.Field(i).Kind() != reflect.Interface && e.Field(i).Kind() != reflect.Ptr { | ||
continue | ||
} | ||
val := e.Field(i).Elem() | ||
if util.IsNilOrInvalidValue(val) { | ||
continue | ||
} | ||
validationErrors = append(validationErrors, validateSubTypes(e.Field(i), failOnMissingValidation, values, icpls)...) | ||
} | ||
|
||
return validationErrors | ||
} | ||
|
||
func processSlice(e reflect.Value, failOnMissingValidation bool, values *v1alpha2.Values, icpls *v1alpha2.IstioControlPlaneSpec) util.Errors { | ||
var validationErrors util.Errors | ||
for i := 0; i < e.Len(); i++ { | ||
validationErrors = append(validationErrors, validateSubTypes(e.Index(i), failOnMissingValidation, values, icpls)...) | ||
} | ||
|
||
return validationErrors | ||
} | ||
|
||
func processMap(e reflect.Value, failOnMissingValidation bool, values *v1alpha2.Values, icpls *v1alpha2.IstioControlPlaneSpec) util.Errors { | ||
var validationErrors util.Errors | ||
for _, k := range e.MapKeys() { | ||
v := e.MapIndex(k) | ||
validationErrors = append(validationErrors, validateSubTypes(v, failOnMissingValidation, values, icpls)...) | ||
} | ||
|
||
return validationErrors | ||
} |
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,81 @@ | ||
// Copyright 2019 Istio Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package validation | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"istio.io/operator/pkg/apis/istio/v1alpha2" | ||
) | ||
|
||
func TestValidate(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
toValidate *v1alpha2.Values | ||
validated bool | ||
}{ | ||
{ | ||
name: "Empty struct", | ||
toValidate: &v1alpha2.Values{}, | ||
validated: true, | ||
}, | ||
{ | ||
name: "With CNI defined", | ||
toValidate: &v1alpha2.Values{ | ||
CNI: &v1alpha2.CNIConfig{ | ||
Enabled: makeBoolPtr(true), | ||
}, | ||
}, | ||
validated: true, | ||
}, | ||
{ | ||
name: "With Slice", | ||
toValidate: &v1alpha2.Values{ | ||
Gateways: &v1alpha2.GatewaysConfig{ | ||
Enabled: makeBoolPtr(true), | ||
EgressGateway: &v1alpha2.EgressGatewayConfig{ | ||
Ports: []*v1alpha2.PortsConfig{ | ||
{ | ||
Name: makeStringPtr("port1"), | ||
}, | ||
{ | ||
Name: makeStringPtr("port2"), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
validated: true, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
err := validateSubTypes(reflect.ValueOf(tt.toValidate).Elem(), false, tt.toValidate, nil) | ||
if len(err) != 0 && tt.validated { | ||
t.Fatalf("Test %s failed with errors: %+v but supposed to succeed", tt.name, err) | ||
} | ||
if len(err) == 0 && !tt.validated { | ||
t.Fatalf("Test %s failed as it is supposed to fail but succeeded", tt.name) | ||
} | ||
} | ||
} | ||
|
||
func makeBoolPtr(v bool) *bool { | ||
return &v | ||
} | ||
func makeStringPtr(v string) *string { | ||
return &v | ||
} |
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