Skip to content

Commit

Permalink
Patch strategic merge preprocessing: implement anchor handling (#2156)
Browse files Browse the repository at this point in the history
* finished walkMap

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* added validation to the patchStrategicMerge

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* finished fixing tests

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* fixed part of old tests

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* patchStrategicMerge anchor preprocessing is finished

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* fix #1915 and #1896

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* fix lint errors

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* removed debug logs

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* added failing test

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>

* Fix unnecessary deletion

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>
  • Loading branch information
kacejot committed Jul 23, 2021
1 parent 0a38f1c commit 4c7ca97
Show file tree
Hide file tree
Showing 9 changed files with 1,369 additions and 581 deletions.
6 changes: 6 additions & 0 deletions pkg/engine/anchor/common/common.go
Expand Up @@ -63,6 +63,12 @@ func IsExistenceAnchor(str string) bool {
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
}

// IsNonAnchor checks that key does not have any anchor
func IsNonAnchor(str string) bool {
key, _ := RemoveAnchor(str)
return str == key
}

// RemoveAnchor remove anchor from the given key. It returns
// the anchor-free tag value and the prefix of the anchor.
func RemoveAnchor(key string) (string, string) {
Expand Down
8 changes: 8 additions & 0 deletions pkg/engine/forceMutate.go
Expand Up @@ -88,6 +88,14 @@ func ForceMutate(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resour
}
}

if rule.Mutation.PatchStrategicMerge != nil {
var resp response.RuleResponse
resp, resource = mutate.ProcessStrategicMergePatch(rule.Name, rule.Mutation.PatchStrategicMerge, resource, logger.WithValues("rule", rule.Name))
if !resp.Success {
return unstructured.Unstructured{}, fmt.Errorf(resp.Message)
}
}

if rule.Mutation.PatchesJSON6902 != "" {
var resp response.RuleResponse
jsonPatches, err := yaml.YAMLToJSON([]byte(rule.Mutation.PatchesJSON6902))
Expand Down
86 changes: 86 additions & 0 deletions pkg/engine/forceMutate_test.go
Expand Up @@ -254,3 +254,89 @@ func Test_ForceMutateSubstituteVarsWithPatchesJson6902(t *testing.T) {

assert.DeepEqual(t, expectedResource.UnstructuredContent(), mutatedResource.UnstructuredContent())
}

func Test_ForceMutateSubstituteVarsWithPatchStrategicMerge(t *testing.T) {
rawPolicy := []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "strategic-merge-patch"
},
"spec": {
"rules": [
{
"name": "set-image-pull-policy-add-command",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"mutate": {
"patchStrategicMerge": {
"spec": {
"volumes": [
{
"emptyDir": {
"medium": "Memory"
},
"name": "cache-volume"
}
]
}
}
}
}
]
}
}
`)

rawResource := []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "check-root-user"
},
"spec": {
"volumes": [
{
"name": "cache-volume",
"emptyDir": { }
},
{
"name": "cache-volume2",
"emptyDir": {
"medium": "Memory"
}
}
]
}
}
`)

expectedRawResource := []byte(`
{"apiVersion":"v1","kind":"Pod","metadata":{"name":"check-root-user"},"spec":{"volumes":[{"emptyDir":{"medium":"Memory"},"name":"cache-volume"},{"emptyDir":{"medium":"Memory"},"name":"cache-volume2"}]}}
`)

var expectedResource interface{}
assert.NilError(t, json.Unmarshal(expectedRawResource, &expectedResource))

var policy kyverno.ClusterPolicy
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)

resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
ctx := context.NewContext()
err = ctx.AddResource(rawResource)
assert.NilError(t, err)

mutatedResource, err := ForceMutate(ctx, policy, *resourceUnstructured)
assert.NilError(t, err)

assert.DeepEqual(t, expectedResource, mutatedResource.UnstructuredContent())
}
3 changes: 2 additions & 1 deletion pkg/engine/mutate/patchesUtils_test.go
Expand Up @@ -7,11 +7,12 @@ import (
"github.com/mattbaird/jsonpatch"
assertnew "github.com/stretchr/testify/assert"
"gotest.tools/assert"
"sigs.k8s.io/controller-runtime/pkg/log"
)

func Test_GeneratePatches(t *testing.T) {

out, err := strategicMergePatch(string(baseBytes), string(overlayBytes))
out, err := strategicMergePatch(log.Log, string(baseBytes), string(overlayBytes))
assert.NilError(t, err)

expectedPatches := map[string]bool{
Expand Down
11 changes: 5 additions & 6 deletions pkg/engine/mutate/strategicMergePatch.go
Expand Up @@ -65,7 +65,7 @@ func ProcessStrategicMergePatch(ruleName string, overlay interface{}, resource u
resp.Message = fmt.Sprintf("failed to process patchStrategicMerge: %v", err)
return resp, resource
}
patchedBytes, err := strategicMergePatch(string(base), string(overlayBytes))
patchedBytes, err := strategicMergePatch(logger, string(base), string(overlayBytes))
if err != nil {
log.Error(err, "failed to apply patchStrategicMerge")
msg := fmt.Sprintf("failed to apply patchStrategicMerge: %v", err)
Expand Down Expand Up @@ -103,9 +103,8 @@ func ProcessStrategicMergePatch(ruleName string, overlay interface{}, resource u
return resp, patchedResource
}

func strategicMergePatch(base, overlay string) ([]byte, error) {

preprocessedYaml, err := preProcessStrategicMergePatch(overlay, base)
func strategicMergePatch(logger logr.Logger, base, overlay string) ([]byte, error) {
preprocessedYaml, err := preProcessStrategicMergePatch(logger, overlay, base)
if err != nil {
return []byte{}, fmt.Errorf("failed to preProcess rule: %+v", err)
}
Expand All @@ -120,9 +119,9 @@ func strategicMergePatch(base, overlay string) ([]byte, error) {
return baseObj.Bytes(), err
}

func preProcessStrategicMergePatch(pattern, resource string) (*yaml.RNode, error) {
func preProcessStrategicMergePatch(logger logr.Logger, pattern, resource string) (*yaml.RNode, error) {
patternNode := yaml.MustParse(pattern)
resourceNode := yaml.MustParse(resource)
err := preProcessPattern(patternNode, resourceNode)
err := preProcessPattern(logger, patternNode, resourceNode)
return patternNode, err
}
5 changes: 3 additions & 2 deletions pkg/engine/mutate/strategicMergePatch_test.go
Expand Up @@ -9,6 +9,7 @@ import (
assertnew "github.com/stretchr/testify/assert"
"gotest.tools/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"
)

func TestMergePatch(t *testing.T) {
Expand Down Expand Up @@ -39,7 +40,7 @@ func TestMergePatch(t *testing.T) {
for i, test := range testCases {

// out
out, err := strategicMergePatch(string(test.rawResource), string(test.rawPolicy))
out, err := strategicMergePatch(log.Log, string(test.rawResource), string(test.rawPolicy))
assert.NilError(t, err)

// expect
Expand Down Expand Up @@ -132,7 +133,7 @@ func Test_PolicyDeserilize(t *testing.T) {
patchString, err := json.Marshal(overlayPatches)
assert.NilError(t, err)

out, err := strategicMergePatch(string(baseBytes), string(patchString))
out, err := strategicMergePatch(log.Log, string(baseBytes), string(patchString))
assert.NilError(t, err)

var ep unstructured.Unstructured
Expand Down

0 comments on commit 4c7ca97

Please sign in to comment.