1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/validation.go

150 lines
4.6 KiB
Go
Raw Normal View History

2019-05-13 18:17:28 -07:00
package engine
import (
"encoding/json"
"strconv"
2019-05-16 19:31:02 +03:00
2019-05-21 11:00:09 -07:00
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/result"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Validate handles validating admission request
// Checks the target resourse for rules defined in the policy
func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) result.Result {
var resource interface{}
json.Unmarshal(rawResource, &resource)
policyResult := result.NewPolicyApplicationResult(policy.Name)
for _, rule := range policy.Spec.Rules {
if rule.Validation == nil {
continue
}
ruleApplicationResult := result.NewRuleApplicationResult(rule.Name)
ok := ResourceMeetsDescription(rawResource, rule.ResourceDescription, gvk)
if !ok {
ruleApplicationResult.AddMessagef("Rule %s is not applicable to resource\n", rule.Name)
policyResult = result.Append(policyResult, &ruleApplicationResult)
continue
}
validationResult := validateResourceWithPattern(resource, rule.Validation.Pattern)
if result.Success != validationResult.Reason {
ruleApplicationResult.MergeWith(&validationResult)
ruleApplicationResult.AddMessagef(*rule.Validation.Message)
} else {
ruleApplicationResult.AddMessagef("Success")
}
policyResult = result.Append(policyResult, &ruleApplicationResult)
}
return policyResult
}
func validateResourceWithPattern(resource, pattern interface{}) result.RuleApplicationResult {
return validateResourceElement(resource, pattern, "/")
}
func validateResourceElement(value, pattern interface{}, path string) result.RuleApplicationResult {
res := result.NewRuleApplicationResult("")
// TODO: Move similar message templates to message package
switch typedPattern := pattern.(type) {
case map[string]interface{}:
typedValue, ok := value.(map[string]interface{})
if !ok {
res.FailWithMessagef("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, pattern, value)
return res
}
2019-05-17 14:51:54 +03:00
return validateMap(typedValue, typedPattern, path)
case []interface{}:
typedValue, ok := value.([]interface{})
if !ok {
res.FailWithMessagef("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, pattern, value)
return res
}
return validateArray(typedValue, typedPattern, path)
2019-06-05 17:35:34 +03:00
case string, float64, int, int64, bool, nil:
if !ValidateValueWithPattern(value, pattern) {
res.FailWithMessagef("Failed to validate value %v with pattern %v. Path: %s", value, pattern, path)
}
return res
default:
res.FailWithMessagef("Pattern contains unknown type %T. Path: %s", pattern, path)
return res
}
}
func validateMap(valueMap, patternMap map[string]interface{}, path string) result.RuleApplicationResult {
res := result.NewRuleApplicationResult("")
for key, pattern := range patternMap {
2019-05-16 21:36:30 +03:00
if wrappedWithParentheses(key) {
key = key[1 : len(key)-1]
}
if pattern == "*" && valueMap[key] != nil {
2019-06-04 17:33:21 +03:00
continue
} else if pattern == "*" && valueMap[key] == nil {
res.FailWithMessagef("Field %s is not present", key)
2019-06-04 17:33:21 +03:00
} else {
elementResult := validateResourceElement(valueMap[key], pattern, path+key+"/")
if result.Failed == elementResult.Reason {
res.Reason = elementResult.Reason
res.Messages = append(res.Messages, elementResult.Messages...)
2019-06-04 17:33:21 +03:00
}
}
}
return res
}
func validateArray(resourceArray, patternArray []interface{}, path string) result.RuleApplicationResult {
res := result.NewRuleApplicationResult("")
2019-05-17 14:51:54 +03:00
if 0 == len(patternArray) {
return res
}
switch pattern := patternArray[0].(type) {
case map[string]interface{}:
2019-05-21 18:27:56 +03:00
anchors := GetAnchorsFromMap(pattern)
for i, value := range resourceArray {
currentPath := path + strconv.Itoa(i) + "/"
resource, ok := value.(map[string]interface{})
if !ok {
res.FailWithMessagef("Pattern and resource have different structures. Path: %s. Expected %T, found %T", currentPath, pattern, value)
return res
}
if skipArrayObject(resource, anchors) {
continue
}
mapValidationResult := validateMap(resource, pattern, currentPath)
if result.Failed == mapValidationResult.Reason {
res.Reason = mapValidationResult.Reason
res.Messages = append(res.Messages, mapValidationResult.Messages...)
2019-05-16 21:36:30 +03:00
}
}
2019-06-05 17:35:34 +03:00
case string, float64, int, int64, bool, nil:
for _, value := range resourceArray {
if !ValidateValueWithPattern(value, pattern) {
res.FailWithMessagef("Failed to validate value %v with pattern %v. Path: %s", value, pattern, path)
}
}
default:
res.FailWithMessagef("Array element pattern of unknown type %T. Path: %s", pattern, path)
2019-05-16 19:31:02 +03:00
}
return res
}