mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
move mutation to subpackage pkg/engine/mutate
This commit is contained in:
parent
3cf9141f4d
commit
472fa29fce
22 changed files with 283 additions and 269 deletions
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -18,15 +18,16 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/variables"
|
"github.com/nirmata/kyverno/pkg/engine/variables"
|
||||||
)
|
)
|
||||||
|
|
||||||
// processOverlay processes validation patterns on the resource
|
// 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()
|
startTime := time.Now()
|
||||||
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
|
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
|
||||||
resp.Name = rule.Name
|
resp.Name = rule.Name
|
||||||
resp.Type = Mutation.String()
|
resp.Type = utils.Mutation.String()
|
||||||
defer func() {
|
defer func() {
|
||||||
resp.RuleStats.ProcessingTime = time.Since(startTime)
|
resp.RuleStats.ProcessingTime = time.Since(startTime)
|
||||||
glog.V(4).Infof("finished applying overlay rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
|
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
|
var patchResource []byte
|
||||||
patchResource, err = ApplyPatches(resourceRaw, patches)
|
patchResource, err = utils.ApplyPatches(resourceRaw, patches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("failed to apply JSON patches: %v", err)
|
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.Success = false
|
||||||
resp.Message = msg
|
resp.Message = msg
|
||||||
return resp, resource
|
return resp, resource
|
||||||
|
@ -285,7 +286,7 @@ func applyOverlayToArrayOfMaps(resource, overlay []interface{}, path string) ([]
|
||||||
lastElementIdx := len(resource)
|
lastElementIdx := len(resource)
|
||||||
for i, overlayElement := range overlay {
|
for i, overlayElement := range overlay {
|
||||||
typedOverlay := overlayElement.(map[string]interface{})
|
typedOverlay := overlayElement.(map[string]interface{})
|
||||||
anchors := getAnchorsFromMap(typedOverlay)
|
anchors := utils.GetAnchorsFromMap(typedOverlay)
|
||||||
|
|
||||||
if len(anchors) > 0 {
|
if len(anchors) > 0 {
|
||||||
// If we have anchors - choose corresponding resource element and mutate it
|
// 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 {
|
func hasOnlyAnchors(overlay interface{}) bool {
|
||||||
switch typed := overlay.(type) {
|
switch typed := overlay.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
if anchors := getAnchorsFromMap(typed); len(anchors) == len(typed) {
|
if anchors := utils.GetAnchorsFromMap(typed); len(anchors) == len(typed) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +457,7 @@ func hasOnlyAnchors(overlay interface{}) bool {
|
||||||
func hasNestedAnchors(overlay interface{}) bool {
|
func hasNestedAnchors(overlay interface{}) bool {
|
||||||
switch typed := overlay.(type) {
|
switch typed := overlay.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
if anchors := getAnchorsFromMap(typed); len(anchors) > 0 {
|
if anchors := utils.GetAnchorsFromMap(typed); len(anchors) > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ func TestProcessOverlayPatches_NestedListWithAnchor(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, patches != nil)
|
assert.Assert(t, patches != nil)
|
||||||
|
|
||||||
patch := JoinPatches(patches)
|
patch := utils.JoinPatches(patches)
|
||||||
decoded, err := jsonpatch.DecodePatch(patch)
|
decoded, err := jsonpatch.DecodePatch(patch)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, decoded != nil)
|
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, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, patches != nil)
|
assert.Assert(t, patches != nil)
|
||||||
|
|
||||||
patch := JoinPatches(patches)
|
patch := utils.JoinPatches(patches)
|
||||||
|
|
||||||
decoded, err := jsonpatch.DecodePatch(patch)
|
decoded, err := jsonpatch.DecodePatch(patch)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
@ -290,7 +291,7 @@ func TestProcessOverlayPatches_TestInsertToArray(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, patches != nil)
|
assert.Assert(t, patches != nil)
|
||||||
|
|
||||||
patch := JoinPatches(patches)
|
patch := utils.JoinPatches(patches)
|
||||||
|
|
||||||
decoded, err := jsonpatch.DecodePatch(patch)
|
decoded, err := jsonpatch.DecodePatch(patch)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
@ -373,7 +374,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err := ApplyPatches(resourceRaw, patches)
|
doc, err := utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expectedResult := []byte(`{
|
expectedResult := []byte(`{
|
||||||
"apiVersion":"apps/v1",
|
"apiVersion":"apps/v1",
|
||||||
|
@ -461,7 +462,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err = ApplyPatches(resourceRaw, patches)
|
doc, err = utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
compareJSONAsMap(t, expectedResult, doc)
|
compareJSONAsMap(t, expectedResult, doc)
|
||||||
|
@ -526,7 +527,7 @@ func TestProcessOverlayPatches_AddingAnchor(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err := ApplyPatches(resourceRaw, patches)
|
doc, err := utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expectedResult := []byte(`{
|
expectedResult := []byte(`{
|
||||||
"metadata":{
|
"metadata":{
|
||||||
|
@ -611,7 +612,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err := ApplyPatches(resourceRaw, patches)
|
doc, err := utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expectedResult := []byte(`
|
expectedResult := []byte(`
|
||||||
{
|
{
|
||||||
|
@ -689,7 +690,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err = ApplyPatches(resourceRaw, patches)
|
doc, err = utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
compareJSONAsMap(t, expectedResult, doc)
|
compareJSONAsMap(t, expectedResult, doc)
|
||||||
|
@ -753,7 +754,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err := ApplyPatches(resourceRaw, patches)
|
doc, err := utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expectedResult := []byte(` {
|
expectedResult := []byte(` {
|
||||||
"apiVersion":"v1",
|
"apiVersion":"v1",
|
||||||
|
@ -892,7 +893,7 @@ func TestProcessOverlayPatches_insertWithCondition(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err := ApplyPatches(resourceRaw, patches)
|
doc, err := utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expectedResult := []byte(`{
|
expectedResult := []byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "apps/v1",
|
||||||
|
@ -1003,7 +1004,7 @@ func TestProcessOverlayPatches_InsertIfNotPresentWithConditions(t *testing.T) {
|
||||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||||
assert.Assert(t, len(patches) != 0)
|
assert.Assert(t, len(patches) != 0)
|
||||||
|
|
||||||
doc, err := ApplyPatches(resourceRaw, patches)
|
doc, err := utils.ApplyPatches(resourceRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expectedResult := []byte(`
|
expectedResult := []byte(`
|
||||||
|
@ -1154,5 +1155,5 @@ func TestApplyOverlay_ConditionOnArray(t *testing.T) {
|
||||||
]`)
|
]`)
|
||||||
p, err := applyOverlay(resource, overlay, "/")
|
p, err := applyOverlay(resource, overlay, "/")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, string(JoinPatches(p)) == string(expectedPatches))
|
assert.Assert(t, string(utils.JoinPatches(p)) == string(expectedPatches))
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,72 +6,24 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"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.
|
// applyPatch applies patch for resource, returns patched resource.
|
||||||
func applyPatch(resource []byte, patchRaw []byte) ([]byte, error) {
|
func applyPatch(resource []byte, patchRaw []byte) ([]byte, error) {
|
||||||
patchesList := [][]byte{patchRaw}
|
patchesList := [][]byte{patchRaw}
|
||||||
return ApplyPatches(resource, patchesList)
|
return utils.ApplyPatches(resource, patchesList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyPatches patches given resource with given patches and returns patched document
|
func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
|
||||||
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) {
|
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
|
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
|
||||||
resp.Name = rule.Name
|
resp.Name = rule.Name
|
||||||
resp.Type = Mutation.String()
|
resp.Type = utils.Mutation.String()
|
||||||
defer func() {
|
defer func() {
|
||||||
resp.RuleStats.ProcessingTime = time.Since(startTime)
|
resp.RuleStats.ProcessingTime = time.Since(startTime)
|
||||||
glog.V(4).Infof("finished JSON patch rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
|
glog.V(4).Infof("finished JSON patch rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
|
|
@ -1,4 +1,4 @@
|
||||||
package engine
|
package mutate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
|
||||||
types "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
types "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const endpointsDocument string = `{
|
const endpointsDocument string = `{
|
||||||
|
@ -36,11 +37,11 @@ const endpointsDocument string = `{
|
||||||
|
|
||||||
func TestProcessPatches_EmptyPatches(t *testing.T) {
|
func TestProcessPatches_EmptyPatches(t *testing.T) {
|
||||||
var emptyRule = types.Rule{}
|
var emptyRule = types.Rule{}
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(emptyRule, *resourceUnstructured)
|
rr, _ := ProcessPatches(emptyRule, *resourceUnstructured)
|
||||||
assert.Check(t, rr.Success)
|
assert.Check(t, rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
assert.Assert(t, len(rr.Patches) == 0)
|
||||||
}
|
}
|
||||||
|
@ -69,14 +70,14 @@ func makeRuleWithPatches(patches []types.Patch) types.Rule {
|
||||||
|
|
||||||
func TestProcessPatches_EmptyDocument(t *testing.T) {
|
func TestProcessPatches_EmptyDocument(t *testing.T) {
|
||||||
rule := makeRuleWithPatch(makeAddIsMutatedLabelPatch())
|
rule := makeRuleWithPatch(makeAddIsMutatedLabelPatch())
|
||||||
rr, _ := processPatches(rule, unstructured.Unstructured{})
|
rr, _ := ProcessPatches(rule, unstructured.Unstructured{})
|
||||||
assert.Assert(t, !rr.Success)
|
assert.Assert(t, !rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
assert.Assert(t, len(rr.Patches) == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProcessPatches_AllEmpty(t *testing.T) {
|
func TestProcessPatches_AllEmpty(t *testing.T) {
|
||||||
emptyRule := types.Rule{}
|
emptyRule := types.Rule{}
|
||||||
rr, _ := processPatches(emptyRule, unstructured.Unstructured{})
|
rr, _ := ProcessPatches(emptyRule, unstructured.Unstructured{})
|
||||||
assert.Check(t, !rr.Success)
|
assert.Check(t, !rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
assert.Assert(t, len(rr.Patches) == 0)
|
||||||
}
|
}
|
||||||
|
@ -85,11 +86,11 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) {
|
||||||
patch := makeAddIsMutatedLabelPatch()
|
patch := makeAddIsMutatedLabelPatch()
|
||||||
patch.Path = "/metadata/additional/is-mutated"
|
patch.Path = "/metadata/additional/is-mutated"
|
||||||
rule := makeRuleWithPatch(patch)
|
rule := makeRuleWithPatch(patch)
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(rule, *resourceUnstructured)
|
rr, _ := ProcessPatches(rule, *resourceUnstructured)
|
||||||
assert.Check(t, !rr.Success)
|
assert.Check(t, !rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
assert.Assert(t, len(rr.Patches) == 0)
|
||||||
}
|
}
|
||||||
|
@ -97,11 +98,11 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) {
|
||||||
func TestProcessPatches_RemovePathDoesntExist(t *testing.T) {
|
func TestProcessPatches_RemovePathDoesntExist(t *testing.T) {
|
||||||
patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
|
patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
|
||||||
rule := makeRuleWithPatch(patch)
|
rule := makeRuleWithPatch(patch)
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(rule, *resourceUnstructured)
|
rr, _ := ProcessPatches(rule, *resourceUnstructured)
|
||||||
assert.Check(t, rr.Success)
|
assert.Check(t, rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
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"}
|
patch1 := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
|
||||||
patch2 := types.Patch{Path: "/spec/labels/label3", Operation: "add", Value: "label3Value"}
|
patch2 := types.Patch{Path: "/spec/labels/label3", Operation: "add", Value: "label3Value"}
|
||||||
rule := makeRuleWithPatches([]types.Patch{patch1, patch2})
|
rule := makeRuleWithPatches([]types.Patch{patch1, patch2})
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(rule, *resourceUnstructured)
|
rr, _ := ProcessPatches(rule, *resourceUnstructured)
|
||||||
assert.Check(t, !rr.Success)
|
assert.Check(t, !rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
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"}
|
patch2 := types.Patch{Path: "/spec/labels/label2", Operation: "remove", Value: "label2Value"}
|
||||||
patch3 := types.Patch{Path: "/metadata/labels/label3", Operation: "add", Value: "label3Value"}
|
patch3 := types.Patch{Path: "/metadata/labels/label3", Operation: "add", Value: "label3Value"}
|
||||||
rule := makeRuleWithPatches([]types.Patch{patch1, patch2, patch3})
|
rule := makeRuleWithPatches([]types.Patch{patch1, patch2, patch3})
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(rule, *resourceUnstructured)
|
rr, _ := ProcessPatches(rule, *resourceUnstructured)
|
||||||
assert.Check(t, rr.Success)
|
assert.Check(t, rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) != 0)
|
assert.Assert(t, len(rr.Patches) != 0)
|
||||||
assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, 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) {
|
func TestProcessPatches_RemovePathDoesntExist_EmptyResult(t *testing.T) {
|
||||||
patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
|
patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
|
||||||
rule := makeRuleWithPatch(patch)
|
rule := makeRuleWithPatch(patch)
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(rule, *resourceUnstructured)
|
rr, _ := ProcessPatches(rule, *resourceUnstructured)
|
||||||
assert.Check(t, rr.Success)
|
assert.Check(t, rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 0)
|
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"}
|
patch1 := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
|
||||||
patch2 := types.Patch{Path: "/metadata/labels/label2", Operation: "add", Value: "label2Value"}
|
patch2 := types.Patch{Path: "/metadata/labels/label2", Operation: "add", Value: "label2Value"}
|
||||||
rule := makeRuleWithPatches([]types.Patch{patch1, patch2})
|
rule := makeRuleWithPatches([]types.Patch{patch1, patch2})
|
||||||
resourceUnstructured, err := ConvertToUnstructured([]byte(endpointsDocument))
|
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(endpointsDocument))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
rr, _ := processPatches(rule, *resourceUnstructured)
|
rr, _ := ProcessPatches(rule, *resourceUnstructured)
|
||||||
assert.Check(t, rr.Success)
|
assert.Check(t, rr.Success)
|
||||||
assert.Assert(t, len(rr.Patches) == 1)
|
assert.Assert(t, len(rr.Patches) == 1)
|
||||||
assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, rr.Patches[0])
|
assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, rr.Patches[0])
|
47
pkg/engine/mutate/utils.go
Normal file
47
pkg/engine/mutate/utils.go
Normal 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
|
||||||
|
}
|
19
pkg/engine/mutate/utils_test.go
Normal file
19
pkg/engine/mutate/utils_test.go
Normal 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("^()"), "")
|
||||||
|
}
|
|
@ -7,9 +7,10 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
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/response"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/variables"
|
"github.com/nirmata/kyverno/pkg/engine/variables"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/rbac"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -80,7 +81,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
|
||||||
// Process Overlay
|
// Process Overlay
|
||||||
if rule.Mutation.Overlay != nil {
|
if rule.Mutation.Overlay != nil {
|
||||||
var ruleResponse response.RuleResponse
|
var ruleResponse response.RuleResponse
|
||||||
ruleResponse, patchedResource = processOverlay(ctx, rule, patchedResource)
|
ruleResponse, patchedResource = mutate.ProcessOverlay(ctx, rule, patchedResource)
|
||||||
if ruleResponse.Success == true && ruleResponse.Patches == nil {
|
if ruleResponse.Success == true && ruleResponse.Patches == nil {
|
||||||
// overlay pattern does not match the resource conditions
|
// overlay pattern does not match the resource conditions
|
||||||
glog.V(4).Infof(ruleResponse.Message)
|
glog.V(4).Infof(ruleResponse.Message)
|
||||||
|
@ -96,7 +97,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
|
||||||
// Process Patches
|
// Process Patches
|
||||||
if rule.Mutation.Patches != nil {
|
if rule.Mutation.Patches != nil {
|
||||||
var ruleResponse response.RuleResponse
|
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())
|
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)
|
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
|
||||||
incrementAppliedRuleCount()
|
incrementAppliedRuleCount()
|
||||||
|
@ -110,7 +111,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
|
||||||
|
|
||||||
if strings.Contains(PodControllers, resource.GetKind()) {
|
if strings.Contains(PodControllers, resource.GetKind()) {
|
||||||
var ruleResponse response.RuleResponse
|
var ruleResponse response.RuleResponse
|
||||||
ruleResponse, patchedResource = processOverlay(ctx, podTemplateRule, patchedResource)
|
ruleResponse, patchedResource = mutate.ProcessOverlay(ctx, podTemplateRule, patchedResource)
|
||||||
if !ruleResponse.Success {
|
if !ruleResponse.Success {
|
||||||
glog.Errorf("Failed to insert annotation to podTemplate of %s/%s/%s: %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), ruleResponse.Message)
|
glog.Errorf("Failed to insert annotation to podTemplate of %s/%s/%s: %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), ruleResponse.Message)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ func Test_VariableSubstitutionOverlay(t *testing.T) {
|
||||||
|
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
ctx := context.NewContext()
|
ctx := context.NewContext()
|
||||||
ctx.AddResource(rawResource)
|
ctx.AddResource(rawResource)
|
||||||
|
|
|
@ -350,7 +350,6 @@ func validatePattern(patternElement interface{}, path string, supportedAnchors [
|
||||||
default:
|
default:
|
||||||
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
|
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) {
|
func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
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/engine/operator"
|
||||||
"github.com/nirmata/kyverno/pkg/utils"
|
"github.com/nirmata/kyverno/pkg/utils"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -208,44 +207,6 @@ func ParseNamespaceFromObject(bytes []byte) string {
|
||||||
return ""
|
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 {
|
func findKind(kinds []string, kindGVK string) bool {
|
||||||
for _, kind := range kinds {
|
for _, kind := range kinds {
|
||||||
if kind == kindGVK {
|
if kind == kindGVK {
|
||||||
|
@ -255,28 +216,6 @@ func findKind(kinds []string, kindGVK string) bool {
|
||||||
return false
|
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 {
|
func isStringIsReference(str string) bool {
|
||||||
if len(str) < len(operator.ReferenceSign) {
|
if len(str) < len(operator.ReferenceSign) {
|
||||||
return false
|
return false
|
||||||
|
@ -285,48 +224,7 @@ func isStringIsReference(str string) bool {
|
||||||
return str[0] == '$' && str[1] == '(' && str[len(str)-1] == ')'
|
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 {
|
type resourceInfo struct {
|
||||||
Resource unstructured.Unstructured
|
Resource unstructured.Unstructured
|
||||||
Gvk *metav1.GroupVersionKind
|
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
95
pkg/engine/utils/utils.go
Normal 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
|
||||||
|
}
|
26
pkg/engine/utils/utils_test.go
Normal file
26
pkg/engine/utils/utils_test.go
Normal 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)
|
||||||
|
}
|
|
@ -1,33 +1,14 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
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
|
// Match multiple kinds
|
||||||
func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
|
func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
|
||||||
rawResource := []byte(`{
|
rawResource := []byte(`{
|
||||||
|
@ -68,7 +49,7 @@ func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
resource, err := ConvertToUnstructured(rawResource)
|
resource, err := utils.ConvertToUnstructured(rawResource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unable to convert raw resource to unstructured: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Errorf("unable to convert raw resource to unstructured: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Errorf("unable to convert raw resource to unstructured: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Errorf("unable to convert raw resource to unstructured: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Errorf("unable to convert raw resource to unstructured: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Errorf("unable to convert raw resource to unstructured: %v", err)
|
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))
|
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("^()"), "")
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/rbac"
|
"github.com/nirmata/kyverno/pkg/engine/rbac"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"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/validate"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/variables"
|
"github.com/nirmata/kyverno/pkg/engine/variables"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
@ -168,7 +169,7 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
glog.V(4).Infof("started applying validation rule %q (%v)", rule.Name, startTime)
|
glog.V(4).Infof("started applying validation rule %q (%v)", rule.Name, startTime)
|
||||||
resp.Name = rule.Name
|
resp.Name = rule.Name
|
||||||
resp.Type = Validation.String()
|
resp.Type = utils.Validation.String()
|
||||||
defer func() {
|
defer func() {
|
||||||
resp.RuleStats.ProcessingTime = time.Since(startTime)
|
resp.RuleStats.ProcessingTime = time.Since(startTime)
|
||||||
glog.V(4).Infof("finished applying validation rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
|
glog.V(4).Infof("finished applying validation rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ func TestGetAnchorsFromMap_ThereAreAnchors(t *testing.T) {
|
||||||
var unmarshalled map[string]interface{}
|
var unmarshalled map[string]interface{}
|
||||||
json.Unmarshal(rawMap, &unmarshalled)
|
json.Unmarshal(rawMap, &unmarshalled)
|
||||||
|
|
||||||
actualMap := getAnchorsFromMap(unmarshalled)
|
actualMap := utils.GetAnchorsFromMap(unmarshalled)
|
||||||
assert.Equal(t, len(actualMap), 2)
|
assert.Equal(t, len(actualMap), 2)
|
||||||
assert.Equal(t, actualMap["(name)"].(string), "nirmata-*")
|
assert.Equal(t, actualMap["(name)"].(string), "nirmata-*")
|
||||||
assert.Equal(t, actualMap["(namespace)"].(string), "kube-?olicy")
|
assert.Equal(t, actualMap["(namespace)"].(string), "kube-?olicy")
|
||||||
|
@ -115,7 +116,7 @@ func TestValidate_ServiceTest(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
|
@ -213,7 +214,7 @@ func TestValidate_MapHasFloats(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
|
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
|
||||||
|
@ -304,7 +305,7 @@ func TestValidate_image_tag_fail(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
msgs := []string{
|
msgs := []string{
|
||||||
"Validation rule 'validate-tag' succeeded.",
|
"Validation rule 'validate-tag' succeeded.",
|
||||||
|
@ -402,7 +403,7 @@ func TestValidate_image_tag_pass(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
msgs := []string{
|
msgs := []string{
|
||||||
"Validation rule 'validate-tag' succeeded.",
|
"Validation rule 'validate-tag' succeeded.",
|
||||||
|
@ -479,7 +480,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
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/."}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
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/'"}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
msgs := []string{"Validation rule 'validate-host-path' succeeded."}
|
msgs := []string{"Validation rule 'validate-host-path' succeeded."}
|
||||||
|
@ -737,7 +738,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
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/'"}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
msgs := []string{"Validation rule 'pod rule 2' succeeded."}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
msgs := []string{"Validation rule 'pod rule 2' succeeded."}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
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/'"}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
msgs := []string{"Validation rule 'pod image rule' succeeded."}
|
msgs := []string{"Validation rule 'pod image rule' succeeded."}
|
||||||
|
@ -1098,7 +1099,7 @@ func TestValidate_AnchorList_fail(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
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."}
|
// 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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
// msgs := []string{"Validation rule 'pod image rule' failed at '/spec/containers/' for resource Pod//myapp-pod."}
|
// 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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
msgs := []string{"Validation rule 'pod image rule' succeeded."}
|
msgs := []string{"Validation rule 'pod image rule' succeeded."}
|
||||||
|
@ -1334,7 +1335,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) {
|
||||||
var policy kyverno.ClusterPolicy
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
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/'"}
|
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
|
var policy kyverno.ClusterPolicy
|
||||||
json.Unmarshal(rawPolicy, &policy)
|
json.Unmarshal(rawPolicy, &policy)
|
||||||
|
|
||||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||||
msgs := []string{"Validation rule 'validate-host-path' succeeded."}
|
msgs := []string{"Validation rule 'validate-host-path' succeeded."}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
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"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
"k8s.io/api/admission/v1beta1"
|
"k8s.io/api/admission/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
@ -95,7 +95,7 @@ func processResourceWithPatches(patch []byte, resource []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resource, err := engine.ApplyPatchNew(resource, patch)
|
resource, err := engineutils.ApplyPatchNew(resource, patch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("failed to patch resource: %v", err)
|
glog.Errorf("failed to patch resource: %v", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -143,7 +143,7 @@ func extractResources(newRaw []byte, request *v1beta1.AdmissionRequest) (unstruc
|
||||||
|
|
||||||
// convertResource converts raw bytes to an unstructured object
|
// convertResource converts raw bytes to an unstructured object
|
||||||
func convertResource(raw []byte, group, version, kind, namespace string) (unstructured.Unstructured, error) {
|
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 {
|
if err != nil {
|
||||||
return unstructured.Unstructured{}, fmt.Errorf("failed to convert raw to unstructured: %v", err)
|
return unstructured.Unstructured{}, fmt.Errorf("failed to convert raw to unstructured: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"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/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
"github.com/nirmata/kyverno/pkg/webhooks/generate"
|
"github.com/nirmata/kyverno/pkg/webhooks/generate"
|
||||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
)
|
)
|
||||||
|
@ -14,7 +15,7 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
|
||||||
var engineResponses []response.EngineResponse
|
var engineResponses []response.EngineResponse
|
||||||
|
|
||||||
// convert RAW to unstructured
|
// convert RAW to unstructured
|
||||||
resource, err := engine.ConvertToUnstructured(request.Object.Raw)
|
resource, err := utils.ConvertToUnstructured(request.Object.Raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//TODO: skip applying the admission control ?
|
//TODO: skip applying the admission control ?
|
||||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||||
|
|
|
@ -3,9 +3,10 @@ package webhooks
|
||||||
import (
|
import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
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/context"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
|
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
|
||||||
policyctr "github.com/nirmata/kyverno/pkg/policy"
|
policyctr "github.com/nirmata/kyverno/pkg/policy"
|
||||||
"github.com/nirmata/kyverno/pkg/utils"
|
"github.com/nirmata/kyverno/pkg/utils"
|
||||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
|
@ -101,7 +102,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
|
||||||
|
|
||||||
if isResponseSuccesful(engineResponses) {
|
if isResponseSuccesful(engineResponses) {
|
||||||
sendStat(false)
|
sendStat(false)
|
||||||
patch := engine.JoinPatches(patches)
|
patch := engineutils.JoinPatches(patches)
|
||||||
return true, patch, ""
|
return true, patch, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
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"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func TestGeneratePodControllerRule_NilAnnotation(t *testing.T) {
|
||||||
patches, errs := generatePodControllerRule(policy)
|
patches, errs := generatePodControllerRule(policy)
|
||||||
assert.Assert(t, len(errs) == 0)
|
assert.Assert(t, len(errs) == 0)
|
||||||
|
|
||||||
p, err := engine.ApplyPatches(policyRaw, patches)
|
p, err := utils.ApplyPatches(policyRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expectedPolicy := []byte(`{
|
expectedPolicy := []byte(`{
|
||||||
|
@ -83,7 +83,7 @@ func TestGeneratePodControllerRule_ExistOtherAnnotation(t *testing.T) {
|
||||||
patches, errs := generatePodControllerRule(policy)
|
patches, errs := generatePodControllerRule(policy)
|
||||||
assert.Assert(t, len(errs) == 0)
|
assert.Assert(t, len(errs) == 0)
|
||||||
|
|
||||||
p, err := engine.ApplyPatches(policyRaw, patches)
|
p, err := utils.ApplyPatches(policyRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expectedPolicy := []byte(`{
|
expectedPolicy := []byte(`{
|
||||||
|
@ -233,7 +233,7 @@ func TestGeneratePodControllerRule(t *testing.T) {
|
||||||
patches, errs := generatePodControllerRule(policy)
|
patches, errs := generatePodControllerRule(policy)
|
||||||
assert.Assert(t, len(errs) == 0)
|
assert.Assert(t, len(errs) == 0)
|
||||||
|
|
||||||
p, err := engine.ApplyPatches(policyRaw, patches)
|
p, err := utils.ApplyPatches(policyRaw, patches)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expectPolicy := []byte(`{
|
expectPolicy := []byte(`{
|
||||||
|
|
Loading…
Add table
Reference in a new issue