mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
policy validation: refactoring
This commit is contained in:
parent
68c87a09ec
commit
3fa8834b4a
13 changed files with 708 additions and 895 deletions
|
@ -5,6 +5,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
)
|
)
|
||||||
|
|
||||||
//ValidationHandler for element processes
|
//ValidationHandler for element processes
|
||||||
|
@ -15,19 +16,20 @@ type ValidationHandler interface {
|
||||||
//CreateElementHandler factory to process elements
|
//CreateElementHandler factory to process elements
|
||||||
func CreateElementHandler(element string, pattern interface{}, path string) ValidationHandler {
|
func CreateElementHandler(element string, pattern interface{}, path string) ValidationHandler {
|
||||||
switch {
|
switch {
|
||||||
case isConditionAnchor(element):
|
case anchor.IsConditionAnchor(element):
|
||||||
return NewConditionAnchorHandler(element, pattern, path)
|
return NewConditionAnchorHandler(element, pattern, path)
|
||||||
case isExistanceAnchor(element):
|
case anchor.IsExistanceAnchor(element):
|
||||||
return NewExistanceHandler(element, pattern, path)
|
return NewExistanceHandler(element, pattern, path)
|
||||||
case isEqualityAnchor(element):
|
case anchor.IsEqualityAnchor(element):
|
||||||
return NewEqualityHandler(element, pattern, path)
|
return NewEqualityHandler(element, pattern, path)
|
||||||
case isNegationAnchor(element):
|
case anchor.IsNegationAnchor(element):
|
||||||
return NewNegationHandler(element, pattern, path)
|
return NewNegationHandler(element, pattern, path)
|
||||||
default:
|
default:
|
||||||
return NewDefaultHandler(element, pattern, path)
|
return NewDefaultHandler(element, pattern, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewNegationHandler returns instance of negation handler
|
||||||
func NewNegationHandler(anchor string, pattern interface{}, path string) ValidationHandler {
|
func NewNegationHandler(anchor string, pattern interface{}, path string) ValidationHandler {
|
||||||
return NegationHandler{
|
return NegationHandler{
|
||||||
anchor: anchor,
|
anchor: anchor,
|
||||||
|
@ -56,6 +58,7 @@ func (nh NegationHandler) Handle(resourceMap map[string]interface{}, originPatte
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewEqualityHandler returens instance of equality handler
|
||||||
func NewEqualityHandler(anchor string, pattern interface{}, path string) ValidationHandler {
|
func NewEqualityHandler(anchor string, pattern interface{}, path string) ValidationHandler {
|
||||||
return EqualityHandler{
|
return EqualityHandler{
|
||||||
anchor: anchor,
|
anchor: anchor,
|
||||||
|
@ -217,7 +220,7 @@ func getAnchorsResourcesFromMap(patternMap map[string]interface{}) (map[string]i
|
||||||
anchors := map[string]interface{}{}
|
anchors := map[string]interface{}{}
|
||||||
resources := map[string]interface{}{}
|
resources := map[string]interface{}{}
|
||||||
for key, value := range patternMap {
|
for key, value := range patternMap {
|
||||||
if isConditionAnchor(key) || isExistanceAnchor(key) || isEqualityAnchor(key) || isNegationAnchor(key) {
|
if anchor.IsConditionAnchor(key) || anchor.IsExistanceAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
|
||||||
anchors[key] = value
|
anchors[key] = value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
54
pkg/engine/anchor/utils.go
Normal file
54
pkg/engine/anchor/utils.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package anchor
|
||||||
|
|
||||||
|
// Anchor function type
|
||||||
|
type IsAnchor func(str string) bool
|
||||||
|
|
||||||
|
func IsConditionAnchor(str string) bool {
|
||||||
|
if len(str) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (str[0] == '(' && str[len(str)-1] == ')')
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNegationAnchor(str string) bool {
|
||||||
|
left := "X("
|
||||||
|
right := ")"
|
||||||
|
if len(str) < len(left)+len(right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
//TODO: trim spaces ?
|
||||||
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAddingAnchor(key string) bool {
|
||||||
|
const left = "+("
|
||||||
|
const right = ")"
|
||||||
|
|
||||||
|
if len(key) < len(left)+len(right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return left == key[:len(left)] && right == key[len(key)-len(right):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsEqualityAnchor(str string) bool {
|
||||||
|
left := "=("
|
||||||
|
right := ")"
|
||||||
|
if len(str) < len(left)+len(right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
//TODO: trim spaces ?
|
||||||
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsExistanceAnchor(str string) bool {
|
||||||
|
left := "^("
|
||||||
|
right := ")"
|
||||||
|
|
||||||
|
if len(str) < len(left)+len(right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
||||||
|
}
|
58
pkg/engine/anchor/utils_test.go
Normal file
58
pkg/engine/anchor/utils_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package anchor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringIsWrappedWithParentheses(t *testing.T) {
|
||||||
|
str := "(something)"
|
||||||
|
assert.Assert(t, IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasOnlyParentheses(t *testing.T) {
|
||||||
|
str := "()"
|
||||||
|
assert.Assert(t, IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasNoParentheses(t *testing.T) {
|
||||||
|
str := "something"
|
||||||
|
assert.Assert(t, !IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasLeftParentheses(t *testing.T) {
|
||||||
|
str := "(something"
|
||||||
|
assert.Assert(t, !IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasRightParentheses(t *testing.T) {
|
||||||
|
str := "something)"
|
||||||
|
assert.Assert(t, !IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringParenthesesInside(t *testing.T) {
|
||||||
|
str := "so)m(et(hin)g"
|
||||||
|
assert.Assert(t, !IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_Empty(t *testing.T) {
|
||||||
|
str := ""
|
||||||
|
assert.Assert(t, !IsConditionAnchor(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsExistanceAnchor_Yes(t *testing.T) {
|
||||||
|
assert.Assert(t, IsExistanceAnchor("^(abc)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsExistanceAnchor_NoRightBracket(t *testing.T) {
|
||||||
|
assert.Assert(t, !IsExistanceAnchor("^(abc"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsExistanceAnchor_OnlyHat(t *testing.T) {
|
||||||
|
assert.Assert(t, !IsExistanceAnchor("^abc"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsExistanceAnchor_ConditionAnchor(t *testing.T) {
|
||||||
|
assert.Assert(t, !IsExistanceAnchor("(abc)"))
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ func Generate(client *client.Client, policy kyverno.ClusterPolicy, ns unstructur
|
||||||
response.PolicyResponse.RulesAppliedCount++
|
response.PolicyResponse.RulesAppliedCount++
|
||||||
}
|
}
|
||||||
for _, rule := range policy.Spec.Rules {
|
for _, rule := range policy.Spec.Rules {
|
||||||
if rule.Generation == (kyverno.Generation{}) {
|
if !rule.HasGenerate() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
||||||
|
|
|
@ -37,7 +37,7 @@ func Mutate(policy kyverno.ClusterPolicy, resource unstructured.Unstructured) (r
|
||||||
|
|
||||||
for _, rule := range policy.Spec.Rules {
|
for _, rule := range policy.Spec.Rules {
|
||||||
//TODO: to be checked before calling the resources as well
|
//TODO: to be checked before calling the resources as well
|
||||||
if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
|
if !rule.HasMutate() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if the resource satisfies the filter conditions defined in the rule
|
// check if the resource satisfies the filter conditions defined in the rule
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
)
|
)
|
||||||
|
|
||||||
// processOverlay processes validation patterns on the resource
|
// processOverlay processes validation patterns on the resource
|
||||||
|
@ -148,7 +149,7 @@ func applyOverlayToMap(resourceMap, overlayMap map[string]interface{}, path stri
|
||||||
for key, value := range overlayMap {
|
for key, value := range overlayMap {
|
||||||
// skip anchor element because it has condition, not
|
// skip anchor element because it has condition, not
|
||||||
// the value that must replace resource value
|
// the value that must replace resource value
|
||||||
if isConditionAnchor(key) {
|
if anchor.IsConditionAnchor(key) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +157,7 @@ func applyOverlayToMap(resourceMap, overlayMap map[string]interface{}, path stri
|
||||||
currentPath := path + noAnchorKey + "/"
|
currentPath := path + noAnchorKey + "/"
|
||||||
resourcePart, ok := resourceMap[noAnchorKey]
|
resourcePart, ok := resourceMap[noAnchorKey]
|
||||||
|
|
||||||
if ok && !isAddingAnchor(key) {
|
if ok && !anchor.IsAddingAnchor(key) {
|
||||||
// Key exists - go down through the overlay and resource trees
|
// Key exists - go down through the overlay and resource trees
|
||||||
patches, err := applyOverlay(resourcePart, value, currentPath)
|
patches, err := applyOverlay(resourcePart, value, currentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
)
|
)
|
||||||
|
|
||||||
func meetConditions(resource, overlay interface{}) bool {
|
func meetConditions(resource, overlay interface{}) bool {
|
||||||
|
@ -51,7 +52,7 @@ func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}) bool {
|
||||||
for key, value := range overlayMap {
|
for key, value := range overlayMap {
|
||||||
resourcePart, ok := resourceMap[key]
|
resourcePart, ok := resourceMap[key]
|
||||||
|
|
||||||
if ok && !isAddingAnchor(key) {
|
if ok && !anchor.IsAddingAnchor(key) {
|
||||||
if !meetConditions(resourcePart, value) {
|
if !meetConditions(resourcePart, value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
package policy
|
package policy
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// joinErrs joins the list of error into single error
|
|
||||||
// adds a new line between errors
|
|
||||||
func joinErrs(errs []error) error {
|
|
||||||
if len(errs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res := "\n"
|
|
||||||
for _, err := range errs {
|
|
||||||
res = fmt.Sprintf(res + err.Error() + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Contains Check if strint is contained in a list of string
|
//Contains Check if strint is contained in a list of string
|
||||||
func containString(list []string, element string) bool {
|
func containString(list []string, element string) bool {
|
||||||
for _, e := range list {
|
for _, e := range list {
|
||||||
|
@ -30,73 +9,3 @@ func containString(list []string, element string) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasExistingAnchor checks if str has existing anchor
|
|
||||||
// strip anchor if necessary
|
|
||||||
func hasExistingAnchor(str string) (bool, string) {
|
|
||||||
left := "^("
|
|
||||||
right := ")"
|
|
||||||
|
|
||||||
if len(str) < len(left)+len(right) {
|
|
||||||
return false, str
|
|
||||||
}
|
|
||||||
|
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right), str[len(left) : len(str)-len(right)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasValidAnchors checks str has the valid anchor
|
|
||||||
// mutate: (), +()
|
|
||||||
// validate: (), ^(), =(), X()
|
|
||||||
// generate: none
|
|
||||||
// invalid anchors: ~(),!()
|
|
||||||
func hasValidAnchors(anchors []anchor, str string) (bool, string) {
|
|
||||||
if wrappedWithAttributes(str) {
|
|
||||||
return mustWrapWithAnchors(anchors, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, str
|
|
||||||
}
|
|
||||||
|
|
||||||
// mustWrapWithAnchors validates str must wrap with
|
|
||||||
// at least one given anchor
|
|
||||||
func mustWrapWithAnchors(anchors []anchor, str string) (bool, string) {
|
|
||||||
for _, a := range anchors {
|
|
||||||
if str[:len(a.left)] == a.left && str[len(str)-len(a.right):] == a.right {
|
|
||||||
return true, str[len(a.left) : len(str)-len(a.right)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, str
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrappedWithAttributes(str string) bool {
|
|
||||||
if len(str) < 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str[0] == '(' && str[len(str)-1] == ')') ||
|
|
||||||
(str[0] == '^' || str[0] == '+' || str[0] == '=' || str[0] == 'X' || str[0] == '!' || str[0] == '~') &&
|
|
||||||
(str[1] == '(' && str[len(str)-1] == ')') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func joinAnchors(anchorPatterns []anchor) string {
|
|
||||||
var res []string
|
|
||||||
for _, a := range anchorPatterns {
|
|
||||||
res = append(res, a.left+a.right)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(res, " || ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasNegationAnchor(str string) (bool, string) {
|
|
||||||
left := "X("
|
|
||||||
right := ")"
|
|
||||||
if len(str) < len(left)+len(right) {
|
|
||||||
return false, str
|
|
||||||
}
|
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right), str[len(left) : len(str)-len(right)]
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,89 +4,84 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type anchor struct {
|
// Validate does some initial check to verify some conditions
|
||||||
left string
|
// - One operation per rule
|
||||||
right string
|
// - ResourceDescription mandatory checks
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
conditionalAnchor = anchor{left: "(", right: ")"}
|
|
||||||
existingAnchor = anchor{left: "^(", right: ")"}
|
|
||||||
equalityAnchor = anchor{left: "=(", right: ")"}
|
|
||||||
plusAnchor = anchor{left: "+(", right: ")"}
|
|
||||||
negationAnchor = anchor{left: "X(", right: ")"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func Validate(p kyverno.ClusterPolicy) error {
|
func Validate(p kyverno.ClusterPolicy) error {
|
||||||
var errs []error
|
if path, err := validateUniqueRuleName(p); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.%s: %v", path, err)
|
||||||
if err := validateUniqueRuleName(p); err != nil {
|
|
||||||
errs = append(errs, fmt.Errorf("- Invalid Policy '%s':", p.Name))
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range p.Spec.Rules {
|
for i, rule := range p.Spec.Rules {
|
||||||
if ruleErrs := validateRule(rule); len(ruleErrs) != 0 {
|
// only one type of rule is allowed per rule
|
||||||
errs = append(errs, fmt.Errorf("- invalid rule '%s':", rule.Name))
|
if err := validateRuleType(rule); err != nil {
|
||||||
errs = append(errs, ruleErrs...)
|
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return joinErrs(errs)
|
// validate resource description
|
||||||
}
|
if path, err := validateResources(rule); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].%s: %v", i, path, err)
|
||||||
// ValidateUniqueRuleName checks if the rule names are unique across a policy
|
}
|
||||||
func validateUniqueRuleName(p kyverno.ClusterPolicy) error {
|
// validate rule types
|
||||||
var ruleNames []string
|
// only one type of rule is allowed per rule
|
||||||
|
if err := validateRuleType(rule); err != nil {
|
||||||
for _, rule := range p.Spec.Rules {
|
// as there are more than 1 operation in rule, not need to evaluate it further
|
||||||
if containString(ruleNames, rule.Name) {
|
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
|
||||||
return fmt.Errorf(`duplicate rule name: '%s'`, rule.Name)
|
}
|
||||||
|
// Operation Validation
|
||||||
|
// Mutation
|
||||||
|
if rule.HasMutate() {
|
||||||
|
if path, err := validateMutation(rule.Mutation); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", i, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Validation
|
||||||
|
if rule.HasValidate() {
|
||||||
|
if path, err := validateValidation(rule.Validation); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", i, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generation
|
||||||
|
if rule.HasGenerate() {
|
||||||
|
if path, err := validateGeneration(rule.Generation); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", i, path, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ruleNames = append(ruleNames, rule.Name)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks if rule is not empty and all substructures are valid
|
func validateResources(rule kyverno.Rule) (string, error) {
|
||||||
func validateRule(r kyverno.Rule) []error {
|
// matched resources
|
||||||
var errs []error
|
if path, err := validateMatchedResourceDescription(rule.MatchResources.ResourceDescription); err != nil {
|
||||||
|
return fmt.Sprintf("resources.%s", path), err
|
||||||
// only one type of rule is allowed per rule
|
|
||||||
if err := validateRuleType(r); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
}
|
||||||
|
// exclude resources
|
||||||
// validate resource description block
|
if path, err := validateExcludeResourceDescription(rule.ExcludeResources.ResourceDescription); err != nil {
|
||||||
if err := validateMatchedResourceDescription(r.MatchResources.ResourceDescription); err != nil {
|
return fmt.Sprintf("resources.%s", path), err
|
||||||
errs = append(errs, fmt.Errorf("error in match block, %v", err))
|
|
||||||
}
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := validateResourceDescription(r.ExcludeResources.ResourceDescription); err != nil {
|
// ValidateUniqueRuleName checks if the rule names are unique across a policy
|
||||||
errs = append(errs, fmt.Errorf("error in exclude block, %v", err))
|
func validateUniqueRuleName(p kyverno.ClusterPolicy) (string, error) {
|
||||||
|
var ruleNames []string
|
||||||
|
|
||||||
|
for i, rule := range p.Spec.Rules {
|
||||||
|
if containString(ruleNames, rule.Name) {
|
||||||
|
return fmt.Sprintf("rule[%d]", i), fmt.Errorf(`duplicate rule name: '%s'`, rule.Name)
|
||||||
|
}
|
||||||
|
ruleNames = append(ruleNames, rule.Name)
|
||||||
}
|
}
|
||||||
|
return "", nil
|
||||||
// validate anchors on mutate
|
|
||||||
if mErrs := validateMutation(r.Mutation); len(mErrs) != 0 {
|
|
||||||
errs = append(errs, mErrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vErrs := validateValidation(r.Validation); len(vErrs) != 0 {
|
|
||||||
errs = append(errs, vErrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := validateGeneration(r.Generation); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateRuleType checks only one type of rule is defined per rule
|
// validateRuleType checks only one type of rule is defined per rule
|
||||||
|
@ -104,7 +99,7 @@ func validateRuleType(r kyverno.Rule) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if operationCount == 0 {
|
if operationCount == 0 {
|
||||||
return fmt.Errorf("no operation defined in the rule '%s'.(supported operations: mutation,validation,generation,query)", r.Name)
|
return fmt.Errorf("no operation defined in the rule '%s'.(supported operations: mutation,validation,generation)", r.Name)
|
||||||
} else if operationCount != 1 {
|
} else if operationCount != 1 {
|
||||||
return fmt.Errorf("multiple operations defined in the rule '%s', only one type of operation is allowed per rule", r.Name)
|
return fmt.Errorf("multiple operations defined in the rule '%s', only one type of operation is allowed per rule", r.Name)
|
||||||
}
|
}
|
||||||
|
@ -116,16 +111,31 @@ func validateRuleType(r kyverno.Rule) error {
|
||||||
// Returns error if
|
// Returns error if
|
||||||
// - kinds is empty array in matched resource block, i.e. kinds: []
|
// - kinds is empty array in matched resource block, i.e. kinds: []
|
||||||
// - selector is invalid
|
// - selector is invalid
|
||||||
func validateMatchedResourceDescription(rd kyverno.ResourceDescription) error {
|
func validateMatchedResourceDescription(rd kyverno.ResourceDescription) (string, error) {
|
||||||
if reflect.DeepEqual(rd, kyverno.ResourceDescription{}) {
|
if reflect.DeepEqual(rd, kyverno.ResourceDescription{}) {
|
||||||
return nil
|
return "", fmt.Errorf("match resources not specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rd.Kinds) == 0 {
|
if len(rd.Kinds) == 0 {
|
||||||
return errors.New("field Kind is not specified")
|
return "match", fmt.Errorf("kind is mandatory")
|
||||||
}
|
}
|
||||||
|
|
||||||
return validateResourceDescription(rd)
|
if err := validateResourceDescription(rd); err != nil {
|
||||||
|
return "match", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateExcludeResourceDescription(rd kyverno.ResourceDescription) (string, error) {
|
||||||
|
if reflect.DeepEqual(rd, kyverno.ResourceDescription{}) {
|
||||||
|
// exclude is not mandatory
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if err := validateResourceDescription(rd); err != nil {
|
||||||
|
return "exclude", err
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateResourceDescription returns error if selector is invalid
|
// validateResourceDescription returns error if selector is invalid
|
||||||
|
@ -144,22 +154,23 @@ func validateResourceDescription(rd kyverno.ResourceDescription) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateMutation(m kyverno.Mutation) []error {
|
func validateMutation(m kyverno.Mutation) (string, error) {
|
||||||
var errs []error
|
// JSON Patches
|
||||||
if len(m.Patches) != 0 {
|
if len(m.Patches) != 0 {
|
||||||
for _, patch := range m.Patches {
|
for i, patch := range m.Patches {
|
||||||
err := validatePatch(patch)
|
if err := validatePatch(patch); err != nil {
|
||||||
errs = append(errs, err)
|
return fmt.Sprintf("patch[%d]", i), err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Overlay
|
||||||
if m.Overlay != nil {
|
if m.Overlay != nil {
|
||||||
_, err := validateAnchors([]anchor{conditionalAnchor, plusAnchor}, m.Overlay, "/")
|
path, err := validatePattern(m.Overlay, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsAddingAnchor})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
return path, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errs
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate if all mandatory PolicyPatch fields are set
|
// Validate if all mandatory PolicyPatch fields are set
|
||||||
|
@ -167,7 +178,6 @@ func validatePatch(pp kyverno.Patch) error {
|
||||||
if pp.Path == "" {
|
if pp.Path == "" {
|
||||||
return errors.New("JSONPatch field 'path' is mandatory")
|
return errors.New("JSONPatch field 'path' is mandatory")
|
||||||
}
|
}
|
||||||
|
|
||||||
if pp.Operation == "add" || pp.Operation == "replace" {
|
if pp.Operation == "add" || pp.Operation == "replace" {
|
||||||
if pp.Value == nil {
|
if pp.Value == nil {
|
||||||
return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation)
|
return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation)
|
||||||
|
@ -181,151 +191,154 @@ func validatePatch(pp kyverno.Patch) error {
|
||||||
return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation)
|
return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateValidation(v kyverno.Validation) []error {
|
func validateValidation(v kyverno.Validation) (string, error) {
|
||||||
var errs []error
|
|
||||||
|
|
||||||
if err := validateOverlayPattern(v); err != nil {
|
if err := validateOverlayPattern(v); err != nil {
|
||||||
errs = append(errs, err)
|
// no need to proceed ahead
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Pattern != nil {
|
if v.Pattern != nil {
|
||||||
if _, err := validateAnchors([]anchor{conditionalAnchor, existingAnchor, equalityAnchor, negationAnchor}, v.Pattern, "/"); err != nil {
|
if path, err := validatePattern(v.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistanceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
||||||
errs = append(errs, err)
|
return fmt.Sprintf("pattern.%s", path), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.AnyPattern) != 0 {
|
if len(v.AnyPattern) != 0 {
|
||||||
for _, p := range v.AnyPattern {
|
for i, pattern := range v.AnyPattern {
|
||||||
if _, err := validateAnchors([]anchor{conditionalAnchor, existingAnchor, equalityAnchor, negationAnchor}, p, "/"); err != nil {
|
if path, err := validatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistanceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
||||||
errs = append(errs, err)
|
return fmt.Sprintf("anyPattern[%d].%s", i, path), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return "", nil
|
||||||
return errs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateOverlayPattern checks one of pattern/anyPattern must exist
|
// validateOverlayPattern checks one of pattern/anyPattern must exist
|
||||||
func validateOverlayPattern(v kyverno.Validation) error {
|
func validateOverlayPattern(v kyverno.Validation) error {
|
||||||
if reflect.DeepEqual(v, kyverno.Validation{}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Pattern == nil && len(v.AnyPattern) == 0 {
|
if v.Pattern == nil && len(v.AnyPattern) == 0 {
|
||||||
return fmt.Errorf("neither pattern nor anyPattern found")
|
return fmt.Errorf("a pattern or anyPattern must be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Pattern != nil && len(v.AnyPattern) != 0 {
|
if v.Pattern != nil && len(v.AnyPattern) != 0 {
|
||||||
return fmt.Errorf("either pattern or anyPattern is allowed")
|
return fmt.Errorf("only one operation allowed per validation rule(pattern or anyPattern)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns error if generator is configured incompletely
|
// Validate returns error if generator is configured incompletely
|
||||||
func validateGeneration(gen kyverno.Generation) error {
|
func validateGeneration(gen kyverno.Generation) (string, error) {
|
||||||
if reflect.DeepEqual(gen, kyverno.Generation{}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if gen.Data == nil && gen.Clone == (kyverno.CloneFrom{}) {
|
if gen.Data == nil && gen.Clone == (kyverno.CloneFrom{}) {
|
||||||
return fmt.Errorf("neither data nor clone (source) of %s is specified", gen.Kind)
|
return "", fmt.Errorf("clone or data are required")
|
||||||
}
|
}
|
||||||
if gen.Data != nil && gen.Clone != (kyverno.CloneFrom{}) {
|
if gen.Data != nil && gen.Clone != (kyverno.CloneFrom{}) {
|
||||||
return fmt.Errorf("both data nor clone (source) of %s are specified", gen.Kind)
|
return "", fmt.Errorf("only one operation allowed per generate rule(data or clone)")
|
||||||
}
|
}
|
||||||
|
// check kind is non empty
|
||||||
if gen.Data != nil {
|
// check name is non empty
|
||||||
if _, err := validateAnchors(nil, gen.Data, "/"); err != nil {
|
if gen.Name == "" {
|
||||||
return fmt.Errorf("anchors are not allowed on generate pattern data: %v", err)
|
return "name", fmt.Errorf("name cannot be empty")
|
||||||
}
|
}
|
||||||
|
if gen.Kind == "" {
|
||||||
|
return "kind", fmt.Errorf("kind cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(gen.Clone, kyverno.CloneFrom{}) {
|
if !reflect.DeepEqual(gen.Clone, kyverno.CloneFrom{}) {
|
||||||
if _, err := validateAnchors(nil, gen.Clone, ""); err != nil {
|
if path, err := validateClone(gen.Clone); err != nil {
|
||||||
return fmt.Errorf("invalid character found on pattern clone: %v", err)
|
return fmt.Sprintf("clone.%s", path), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if gen.Data != nil {
|
||||||
return nil
|
//TODO: is this required ?? as anchors can only be on pattern and not resource
|
||||||
|
// we can add this check by not sure if its needed here
|
||||||
|
if path, err := validatePattern(gen.Data, "/", []anchor.IsAnchor{}); err != nil {
|
||||||
|
return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resoruces: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateAnchors validates:
|
func validateClone(c kyverno.CloneFrom) (string, error) {
|
||||||
// 1. existing acnchor must define on array
|
if c.Name == "" {
|
||||||
// 2. anchors in mutation must be one of: (), +()
|
return "name", fmt.Errorf("name cannot be empty")
|
||||||
// 3. anchors in validate must be one of: (), ^(), =(), X()
|
}
|
||||||
// 4. no anchor is allowed in generate
|
if c.Namespace == "" {
|
||||||
func validateAnchors(anchorPatterns []anchor, pattern interface{}, path string) (string, error) {
|
return "namespace", fmt.Errorf("namespace cannot be empty")
|
||||||
switch typedPattern := pattern.(type) {
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePattern(patternElement interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
|
switch typedPatternElement := patternElement.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
return validateAnchorsOnMap(anchorPatterns, typedPattern, path)
|
return validateMap(typedPatternElement, path, supportedAnchors)
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
return validateAnchorsOnArray(anchorPatterns, typedPattern, path)
|
return validateArray(typedPatternElement, path, supportedAnchors)
|
||||||
case string, float64, int, int64, bool, nil:
|
case string, float64, int, int64, bool, nil:
|
||||||
// check on type string
|
//TODO? check operator
|
||||||
if checkedPattern := reflect.ValueOf(pattern); checkedPattern.Kind() == reflect.String {
|
|
||||||
if hasAnchor, str := hasExistingAnchor(checkedPattern.String()); hasAnchor {
|
|
||||||
return path, fmt.Errorf("existing anchor at %s must be of type array, found: %v", path+str, checkedPattern.Kind())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return nil on all other cases
|
|
||||||
return "", nil
|
|
||||||
case interface{}:
|
|
||||||
// special case for generate clone, as it is a struct
|
|
||||||
if clone, ok := pattern.(kyverno.CloneFrom); ok {
|
|
||||||
return "", validateAnchorsOnCloneFrom(nil, clone)
|
|
||||||
}
|
|
||||||
return "", nil
|
return "", nil
|
||||||
default:
|
default:
|
||||||
glog.V(4).Infof("Pattern contains unknown type %T. Path: %s", pattern, path)
|
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
|
||||||
return path, fmt.Errorf("pattern contains unknown type, path: %s", path)
|
|
||||||
}
|
}
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateAnchorsOnCloneFrom(anchorPatterns []anchor, pattern kyverno.CloneFrom) error {
|
func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
// namespace and name are required fields
|
// check if anchors are defined
|
||||||
// if wrapped with invalid character, this field is empty during unmarshaling
|
for key, value := range patternMap {
|
||||||
if pattern.Namespace == "" {
|
// if key is anchor
|
||||||
return errors.New("namespace is requried")
|
// check regex () -> this is anchor
|
||||||
}
|
// ()
|
||||||
|
// single char ()
|
||||||
if pattern.Name == "" {
|
matched, err := regexp.MatchString(`^.?\(.+\)$`, key)
|
||||||
return errors.New("name is requried")
|
if err != nil {
|
||||||
}
|
return path + "/" + key, fmt.Errorf("Unable to parse the field %s: %v", key, err)
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateAnchorsOnMap(anchorPatterns []anchor, pattern map[string]interface{}, path string) (string, error) {
|
|
||||||
for key, patternElement := range pattern {
|
|
||||||
if valid, str := hasValidAnchors(anchorPatterns, key); !valid {
|
|
||||||
return path, fmt.Errorf("invalid anchor found at %s, expect: %s", path+str, joinAnchors(anchorPatterns))
|
|
||||||
}
|
}
|
||||||
if hasAnchor, str := hasExistingAnchor(key); hasAnchor {
|
// check the type of anchor
|
||||||
if checkedPattern := reflect.ValueOf(patternElement); checkedPattern.Kind() != reflect.Slice {
|
if matched {
|
||||||
return path, fmt.Errorf("existing anchor at %s must be of type array, found: %T", path+str, patternElement)
|
// some type of anchor
|
||||||
|
// check if valid anchor
|
||||||
|
if !checkAnchors(key, supportedAnchors) {
|
||||||
|
return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addition check for existance anchor
|
||||||
|
// value must be of type list
|
||||||
|
if anchor.IsExistanceAnchor(key) {
|
||||||
|
typedValue, ok := value.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return path + "/" + key, fmt.Errorf("Existance anchor should have value of type list")
|
||||||
|
}
|
||||||
|
// validate there is only one entry in the list
|
||||||
|
if len(typedValue) == 0 || len(typedValue) > 1 {
|
||||||
|
return path + "/" + key, fmt.Errorf("Existance anchor: single value expected, multiple specified")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// lets validate the values now :)
|
||||||
if path, err := validateAnchors(anchorPatterns, patternElement, path+key+"/"); err != nil {
|
if errPath, err := validatePattern(value, path+"/"+key, supportedAnchors); err != nil {
|
||||||
return path, err
|
return errPath, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateAnchorsOnArray(anchorPatterns []anchor, patternArray []interface{}, path string) (string, error) {
|
func validateArray(patternArray []interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
if len(patternArray) == 0 {
|
for i, patternElement := range patternArray {
|
||||||
return path, fmt.Errorf("pattern array at %s is empty", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, pattern := range patternArray {
|
|
||||||
currentPath := path + strconv.Itoa(i) + "/"
|
currentPath := path + strconv.Itoa(i) + "/"
|
||||||
if path, err := validateAnchors(anchorPatterns, pattern, currentPath); err != nil {
|
// lets validate the values now :)
|
||||||
return path, err
|
if errPath, err := validatePattern(patternElement, currentPath, supportedAnchors); err != nil {
|
||||||
|
return errPath, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkAnchors(key string, supportedAnchors []anchor.IsAnchor) bool {
|
||||||
|
for _, f := range supportedAnchors {
|
||||||
|
if f(key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
"github.com/nirmata/kyverno/pkg/utils"
|
"github.com/nirmata/kyverno/pkg/utils"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
|
||||||
|
@ -209,11 +210,12 @@ func ParseNamespaceFromObject(bytes []byte) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validation
|
||||||
func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
|
func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
for key, value := range anchorsMap {
|
for key, value := range anchorsMap {
|
||||||
if isConditionAnchor(key) || isExistanceAnchor(key) {
|
if anchor.IsConditionAnchor(key) || anchor.IsExistanceAnchor(key) {
|
||||||
result[key] = value
|
result[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,13 +223,14 @@ func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mutation
|
||||||
func getElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
|
func getElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
|
||||||
anchors := make(map[string]interface{})
|
anchors := make(map[string]interface{})
|
||||||
elementsWithoutanchor := make(map[string]interface{})
|
elementsWithoutanchor := make(map[string]interface{})
|
||||||
for key, value := range anchorsMap {
|
for key, value := range anchorsMap {
|
||||||
if isConditionAnchor(key) || isExistanceAnchor(key) {
|
if anchor.IsConditionAnchor(key) || anchor.IsExistanceAnchor(key) {
|
||||||
anchors[key] = value
|
anchors[key] = value
|
||||||
} else if !isAddingAnchor(key) {
|
} else if !anchor.IsAddingAnchor(key) {
|
||||||
elementsWithoutanchor[key] = value
|
elementsWithoutanchor[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,7 +240,7 @@ func getElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface
|
||||||
|
|
||||||
func getAnchorFromMap(anchorsMap map[string]interface{}) (string, interface{}) {
|
func getAnchorFromMap(anchorsMap map[string]interface{}) (string, interface{}) {
|
||||||
for key, value := range anchorsMap {
|
for key, value := range anchorsMap {
|
||||||
if isConditionAnchor(key) || isExistanceAnchor(key) {
|
if anchor.IsConditionAnchor(key) || anchor.IsExistanceAnchor(key) {
|
||||||
return key, value
|
return key, value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,13 +257,13 @@ func findKind(kinds []string, kindGVK string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isConditionAnchor(str string) bool {
|
// func isConditionAnchor(str string) bool {
|
||||||
if len(str) < 2 {
|
// if len(str) < 2 {
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
|
|
||||||
return (str[0] == '(' && str[len(str)-1] == ')')
|
// return (str[0] == '(' && str[len(str)-1] == ')')
|
||||||
}
|
// }
|
||||||
|
|
||||||
func getRawKeyIfWrappedWithAttributes(str string) string {
|
func getRawKeyIfWrappedWithAttributes(str string) string {
|
||||||
if len(str) < 2 {
|
if len(str) < 2 {
|
||||||
|
@ -284,48 +287,6 @@ func isStringIsReference(str string) bool {
|
||||||
return str[0] == '$' && str[1] == '(' && str[len(str)-1] == ')'
|
return str[0] == '$' && str[1] == '(' && str[len(str)-1] == ')'
|
||||||
}
|
}
|
||||||
|
|
||||||
func isExistanceAnchor(str string) bool {
|
|
||||||
left := "^("
|
|
||||||
right := ")"
|
|
||||||
|
|
||||||
if len(str) < len(left)+len(right) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEqualityAnchor(str string) bool {
|
|
||||||
left := "=("
|
|
||||||
right := ")"
|
|
||||||
if len(str) < len(left)+len(right) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
//TODO: trim spaces ?
|
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNegationAnchor(str string) bool {
|
|
||||||
left := "X("
|
|
||||||
right := ")"
|
|
||||||
if len(str) < len(left)+len(right) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
//TODO: trim spaces ?
|
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAddingAnchor(key string) bool {
|
|
||||||
const left = "+("
|
|
||||||
const right = ")"
|
|
||||||
|
|
||||||
if len(key) < len(left)+len(right) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return left == key[:len(left)] && right == key[len(key)-len(right):]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if array object matches anchors. If not - skip - return true
|
// Checks if array object matches anchors. If not - skip - return true
|
||||||
func skipArrayObject(object, anchors map[string]interface{}) bool {
|
func skipArrayObject(object, anchors map[string]interface{}) bool {
|
||||||
for key, pattern := range anchors {
|
for key, pattern := range anchors {
|
||||||
|
@ -346,11 +307,11 @@ func skipArrayObject(object, anchors map[string]interface{}) bool {
|
||||||
|
|
||||||
// removeAnchor remove special characters around anchored key
|
// removeAnchor remove special characters around anchored key
|
||||||
func removeAnchor(key string) string {
|
func removeAnchor(key string) string {
|
||||||
if isConditionAnchor(key) {
|
if anchor.IsConditionAnchor(key) {
|
||||||
return key[1 : len(key)-1]
|
return key[1 : len(key)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if isExistanceAnchor(key) || isAddingAnchor(key) || isEqualityAnchor(key) || isNegationAnchor(key) {
|
if anchor.IsExistanceAnchor(key) || anchor.IsAddingAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
|
||||||
return key[2 : len(key)-1]
|
return key[2 : len(key)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -392,57 +392,6 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
|
||||||
assert.Assert(t, !MatchesResourceDescription(*resource, rule))
|
assert.Assert(t, !MatchesResourceDescription(*resource, rule))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrappedWithParentheses_StringIsWrappedWithParentheses(t *testing.T) {
|
|
||||||
str := "(something)"
|
|
||||||
assert.Assert(t, isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedWithParentheses_StringHasOnlyParentheses(t *testing.T) {
|
|
||||||
str := "()"
|
|
||||||
assert.Assert(t, isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedWithParentheses_StringHasNoParentheses(t *testing.T) {
|
|
||||||
str := "something"
|
|
||||||
assert.Assert(t, !isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedWithParentheses_StringHasLeftParentheses(t *testing.T) {
|
|
||||||
str := "(something"
|
|
||||||
assert.Assert(t, !isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedWithParentheses_StringHasRightParentheses(t *testing.T) {
|
|
||||||
str := "something)"
|
|
||||||
assert.Assert(t, !isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedWithParentheses_StringParenthesesInside(t *testing.T) {
|
|
||||||
str := "so)m(et(hin)g"
|
|
||||||
assert.Assert(t, !isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedWithParentheses_Empty(t *testing.T) {
|
|
||||||
str := ""
|
|
||||||
assert.Assert(t, !isConditionAnchor(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsExistanceAnchor_Yes(t *testing.T) {
|
|
||||||
assert.Assert(t, isExistanceAnchor("^(abc)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsExistanceAnchor_NoRightBracket(t *testing.T) {
|
|
||||||
assert.Assert(t, !isExistanceAnchor("^(abc"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsExistanceAnchor_OnlyHat(t *testing.T) {
|
|
||||||
assert.Assert(t, !isExistanceAnchor("^abc"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsExistanceAnchor_ConditionAnchor(t *testing.T) {
|
|
||||||
assert.Assert(t, !isExistanceAnchor("(abc)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveAnchor_ConditionAnchor(t *testing.T) {
|
func TestRemoveAnchor_ConditionAnchor(t *testing.T) {
|
||||||
assert.Equal(t, removeAnchor("(abc)"), "abc")
|
assert.Equal(t, removeAnchor("(abc)"), "abc")
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,10 +42,9 @@ func Validate(policy kyverno.ClusterPolicy, resource unstructured.Unstructured)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range policy.Spec.Rules {
|
for _, rule := range policy.Spec.Rules {
|
||||||
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
if !rule.HasValidate() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the resource satisfies the filter conditions defined in the rule
|
// check if the resource satisfies the filter conditions defined in the rule
|
||||||
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||||
// dont statisfy a policy rule resource description
|
// dont statisfy a policy rule resource description
|
||||||
|
@ -201,7 +201,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, origPattern int
|
||||||
// but if there are non then its a if then check
|
// but if there are non then its a if then check
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If Conditional anchor fails then we dont process the resources
|
// If Conditional anchor fails then we dont process the resources
|
||||||
if isConditionAnchor(key) {
|
if anchor.IsConditionAnchor(key) {
|
||||||
glog.V(4).Infof("condition anchor did not satisfy, wont process the resources: %s", err)
|
glog.V(4).Infof("condition anchor did not satisfy, wont process the resources: %s", err)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue