mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
rebase with 28 stateless policy engine
This commit is contained in:
commit
8d5ae48c30
7 changed files with 441 additions and 94 deletions
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
client "github.com/nirmata/kube-policy/client"
|
client "github.com/nirmata/kube-policy/client"
|
||||||
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||||
"github.com/nirmata/kube-policy/pkg/engine/mutation"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +29,7 @@ func Generate(client *client.Client, logger *log.Logger, policy kubepolicy.Polic
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := mutation.ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk)
|
ok, err := ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err)
|
logger.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err)
|
||||||
continue
|
continue
|
||||||
|
@ -60,7 +59,7 @@ func applyRuleGenerator(client *client.Client, rawResource []byte, generator *ku
|
||||||
return fmt.Errorf("Generator for '%s' is invalid: %s", generator.Kind, err)
|
return fmt.Errorf("Generator for '%s' is invalid: %s", generator.Kind, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespaceName := mutation.ParseNameFromObject(rawResource)
|
namespaceName := ParseNameFromObject(rawResource)
|
||||||
// Generate the resource
|
// Generate the resource
|
||||||
switch gvk.Kind {
|
switch gvk.Kind {
|
||||||
case "configmap":
|
case "configmap":
|
||||||
|
|
|
@ -26,7 +26,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := mutation.ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk)
|
ok, err := ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err)
|
log.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package mutation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func assertEqDataImpl(t *testing.T, expected, actual []byte, formatModifier string) {
|
|
||||||
if len(expected) != len(actual) {
|
|
||||||
t.Errorf("len(expected) != len(actual): %d != %d\n1:"+formatModifier+"\n2:"+formatModifier, len(expected), len(actual), expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, val := range actual {
|
|
||||||
if val != expected[idx] {
|
|
||||||
t.Errorf("Slices not equal at index %d:\n1:"+formatModifier+"\n2:"+formatModifier, idx, expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertEqData(t *testing.T, expected, actual []byte) {
|
|
||||||
assertEqDataImpl(t, expected, actual, "%x")
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertEqStringAndData(t *testing.T, str string, data []byte) {
|
|
||||||
assertEqDataImpl(t, []byte(str), data, "%s")
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package mutation
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,10 +6,46 @@ import (
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ResourceMeetsRules checks requests kind, name and labels to fit the policy
|
||||||
|
func ResourceMeetsRules(resourceRaw []byte, description kubepolicy.ResourceDescription, gvk metav1.GroupVersionKind) (bool, error) {
|
||||||
|
if description.Kind != gvk.Kind {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if resourceRaw != nil {
|
||||||
|
meta := ParseMetadataFromObject(resourceRaw)
|
||||||
|
name := ParseNameFromObject(resourceRaw)
|
||||||
|
|
||||||
|
if description.Name != nil {
|
||||||
|
|
||||||
|
if !wildcard.Match(*description.Name, name) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if description.Selector != nil {
|
||||||
|
selector, err := metav1.LabelSelectorAsSelector(description.Selector)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
labelMap := ParseLabelsFromMetadata(meta)
|
||||||
|
|
||||||
|
if !selector.Matches(labelMap) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ParseMetadataFromObject(bytes []byte) map[string]interface{} {
|
func ParseMetadataFromObject(bytes []byte) map[string]interface{} {
|
||||||
var objectJSON map[string]interface{}
|
var objectJSON map[string]interface{}
|
||||||
json.Unmarshal(bytes, &objectJSON)
|
json.Unmarshal(bytes, &objectJSON)
|
||||||
|
@ -68,38 +104,3 @@ func ParseRegexPolicyResourceName(policyResourceName string) (string, bool) {
|
||||||
}
|
}
|
||||||
return strings.Trim(regex[1], " "), true
|
return strings.Trim(regex[1], " "), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceMeetsRules checks requests kind, name and labels to fit the policy
|
|
||||||
func ResourceMeetsRules(resourceRaw []byte, description kubepolicy.ResourceDescription, gvk metav1.GroupVersionKind) (bool, error) {
|
|
||||||
if description.Kind != gvk.Kind {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if resourceRaw != nil {
|
|
||||||
meta := ParseMetadataFromObject(resourceRaw)
|
|
||||||
name := ParseNameFromObject(resourceRaw)
|
|
||||||
|
|
||||||
if description.Name != nil {
|
|
||||||
|
|
||||||
if !wildcard.Match(*description.Name, name) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if description.Selector != nil {
|
|
||||||
selector, err := metav1.LabelSelectorAsSelector(description.Selector)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
labelMap := ParseLabelsFromMetadata(meta)
|
|
||||||
|
|
||||||
if !selector.Matches(labelMap) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
|
@ -5,11 +5,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
|
|
||||||
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||||
"github.com/nirmata/kube-policy/pkg/engine/mutation"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
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) bool {
|
func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) bool {
|
||||||
var resource interface{}
|
var resource interface{}
|
||||||
json.Unmarshal(rawResource, &resource)
|
json.Unmarshal(rawResource, &resource)
|
||||||
|
@ -28,7 +31,7 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := mutation.ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk)
|
ok, err := ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err)
|
log.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err)
|
||||||
continue
|
continue
|
||||||
|
@ -43,60 +46,196 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := traverseAndValidate(resource, rule.Validation.Pattern); err != nil {
|
if !validateMap(resource, rule.Validation.Pattern) {
|
||||||
log.Printf("Validation with the rule %s has failed %s: %s\n", rule.Name, err.Error(), *rule.Validation.Message)
|
log.Printf("Validation with the rule %s has failed: %s\n", rule.Name, *rule.Validation.Message)
|
||||||
allowed = false
|
allowed = false
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Validation rule %s is successful %s: %s\n", rule.Name, err.Error(), *rule.Validation.Message)
|
log.Printf("Validation rule %s is successful\n", rule.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allowed
|
return allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseAndValidate(resourcePart, patternPart interface{}) error {
|
func validateMap(resourcePart, patternPart interface{}) bool {
|
||||||
|
pattern := patternPart.(map[string]interface{})
|
||||||
|
resource, ok := resourcePart.(map[string]interface{})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("Validating error: expected Map, found %T\n", resourcePart)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range pattern {
|
||||||
|
if wrappedWithParentheses(key) {
|
||||||
|
key = key[1 : len(key)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validateMapElement(resource[key], value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateArray(resourcePart, patternPart interface{}) bool {
|
||||||
|
patternArray := patternPart.([]interface{})
|
||||||
|
resourceArray, ok := resourcePart.([]interface{})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("Validating error: expected array, found %T\n", resourcePart)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pattern := patternArray[0].(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
anchors, err := getAnchorsFromMap(pattern)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Validating error: %v\n", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range resourceArray {
|
||||||
|
resource, ok := value.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("Validating error: expected Map, found %T\n", resourcePart)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipArrayObject(resource, anchors) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validateMap(resource, pattern) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
for _, value := range resourceArray {
|
||||||
|
if !checkSingleValue(value, patternArray[0]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateMapElement(resourcePart, patternPart interface{}) bool {
|
||||||
switch pattern := patternPart.(type) {
|
switch pattern := patternPart.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
dictionary, ok := resourcePart.(map[string]interface{})
|
dictionary, ok := resourcePart.(map[string]interface{})
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Validating error: expected %T, found %T", patternPart, resourcePart)
|
fmt.Printf("Validating error: expected %T, found %T\n", patternPart, resourcePart)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
return validateMap(dictionary, pattern)
|
||||||
for key, value := range pattern {
|
|
||||||
err = traverseAndValidate(dictionary[key], value)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
|
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
array, ok := resourcePart.([]interface{})
|
array, ok := resourcePart.([]interface{})
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Validating error: expected %T, found %T", patternPart, resourcePart)
|
fmt.Printf("Validating error: expected %T, found %T\n", patternPart, resourcePart)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
return validateArray(array, pattern)
|
||||||
for i, value := range pattern {
|
|
||||||
err = traverseAndValidate(array[i], value)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
case string:
|
case string:
|
||||||
str := resourcePart.(string)
|
str, ok := resourcePart.(string)
|
||||||
if !checkForWildcard(str, pattern) {
|
|
||||||
return fmt.Errorf("Value %s has not passed wildcard check %s", str, pattern)
|
if !ok {
|
||||||
|
fmt.Printf("Validating error: expected %T, found %T\n", patternPart, resourcePart)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return checkSingleValue(str, pattern)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Received unknown type: %T", patternPart)
|
fmt.Printf("Validating error: unknown type in map: %T\n", patternPart)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAnchorsFromMap(pattern map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
for key, value := range pattern {
|
||||||
|
if wrappedWithParentheses(key) {
|
||||||
|
result[key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipArrayObject(object, anchors map[string]interface{}) bool {
|
||||||
|
for key, pattern := range anchors {
|
||||||
|
key = key[1 : len(key)-1]
|
||||||
|
|
||||||
|
value, ok := object[key]
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkSingleValue(value, pattern) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSingleValue(value, pattern interface{}) bool {
|
||||||
|
switch typedPattern := pattern.(type) {
|
||||||
|
case string:
|
||||||
|
switch typedValue := value.(type) {
|
||||||
|
case string:
|
||||||
|
return checkForWildcard(typedValue, typedPattern)
|
||||||
|
case float64:
|
||||||
|
return checkForOperator(typedValue, typedPattern)
|
||||||
|
case int:
|
||||||
|
return checkForOperator(float64(typedValue), typedPattern)
|
||||||
|
default:
|
||||||
|
fmt.Printf("Validating error: expected string or numerical type, found %T, pattern: %s\n", value, typedPattern)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
num, ok := value.(float64)
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("Validating error: expected float, found %T\n", value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return typedPattern == num
|
||||||
|
case int:
|
||||||
|
num, ok := value.(int)
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("Validating error: expected int, found %T\n", value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return typedPattern == num
|
||||||
|
default:
|
||||||
|
fmt.Printf("Validating error: expected pattern (string or numerical type), found %T\n", pattern)
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForWildcard(value, pattern string) bool {
|
func checkForWildcard(value, pattern string) bool {
|
||||||
return value == pattern
|
return wildcard.Match(pattern, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForOperator(value int, pattern string) bool {
|
func checkForOperator(value float64, pattern string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrappedWithParentheses(str string) bool {
|
||||||
|
if len(str) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (str[0] == '(' && str[len(str)-1] == ')')
|
||||||
|
}
|
||||||
|
|
234
pkg/engine/validation_test.go
Normal file
234
pkg/engine/validation_test.go
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringIsWrappedWithParentheses(t *testing.T) {
|
||||||
|
str := "(something)"
|
||||||
|
assert.Assert(t, wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasOnlyParentheses(t *testing.T) {
|
||||||
|
str := "()"
|
||||||
|
assert.Assert(t, wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasNoParentheses(t *testing.T) {
|
||||||
|
str := "something"
|
||||||
|
assert.Assert(t, !wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasLeftParentheses(t *testing.T) {
|
||||||
|
str := "(something"
|
||||||
|
assert.Assert(t, !wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringHasRightParentheses(t *testing.T) {
|
||||||
|
str := "something)"
|
||||||
|
assert.Assert(t, !wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_StringParenthesesInside(t *testing.T) {
|
||||||
|
str := "so)m(et(hin)g"
|
||||||
|
assert.Assert(t, !wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrappedWithParentheses_Empty(t *testing.T) {
|
||||||
|
str := ""
|
||||||
|
assert.Assert(t, !wrappedWithParentheses(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckForWildcard_AsteriskTest(t *testing.T) {
|
||||||
|
pattern := "*"
|
||||||
|
value := "anything"
|
||||||
|
empty := ""
|
||||||
|
|
||||||
|
assert.Assert(t, checkForWildcard(value, pattern))
|
||||||
|
assert.Assert(t, checkForWildcard(empty, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckForWildcard_LeftAsteriskTest(t *testing.T) {
|
||||||
|
pattern := "*right"
|
||||||
|
value := "leftright"
|
||||||
|
right := "right"
|
||||||
|
|
||||||
|
assert.Assert(t, checkForWildcard(value, pattern))
|
||||||
|
assert.Assert(t, checkForWildcard(right, pattern))
|
||||||
|
|
||||||
|
value = "leftmiddle"
|
||||||
|
middle := "middle"
|
||||||
|
|
||||||
|
assert.Assert(t, !checkForWildcard(value, pattern))
|
||||||
|
assert.Assert(t, !checkForWildcard(middle, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckForWildcard_MiddleAsteriskTest(t *testing.T) {
|
||||||
|
pattern := "ab*ba"
|
||||||
|
value := "abbba"
|
||||||
|
assert.Assert(t, checkForWildcard(value, pattern))
|
||||||
|
|
||||||
|
value = "abbca"
|
||||||
|
assert.Assert(t, !checkForWildcard(value, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckForWildcard_QuestionMark(t *testing.T) {
|
||||||
|
pattern := "ab?ba"
|
||||||
|
value := "abbba"
|
||||||
|
assert.Assert(t, checkForWildcard(value, pattern))
|
||||||
|
|
||||||
|
value = "abbbba"
|
||||||
|
assert.Assert(t, !checkForWildcard(value, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckSingleValue_CheckInt(t *testing.T) {
|
||||||
|
pattern := 89
|
||||||
|
value := 89
|
||||||
|
assert.Assert(t, checkSingleValue(value, pattern))
|
||||||
|
|
||||||
|
value = 202
|
||||||
|
assert.Assert(t, !checkSingleValue(value, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckSingleValue_CheckFloat(t *testing.T) {
|
||||||
|
pattern := 89.9091
|
||||||
|
value := 89.9091
|
||||||
|
assert.Assert(t, checkSingleValue(value, pattern))
|
||||||
|
|
||||||
|
value = 89.9092
|
||||||
|
assert.Assert(t, !checkSingleValue(value, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckSingleValue_CheckOperatorMore(t *testing.T) {
|
||||||
|
pattern := ">10"
|
||||||
|
value := 89
|
||||||
|
assert.Assert(t, checkSingleValue(value, pattern))
|
||||||
|
|
||||||
|
pattern = ">10"
|
||||||
|
floatValue := 89.901
|
||||||
|
assert.Assert(t, checkSingleValue(floatValue, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckSingleValue_CheckWildcard(t *testing.T) {
|
||||||
|
pattern := "nirmata_*"
|
||||||
|
value := "nirmata_awesome"
|
||||||
|
assert.Assert(t, checkSingleValue(value, pattern))
|
||||||
|
|
||||||
|
pattern = "nirmata_*"
|
||||||
|
value = "spasex_awesome"
|
||||||
|
assert.Assert(t, !checkSingleValue(value, pattern))
|
||||||
|
|
||||||
|
pattern = "g?t"
|
||||||
|
value = "git"
|
||||||
|
assert.Assert(t, checkSingleValue(value, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkipArrayObject_OneAnchor(t *testing.T) {
|
||||||
|
|
||||||
|
rawAnchors := []byte(`{"(name)": "nirmata-*"}`)
|
||||||
|
rawResource := []byte(`{"name": "nirmata-resource", "namespace": "kube-policy", "object": { "label": "app", "array": [ 1, 2, 3 ]}}`)
|
||||||
|
|
||||||
|
var resource, anchor map[string]interface{}
|
||||||
|
|
||||||
|
json.Unmarshal(rawAnchors, &anchor)
|
||||||
|
json.Unmarshal(rawResource, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, !skipArrayObject(resource, anchor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkipArrayObject_OneNumberAnchorPass(t *testing.T) {
|
||||||
|
|
||||||
|
rawAnchors := []byte(`{"(count)": 1}`)
|
||||||
|
rawResource := []byte(`{"name": "nirmata-resource", "count": 1, "namespace": "kube-policy", "object": { "label": "app", "array": [ 1, 2, 3 ]}}`)
|
||||||
|
|
||||||
|
var resource, anchor map[string]interface{}
|
||||||
|
|
||||||
|
json.Unmarshal(rawAnchors, &anchor)
|
||||||
|
json.Unmarshal(rawResource, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, !skipArrayObject(resource, anchor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkipArrayObject_TwoAnchorsPass(t *testing.T) {
|
||||||
|
rawAnchors := []byte(`{"(name)": "nirmata-*", "(namespace)": "kube-?olicy"}`)
|
||||||
|
rawResource := []byte(`{"name": "nirmata-resource", "namespace": "kube-policy", "object": { "label": "app", "array": [ 1, 2, 3 ]}}`)
|
||||||
|
|
||||||
|
var resource, anchor map[string]interface{}
|
||||||
|
|
||||||
|
json.Unmarshal(rawAnchors, &anchor)
|
||||||
|
json.Unmarshal(rawResource, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, !skipArrayObject(resource, anchor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkipArrayObject_TwoAnchorsSkip(t *testing.T) {
|
||||||
|
rawAnchors := []byte(`{"(name)": "nirmata-*", "(namespace)": "some-?olicy"}`)
|
||||||
|
rawResource := []byte(`{"name": "nirmata-resource", "namespace": "kube-policy", "object": { "label": "app", "array": [ 1, 2, 3 ]}}`)
|
||||||
|
|
||||||
|
var resource, anchor map[string]interface{}
|
||||||
|
|
||||||
|
json.Unmarshal(rawAnchors, &anchor)
|
||||||
|
json.Unmarshal(rawResource, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, skipArrayObject(resource, anchor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAnchorsFromMap_ThereAreAnchors(t *testing.T) {
|
||||||
|
rawMap := []byte(`{"(name)": "nirmata-*", "notAnchor1": 123, "(namespace)": "kube-?olicy", "notAnchor2": "sample-text", "object": { "key1": "value1", "(key2)": "value2"}}`)
|
||||||
|
|
||||||
|
var unmarshalled map[string]interface{}
|
||||||
|
json.Unmarshal(rawMap, &unmarshalled)
|
||||||
|
|
||||||
|
actualMap, err := getAnchorsFromMap(unmarshalled)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, len(actualMap), 2)
|
||||||
|
assert.Equal(t, actualMap["(name)"].(string), "nirmata-*")
|
||||||
|
assert.Equal(t, actualMap["(namespace)"].(string), "kube-?olicy")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAnchorsFromMap_ThereAreNoAnchors(t *testing.T) {
|
||||||
|
rawMap := []byte(`{"name": "nirmata-*", "notAnchor1": 123, "namespace": "kube-?olicy", "notAnchor2": "sample-text", "object": { "key1": "value1", "(key2)": "value2"}}`)
|
||||||
|
|
||||||
|
var unmarshalled map[string]interface{}
|
||||||
|
json.Unmarshal(rawMap, &unmarshalled)
|
||||||
|
|
||||||
|
actualMap, err := getAnchorsFromMap(unmarshalled)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Assert(t, len(actualMap) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateMapElement_TwoElementsInArrayOnePass(t *testing.T) {
|
||||||
|
rawPattern := []byte(`[ { "(name)": "nirmata-*", "object": [ { "(key1)": "value*", "key2": "value*" } ] } ]`)
|
||||||
|
rawMap := []byte(`[ { "name": "nirmata-1", "object": [ { "key1": "value1", "key2": "value2" } ] }, { "name": "nirmata-1", "object": [ { "key1": "not_value", "key2": "not_value" } ] } ]`)
|
||||||
|
|
||||||
|
var pattern, resource interface{}
|
||||||
|
json.Unmarshal(rawPattern, &pattern)
|
||||||
|
json.Unmarshal(rawMap, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, validateMapElement(resource, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateMapElement_OneElementInArrayPass(t *testing.T) {
|
||||||
|
rawPattern := []byte(`[ { "(name)": "nirmata-*", "object": [ { "(key1)": "value*", "key2": "value*" } ] } ]`)
|
||||||
|
rawMap := []byte(`[ { "name": "nirmata-1", "object": [ { "key1": "value1", "key2": "value2" } ] } ]`)
|
||||||
|
|
||||||
|
var pattern, resource interface{}
|
||||||
|
json.Unmarshal(rawPattern, &pattern)
|
||||||
|
json.Unmarshal(rawMap, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, validateMapElement(resource, pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
|
||||||
|
rawPattern := []byte(`[{"(name)": "nirmata-*", "object":[{"(key1)": "value*", "key2": "value*"}]}]`)
|
||||||
|
rawMap := []byte(`[ { "name": "nirmata-1", "object": [ { "key1": "value5", "key2": "1value1" } ] } ]`)
|
||||||
|
|
||||||
|
var pattern, resource interface{}
|
||||||
|
json.Unmarshal(rawPattern, &pattern)
|
||||||
|
json.Unmarshal(rawMap, &resource)
|
||||||
|
|
||||||
|
assert.Assert(t, !validateMapElement(resource, pattern))
|
||||||
|
}
|
|
@ -148,8 +148,8 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
||||||
allPatches = append(allPatches, policyPatches...)
|
allPatches = append(allPatches, policyPatches...)
|
||||||
|
|
||||||
if len(policyPatches) > 0 {
|
if len(policyPatches) > 0 {
|
||||||
namespace := mutation.ParseNamespaceFromObject(request.Object.Raw)
|
namespace := engine.ParseNamespaceFromObject(request.Object.Raw)
|
||||||
name := mutation.ParseNameFromObject(request.Object.Raw)
|
name := engine.ParseNameFromObject(request.Object.Raw)
|
||||||
ws.logger.Printf("Policy %s applied to %s %s/%s", policy.Name, request.Kind.Kind, namespace, name)
|
ws.logger.Printf("Policy %s applied to %s %s/%s", policy.Name, request.Kind.Kind, namespace, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue