1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 10:55:05 +00:00

move mutation to subpackage pkg/engine/mutate

This commit is contained in:
Shuting Zhao 2020-01-07 17:06:17 -08:00
parent 3cf9141f4d
commit 472fa29fce
22 changed files with 283 additions and 269 deletions

View file

@ -1,4 +1,4 @@
package engine
package mutate
import (
"encoding/json"
@ -18,15 +18,16 @@ import (
"github.com/nirmata/kyverno/pkg/engine/anchor"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/engine/variables"
)
// processOverlay processes validation patterns on the resource
func processOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
func ProcessOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
startTime := time.Now()
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
resp.Name = rule.Name
resp.Type = Mutation.String()
resp.Type = utils.Mutation.String()
defer func() {
resp.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying overlay rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
@ -83,10 +84,10 @@ func processOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstr
}
var patchResource []byte
patchResource, err = ApplyPatches(resourceRaw, patches)
patchResource, err = utils.ApplyPatches(resourceRaw, patches)
if err != nil {
msg := fmt.Sprintf("failed to apply JSON patches: %v", err)
glog.V(2).Infof("%s, patches=%s", msg, string(JoinPatches(patches)))
glog.V(2).Infof("%s, patches=%s", msg, string(utils.JoinPatches(patches)))
resp.Success = false
resp.Message = msg
return resp, resource
@ -285,7 +286,7 @@ func applyOverlayToArrayOfMaps(resource, overlay []interface{}, path string) ([]
lastElementIdx := len(resource)
for i, overlayElement := range overlay {
typedOverlay := overlayElement.(map[string]interface{})
anchors := getAnchorsFromMap(typedOverlay)
anchors := utils.GetAnchorsFromMap(typedOverlay)
if len(anchors) > 0 {
// If we have anchors - choose corresponding resource element and mutate it
@ -430,7 +431,7 @@ func removeAnchroFromMap(overlay map[string]interface{}) map[string]interface{}
func hasOnlyAnchors(overlay interface{}) bool {
switch typed := overlay.(type) {
case map[string]interface{}:
if anchors := getAnchorsFromMap(typed); len(anchors) == len(typed) {
if anchors := utils.GetAnchorsFromMap(typed); len(anchors) == len(typed) {
return true
}
@ -456,7 +457,7 @@ func hasOnlyAnchors(overlay interface{}) bool {
func hasNestedAnchors(overlay interface{}) bool {
switch typed := overlay.(type) {
case map[string]interface{}:
if anchors := getAnchorsFromMap(typed); len(anchors) > 0 {
if anchors := utils.GetAnchorsFromMap(typed); len(anchors) > 0 {
return true
}

View file

@ -1,4 +1,4 @@
package engine
package mutate
import (
"fmt"

View file

@ -1,4 +1,4 @@
package engine
package mutate
import (
"encoding/json"

View file

@ -1,4 +1,4 @@
package engine
package mutate
import "fmt"

View file

@ -1,4 +1,4 @@
package engine
package mutate
import (
"encoding/json"
@ -6,6 +6,7 @@ import (
"testing"
jsonpatch "github.com/evanphx/json-patch"
"github.com/nirmata/kyverno/pkg/engine/utils"
"gotest.tools/assert"
)
@ -69,7 +70,7 @@ func TestProcessOverlayPatches_NestedListWithAnchor(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, patches != nil)
patch := JoinPatches(patches)
patch := utils.JoinPatches(patches)
decoded, err := jsonpatch.DecodePatch(patch)
assert.NilError(t, err)
assert.Assert(t, decoded != nil)
@ -169,7 +170,7 @@ func TestProcessOverlayPatches_InsertIntoArray(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, patches != nil)
patch := JoinPatches(patches)
patch := utils.JoinPatches(patches)
decoded, err := jsonpatch.DecodePatch(patch)
assert.NilError(t, err)
@ -290,7 +291,7 @@ func TestProcessOverlayPatches_TestInsertToArray(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, patches != nil)
patch := JoinPatches(patches)
patch := utils.JoinPatches(patches)
decoded, err := jsonpatch.DecodePatch(patch)
assert.NilError(t, err)
@ -373,7 +374,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
doc, err := utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(`{
"apiVersion":"apps/v1",
@ -461,7 +462,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err = ApplyPatches(resourceRaw, patches)
doc, err = utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
compareJSONAsMap(t, expectedResult, doc)
@ -526,7 +527,7 @@ func TestProcessOverlayPatches_AddingAnchor(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
doc, err := utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(`{
"metadata":{
@ -611,7 +612,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
doc, err := utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(`
{
@ -689,7 +690,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err = ApplyPatches(resourceRaw, patches)
doc, err = utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
compareJSONAsMap(t, expectedResult, doc)
@ -753,7 +754,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
doc, err := utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(` {
"apiVersion":"v1",
@ -892,7 +893,7 @@ func TestProcessOverlayPatches_insertWithCondition(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
doc, err := utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(`{
"apiVersion": "apps/v1",
@ -1003,7 +1004,7 @@ func TestProcessOverlayPatches_InsertIfNotPresentWithConditions(t *testing.T) {
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
doc, err := utils.ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(`
@ -1154,5 +1155,5 @@ func TestApplyOverlay_ConditionOnArray(t *testing.T) {
]`)
p, err := applyOverlay(resource, overlay, "/")
assert.NilError(t, err)
assert.Assert(t, string(JoinPatches(p)) == string(expectedPatches))
assert.Assert(t, string(utils.JoinPatches(p)) == string(expectedPatches))
}

View file

@ -1,4 +1,4 @@
package engine
package mutate
import (
"encoding/json"
@ -6,72 +6,24 @@ import (
"strings"
"time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// JoinPatches joins array of serialized JSON patches to the single JSONPatch array
func JoinPatches(patches [][]byte) []byte {
var result []byte
if len(patches) == 0 {
return result
}
result = append(result, []byte("[\n")...)
for index, patch := range patches {
result = append(result, patch...)
if index != len(patches)-1 {
result = append(result, []byte(",\n")...)
}
}
result = append(result, []byte("\n]")...)
return result
}
// applyPatch applies patch for resource, returns patched resource.
func applyPatch(resource []byte, patchRaw []byte) ([]byte, error) {
patchesList := [][]byte{patchRaw}
return ApplyPatches(resource, patchesList)
return utils.ApplyPatches(resource, patchesList)
}
// ApplyPatches patches given resource with given patches and returns patched document
func ApplyPatches(resource []byte, patches [][]byte) ([]byte, error) {
joinedPatches := JoinPatches(patches)
patch, err := jsonpatch.DecodePatch(joinedPatches)
if err != nil {
return nil, err
}
patchedDocument, err := patch.Apply(resource)
if err != nil {
return resource, err
}
return patchedDocument, err
}
//ApplyPatchNew patches given resource with given joined patches
func ApplyPatchNew(resource, patch []byte) ([]byte, error) {
jsonpatch, err := jsonpatch.DecodePatch(patch)
if err != nil {
return nil, err
}
patchedResource, err := jsonpatch.Apply(resource)
if err != nil {
return nil, err
}
return patchedResource, err
}
func processPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
startTime := time.Now()
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
resp.Name = rule.Name
resp.Type = Mutation.String()
resp.Type = utils.Mutation.String()
defer func() {
resp.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished JSON patch rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)

View file

@ -1,4 +1,4 @@
package engine
package mutate
import (
"testing"
@ -7,6 +7,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
types "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/utils"
)
const endpointsDocument string = `{
@ -36,11 +37,11 @@ const endpointsDocument string = `{
func TestProcessPatches_EmptyPatches(t *testing.T) {
var emptyRule = types.Rule{}
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(emptyRule, *resourceUnstructured)
rr, _ := ProcessPatches(emptyRule, *resourceUnstructured)
assert.Check(t, rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
@ -69,14 +70,14 @@ func makeRuleWithPatches(patches []types.Patch) types.Rule {
func TestProcessPatches_EmptyDocument(t *testing.T) {
rule := makeRuleWithPatch(makeAddIsMutatedLabelPatch())
rr, _ := processPatches(rule, unstructured.Unstructured{})
rr, _ := ProcessPatches(rule, unstructured.Unstructured{})
assert.Assert(t, !rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
func TestProcessPatches_AllEmpty(t *testing.T) {
emptyRule := types.Rule{}
rr, _ := processPatches(emptyRule, unstructured.Unstructured{})
rr, _ := ProcessPatches(emptyRule, unstructured.Unstructured{})
assert.Check(t, !rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
@ -85,11 +86,11 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) {
patch := makeAddIsMutatedLabelPatch()
patch.Path = "/metadata/additional/is-mutated"
rule := makeRuleWithPatch(patch)
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(rule, *resourceUnstructured)
rr, _ := ProcessPatches(rule, *resourceUnstructured)
assert.Check(t, !rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
@ -97,11 +98,11 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) {
func TestProcessPatches_RemovePathDoesntExist(t *testing.T) {
patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
rule := makeRuleWithPatch(patch)
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(rule, *resourceUnstructured)
rr, _ := ProcessPatches(rule, *resourceUnstructured)
assert.Check(t, rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
@ -110,11 +111,11 @@ func TestProcessPatches_AddAndRemovePathsDontExist_EmptyResult(t *testing.T) {
patch1 := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
patch2 := types.Patch{Path: "/spec/labels/label3", Operation: "add", Value: "label3Value"}
rule := makeRuleWithPatches([]types.Patch{patch1, patch2})
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(rule, *resourceUnstructured)
rr, _ := ProcessPatches(rule, *resourceUnstructured)
assert.Check(t, !rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
@ -124,11 +125,11 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul
patch2 := types.Patch{Path: "/spec/labels/label2", Operation: "remove", Value: "label2Value"}
patch3 := types.Patch{Path: "/metadata/labels/label3", Operation: "add", Value: "label3Value"}
rule := makeRuleWithPatches([]types.Patch{patch1, patch2, patch3})
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(rule, *resourceUnstructured)
rr, _ := ProcessPatches(rule, *resourceUnstructured)
assert.Check(t, rr.Success)
assert.Assert(t, len(rr.Patches) != 0)
assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, rr.Patches[0])
@ -137,11 +138,11 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul
func TestProcessPatches_RemovePathDoesntExist_EmptyResult(t *testing.T) {
patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
rule := makeRuleWithPatch(patch)
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(rule, *resourceUnstructured)
rr, _ := ProcessPatches(rule, *resourceUnstructured)
assert.Check(t, rr.Success)
assert.Assert(t, len(rr.Patches) == 0)
}
@ -150,11 +151,11 @@ func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) {
patch1 := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
patch2 := types.Patch{Path: "/metadata/labels/label2", Operation: "add", Value: "label2Value"}
rule := makeRuleWithPatches([]types.Patch{patch1, patch2})
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
if err != nil {
t.Error(err)
}
rr, _ := processPatches(rule, *resourceUnstructured)
rr, _ := ProcessPatches(rule, *resourceUnstructured)
assert.Check(t, rr.Success)
assert.Assert(t, len(rr.Patches) == 1)
assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, rr.Patches[0])

View file

@ -0,0 +1,47 @@
package mutate
import (
"github.com/nirmata/kyverno/pkg/engine/anchor"
)
// removeAnchor remove special characters around anchored key
func removeAnchor(key string) string {
if anchor.IsConditionAnchor(key) {
return key[1 : len(key)-1]
}
if anchor.IsExistanceAnchor(key) || anchor.IsAddingAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
return key[2 : len(key)-1]
}
return key
}
func getRawKeyIfWrappedWithAttributes(str string) string {
if len(str) < 2 {
return str
}
if str[0] == '(' && str[len(str)-1] == ')' {
return str[1 : len(str)-1]
} else if (str[0] == '$' || str[0] == '^' || str[0] == '+' || str[0] == '=') && (str[1] == '(' && str[len(str)-1] == ')') {
return str[2 : len(str)-1]
} else {
return str
}
}
// getAnchorAndElementsFromMap gets the condition anchor map and resource map without anchor
func getAnchorAndElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
anchors := make(map[string]interface{})
elementsWithoutanchor := make(map[string]interface{})
for key, value := range anchorsMap {
if anchor.IsConditionAnchor(key) {
anchors[key] = value
} else if !anchor.IsAddingAnchor(key) {
elementsWithoutanchor[key] = value
}
}
return anchors, elementsWithoutanchor
}

View file

@ -0,0 +1,19 @@
package mutate
import (
"testing"
"gotest.tools/assert"
)
func TestRemoveAnchor_ConditionAnchor(t *testing.T) {
assert.Equal(t, removeAnchor("(abc)"), "abc")
}
func TestRemoveAnchor_ExistanceAnchor(t *testing.T) {
assert.Equal(t, removeAnchor("^(abc)"), "abc")
}
func TestRemoveAnchor_EmptyExistanceAnchor(t *testing.T) {
assert.Equal(t, removeAnchor("^()"), "")
}

View file

@ -7,9 +7,10 @@ import (
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/mutate"
"github.com/nirmata/kyverno/pkg/engine/rbac"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/variables"
"github.com/nirmata/kyverno/pkg/engine/rbac"
)
const (
@ -80,7 +81,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
// Process Overlay
if rule.Mutation.Overlay != nil {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = processOverlay(ctx, rule, patchedResource)
ruleResponse, patchedResource = mutate.ProcessOverlay(ctx, rule, patchedResource)
if ruleResponse.Success == true && ruleResponse.Patches == nil {
// overlay pattern does not match the resource conditions
glog.V(4).Infof(ruleResponse.Message)
@ -96,7 +97,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
// Process Patches
if rule.Mutation.Patches != nil {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = processPatches(rule, patchedResource)
ruleResponse, patchedResource = mutate.ProcessPatches(rule, patchedResource)
glog.Infof("Mutate patches in rule '%s' successfully applied on %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
incrementAppliedRuleCount()
@ -110,7 +111,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
if strings.Contains(PodControllers, resource.GetKind()) {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = processOverlay(ctx, podTemplateRule, patchedResource)
ruleResponse, patchedResource = mutate.ProcessOverlay(ctx, podTemplateRule, patchedResource)
if !ruleResponse.Success {
glog.Errorf("Failed to insert annotation to podTemplate of %s/%s/%s: %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), ruleResponse.Message)
continue

View file

@ -7,6 +7,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/utils"
"gotest.tools/assert"
)
@ -67,7 +68,7 @@ func Test_VariableSubstitutionOverlay(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
ctx := context.NewContext()
ctx.AddResource(rawResource)

View file

@ -350,7 +350,6 @@ func validatePattern(patternElement interface{}, path string, supportedAnchors [
default:
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
}
return "", nil
}
func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {

View file

@ -9,7 +9,6 @@ import (
"github.com/minio/minio/pkg/wildcard"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/anchor"
"github.com/nirmata/kyverno/pkg/engine/operator"
"github.com/nirmata/kyverno/pkg/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -208,44 +207,6 @@ func ParseNamespaceFromObject(bytes []byte) string {
return ""
}
// getAnchorsFromMap gets the conditional anchor map
func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for key, value := range anchorsMap {
if anchor.IsConditionAnchor(key) {
result[key] = value
}
}
return result
}
// getAnchorAndElementsFromMap gets the condition anchor map and resource map without anchor
func getAnchorAndElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
anchors := make(map[string]interface{})
elementsWithoutanchor := make(map[string]interface{})
for key, value := range anchorsMap {
if anchor.IsConditionAnchor(key) {
anchors[key] = value
} else if !anchor.IsAddingAnchor(key) {
elementsWithoutanchor[key] = value
}
}
return anchors, elementsWithoutanchor
}
func getAnchorFromMap(anchorsMap map[string]interface{}) (string, interface{}) {
for key, value := range anchorsMap {
if anchor.IsConditionAnchor(key) || anchor.IsExistanceAnchor(key) {
return key, value
}
}
return "", nil
}
func findKind(kinds []string, kindGVK string) bool {
for _, kind := range kinds {
if kind == kindGVK {
@ -255,28 +216,6 @@ func findKind(kinds []string, kindGVK string) bool {
return false
}
// func isConditionAnchor(str string) bool {
// if len(str) < 2 {
// return false
// }
// return (str[0] == '(' && str[len(str)-1] == ')')
// }
func getRawKeyIfWrappedWithAttributes(str string) string {
if len(str) < 2 {
return str
}
if str[0] == '(' && str[len(str)-1] == ')' {
return str[1 : len(str)-1]
} else if (str[0] == '$' || str[0] == '^' || str[0] == '+' || str[0] == '=') && (str[1] == '(' && str[len(str)-1] == ')') {
return str[2 : len(str)-1]
} else {
return str
}
}
func isStringIsReference(str string) bool {
if len(str) < len(operator.ReferenceSign) {
return false
@ -285,48 +224,7 @@ func isStringIsReference(str string) bool {
return str[0] == '$' && str[1] == '(' && str[len(str)-1] == ')'
}
// removeAnchor remove special characters around anchored key
func removeAnchor(key string) string {
if anchor.IsConditionAnchor(key) {
return key[1 : len(key)-1]
}
if anchor.IsExistanceAnchor(key) || anchor.IsAddingAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
return key[2 : len(key)-1]
}
return key
}
type resourceInfo struct {
Resource unstructured.Unstructured
Gvk *metav1.GroupVersionKind
}
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
resource := &unstructured.Unstructured{}
err := resource.UnmarshalJSON(data)
if err != nil {
glog.V(4).Infof("failed to unmarshall resource: %v", err)
return nil, err
}
return resource, nil
}
type RuleType int
const (
Mutation RuleType = iota
Validation
Generation
All
)
func (ri RuleType) String() string {
return [...]string{
"Mutation",
"Validation",
"Generation",
"All",
}[ri]
}

95
pkg/engine/utils/utils.go Normal file
View file

@ -0,0 +1,95 @@
package utils
import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/nirmata/kyverno/pkg/engine/anchor"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
type RuleType int
const (
Mutation RuleType = iota
Validation
Generation
All
)
func (ri RuleType) String() string {
return [...]string{
"Mutation",
"Validation",
"Generation",
"All",
}[ri]
}
// ApplyPatches patches given resource with given patches and returns patched document
func ApplyPatches(resource []byte, patches [][]byte) ([]byte, error) {
joinedPatches := JoinPatches(patches)
patch, err := jsonpatch.DecodePatch(joinedPatches)
if err != nil {
return nil, err
}
patchedDocument, err := patch.Apply(resource)
if err != nil {
return resource, err
}
return patchedDocument, err
}
//ApplyPatchNew patches given resource with given joined patches
func ApplyPatchNew(resource, patch []byte) ([]byte, error) {
jsonpatch, err := jsonpatch.DecodePatch(patch)
if err != nil {
return nil, err
}
patchedResource, err := jsonpatch.Apply(resource)
if err != nil {
return nil, err
}
return patchedResource, err
}
// JoinPatches joins array of serialized JSON patches to the single JSONPatch array
func JoinPatches(patches [][]byte) []byte {
var result []byte
if len(patches) == 0 {
return result
}
result = append(result, []byte("[\n")...)
for index, patch := range patches {
result = append(result, patch...)
if index != len(patches)-1 {
result = append(result, []byte(",\n")...)
}
}
result = append(result, []byte("\n]")...)
return result
}
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
resource := &unstructured.Unstructured{}
err := resource.UnmarshalJSON(data)
if err != nil {
return nil, err
}
return resource, nil
}
// getAnchorsFromMap gets the conditional anchor map
func GetAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for key, value := range anchorsMap {
if anchor.IsConditionAnchor(key) {
result[key] = value
}
}
return result
}

View file

@ -0,0 +1,26 @@
package utils
import(
"testing"
"encoding/json"
"gotest.tools/assert"
)
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 := GetAnchorsFromMap(unmarshalled)
assert.Assert(t, len(actualMap) == 0)
}

View file

@ -1,33 +1,14 @@
package engine
import (
"encoding/json"
"testing"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/utils"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
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 := getAnchorsFromMap(unmarshalled)
assert.Assert(t, len(actualMap) == 0)
}
// Match multiple kinds
func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
rawResource := []byte(`{
@ -68,7 +49,7 @@ func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
}
}
}`)
resource, err := ConvertToUnstructured(rawResource)
resource, err := utils.ConvertToUnstructured(rawResource)
if err != nil {
t.Errorf("unable to convert raw resource to unstructured: %v", err)
@ -125,7 +106,7 @@ func TestResourceDescriptionMatch_Name(t *testing.T) {
}
}
}`)
resource, err := ConvertToUnstructured(rawResource)
resource, err := utils.ConvertToUnstructured(rawResource)
if err != nil {
t.Errorf("unable to convert raw resource to unstructured: %v", err)
@ -183,7 +164,7 @@ func TestResourceDescriptionMatch_Name_Regex(t *testing.T) {
}
}
}`)
resource, err := ConvertToUnstructured(rawResource)
resource, err := utils.ConvertToUnstructured(rawResource)
if err != nil {
t.Errorf("unable to convert raw resource to unstructured: %v", err)
@ -241,7 +222,7 @@ func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) {
}
}
}`)
resource, err := ConvertToUnstructured(rawResource)
resource, err := utils.ConvertToUnstructured(rawResource)
if err != nil {
t.Errorf("unable to convert raw resource to unstructured: %v", err)
@ -307,7 +288,7 @@ func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) {
}
}
}`)
resource, err := ConvertToUnstructured(rawResource)
resource, err := utils.ConvertToUnstructured(rawResource)
if err != nil {
t.Errorf("unable to convert raw resource to unstructured: %v", err)
@ -375,7 +356,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
}
}
}`)
resource, err := ConvertToUnstructured(rawResource)
resource, err := utils.ConvertToUnstructured(rawResource)
if err != nil {
t.Errorf("unable to convert raw resource to unstructured: %v", err)
@ -411,15 +392,3 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
assert.Assert(t, !MatchesResourceDescription(*resource, rule))
}
func TestRemoveAnchor_ConditionAnchor(t *testing.T) {
assert.Equal(t, removeAnchor("(abc)"), "abc")
}
func TestRemoveAnchor_ExistanceAnchor(t *testing.T) {
assert.Equal(t, removeAnchor("^(abc)"), "abc")
}
func TestRemoveAnchor_EmptyExistanceAnchor(t *testing.T) {
assert.Equal(t, removeAnchor("^()"), "")
}

View file

@ -11,6 +11,7 @@ import (
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/rbac"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/engine/validate"
"github.com/nirmata/kyverno/pkg/engine/variables"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -168,7 +169,7 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
startTime := time.Now()
glog.V(4).Infof("started applying validation rule %q (%v)", rule.Name, startTime)
resp.Name = rule.Name
resp.Type = Validation.String()
resp.Type = utils.Validation.String()
defer func() {
resp.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying validation rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)

View file

@ -5,6 +5,7 @@ import (
"testing"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/utils"
"gotest.tools/assert"
)
@ -23,7 +24,7 @@ func TestGetAnchorsFromMap_ThereAreAnchors(t *testing.T) {
var unmarshalled map[string]interface{}
json.Unmarshal(rawMap, &unmarshalled)
actualMap := getAnchorsFromMap(unmarshalled)
actualMap := utils.GetAnchorsFromMap(unmarshalled)
assert.Equal(t, len(actualMap), 2)
assert.Equal(t, actualMap["(name)"].(string), "nirmata-*")
assert.Equal(t, actualMap["(namespace)"].(string), "kube-?olicy")
@ -115,7 +116,7 @@ func TestValidate_ServiceTest(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
@ -213,7 +214,7 @@ func TestValidate_MapHasFloats(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
@ -304,7 +305,7 @@ func TestValidate_image_tag_fail(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
msgs := []string{
"Validation rule 'validate-tag' succeeded.",
@ -402,7 +403,7 @@ func TestValidate_image_tag_pass(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
msgs := []string{
"Validation rule 'validate-tag' succeeded.",
@ -479,7 +480,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation error: A namespace is required; Validation rule check-default-namespace anyPattern[0] failed at path /metadata/namespace/.;Validation rule check-default-namespace anyPattern[1] failed at path /metadata/namespace/."}
@ -560,7 +561,7 @@ func TestValidate_host_network_port(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation error: Host network and port are not allowed; Validation rule 'validate-host-network-port' failed at path '/spec/containers/0/ports/0/hostPort/'"}
@ -649,7 +650,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation rule 'validate-host-path' succeeded."}
@ -737,7 +738,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation error: Host path '/var/lib/' is not allowed; Validation rule 'validate-host-path' failed at path '/spec/volumes/0/hostPath/path/'"}
@ -806,7 +807,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation rule 'pod rule 2' succeeded."}
@ -878,7 +879,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation rule 'pod rule 2' succeeded."}
@ -950,7 +951,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation error: pod: validate run as non root user; Validation rule 'pod rule 2' failed at path '/spec/securityContext/runAsNonRoot/'"}
@ -1024,7 +1025,7 @@ func TestValidate_AnchorList_pass(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation rule 'pod image rule' succeeded."}
@ -1098,7 +1099,7 @@ func TestValidate_AnchorList_fail(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
// msgs := []string{"Validation rule 'pod image rule' failed at '/spec/containers/1/name/' for resource Pod//myapp-pod."}
@ -1172,7 +1173,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
// msgs := []string{"Validation rule 'pod image rule' failed at '/spec/containers/' for resource Pod//myapp-pod."}
@ -1247,7 +1248,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation rule 'pod image rule' succeeded."}
@ -1334,7 +1335,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation error: Host path is not allowed; Validation rule 'validate-host-path' failed at path '/spec/volumes/0/hostPath/'"}
@ -1420,7 +1421,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) {
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
msgs := []string{"Validation rule 'validate-host-path' succeeded."}

View file

@ -6,7 +6,7 @@ import (
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/engine/response"
"k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -95,7 +95,7 @@ func processResourceWithPatches(patch []byte, resource []byte) []byte {
return nil
}
resource, err := engine.ApplyPatchNew(resource, patch)
resource, err := engineutils.ApplyPatchNew(resource, patch)
if err != nil {
glog.Errorf("failed to patch resource: %v", err)
return nil
@ -143,7 +143,7 @@ func extractResources(newRaw []byte, request *v1beta1.AdmissionRequest) (unstruc
// convertResource converts raw bytes to an unstructured object
func convertResource(raw []byte, group, version, kind, namespace string) (unstructured.Unstructured, error) {
obj, err := engine.ConvertToUnstructured(raw)
obj, err := engineutils.ConvertToUnstructured(raw)
if err != nil {
return unstructured.Unstructured{}, fmt.Errorf("failed to convert raw to unstructured: %v", err)
}

View file

@ -6,6 +6,7 @@ import (
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/webhooks/generate"
v1beta1 "k8s.io/api/admission/v1beta1"
)
@ -14,7 +15,7 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
var engineResponses []response.EngineResponse
// convert RAW to unstructured
resource, err := engine.ConvertToUnstructured(request.Object.Raw)
resource, err := utils.ConvertToUnstructured(request.Object.Raw)
if err != nil {
//TODO: skip applying the admission control ?
glog.Errorf("unable to convert raw resource to unstructured: %v", err)

View file

@ -3,9 +3,10 @@ package webhooks
import (
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
engine "github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
policyctr "github.com/nirmata/kyverno/pkg/policy"
"github.com/nirmata/kyverno/pkg/utils"
v1beta1 "k8s.io/api/admission/v1beta1"
@ -101,7 +102,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
if isResponseSuccesful(engineResponses) {
sendStat(false)
patch := engine.JoinPatches(patches)
patch := engineutils.JoinPatches(patches)
return true, patch, ""
}

View file

@ -6,7 +6,7 @@ import (
"testing"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/utils"
"gotest.tools/assert"
)
@ -31,7 +31,7 @@ func TestGeneratePodControllerRule_NilAnnotation(t *testing.T) {
patches, errs := generatePodControllerRule(policy)
assert.Assert(t, len(errs) == 0)
p, err := engine.ApplyPatches(policyRaw, patches)
p, err := utils.ApplyPatches(policyRaw, patches)
assert.NilError(t, err)
expectedPolicy := []byte(`{
@ -83,7 +83,7 @@ func TestGeneratePodControllerRule_ExistOtherAnnotation(t *testing.T) {
patches, errs := generatePodControllerRule(policy)
assert.Assert(t, len(errs) == 0)
p, err := engine.ApplyPatches(policyRaw, patches)
p, err := utils.ApplyPatches(policyRaw, patches)
assert.NilError(t, err)
expectedPolicy := []byte(`{
@ -233,7 +233,7 @@ func TestGeneratePodControllerRule(t *testing.T) {
patches, errs := generatePodControllerRule(policy)
assert.Assert(t, len(errs) == 0)
p, err := engine.ApplyPatches(policyRaw, patches)
p, err := utils.ApplyPatches(policyRaw, patches)
assert.NilError(t, err)
expectPolicy := []byte(`{