1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00

feat: Removal of multiple elements using JSON patch in a foreach loop (#6335)

* handle mismatch of indices for removal operation while using foreach in mutate rule

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>

* Refactor elementIsRemoved function to pass 'gofumpt' check

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>

* traverse elements array in reverse order to handle removal of multiple elements

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>

* handle failing test case by changing assertion order

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>

* add kuttl tests

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>

* remove cleanup file from kuttl test

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>

* sort patches at the end

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix policy

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* move tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: Ashwin901 <ashwinprasanna9@gmail.com>
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Co-authored-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
Ashwin P 2023-04-13 17:34:56 +00:00 committed by GitHub
parent dbbdc1b96c
commit f2fc0d13a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 6 deletions

View file

@ -2,6 +2,7 @@ package mutation
import (
"context"
"encoding/json"
"fmt"
"github.com/go-logr/logr"
@ -9,8 +10,10 @@ import (
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/internal"
"github.com/kyverno/kyverno/pkg/engine/mutate"
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/utils/api"
"github.com/mattbaird/jsonpatch"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -129,8 +132,24 @@ func (f *forEachMutator) mutateElements(ctx context.Context, foreach kyvernov1.F
allPatches = append(allPatches, mutateResp.Patches...)
}
}
return mutate.NewResponse(engineapi.RuleStatusPass, patchedResource.unstructured, allPatches, "")
var sortedPatches []jsonpatch.JsonPatchOperation
for _, p := range allPatches {
var jp jsonpatch.JsonPatchOperation
if err := json.Unmarshal(p, &jp); err != nil {
return mutate.NewErrorResponse("failed to convert patch", err)
}
sortedPatches = append(sortedPatches, jp)
}
sortedPatches = patch.FilterAndSortPatches(sortedPatches)
var finalPatches [][]byte
for _, p := range sortedPatches {
if data, err := p.MarshalJSON(); err != nil {
return mutate.NewErrorResponse("failed to marshal patch", err)
} else {
finalPatches = append(finalPatches, data)
}
}
return mutate.NewResponse(engineapi.RuleStatusPass, patchedResource.unstructured, finalPatches, "")
}
func buildRuleResponse(rule *kyvernov1.Rule, mutateResp *mutate.Response, info resourceInfo) *engineapi.RuleResponse {

View file

@ -16,7 +16,7 @@ func generatePatches(src, dst []byte) ([][]byte, error) {
return nil, err
}
sortedPatches := filterAndSortPatches(pp)
sortedPatches := FilterAndSortPatches(pp)
for _, p := range sortedPatches {
pbytes, err := p.MarshalJSON()
if err != nil {
@ -29,13 +29,13 @@ func generatePatches(src, dst []byte) ([][]byte, error) {
return patchesBytes, err
}
// filterAndSortPatches
// FilterAndSortPatches
// 1. filters out patches with the certain paths
// 2. sorts the removal patches(with same path) by the key of index
// in descending order. The sort is required as when removing multiple
// elements from an array, the elements must be removed in descending
// order to preserve each index value.
func filterAndSortPatches(originalPatches []jsonpatch.JsonPatchOperation) []jsonpatch.JsonPatchOperation {
func FilterAndSortPatches(originalPatches []jsonpatch.JsonPatchOperation) []jsonpatch.JsonPatchOperation {
patches := filterInvalidPatches(originalPatches)
result := make([]jsonpatch.JsonPatchOperation, len(patches))

View file

@ -320,7 +320,7 @@ func Test_sortRemovalPatches(t *testing.T) {
}
for i, test := range tests {
sortedPatches := filterAndSortPatches(test.patches)
sortedPatches := FilterAndSortPatches(test.patches)
assertnew.Equal(t, test.expected, sortedPatches, fmt.Sprintf("%dth test fails", i))
}
}

View file

@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- policy.yaml
assert:
- policy-assert.yaml

View file

@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- pod.yaml
assert:
- pod-assert.yaml

View file

@ -0,0 +1,12 @@
## Description
This test checks if multiple elements are successfully removed while using foreach.
## Expected Behavior
The two `hostPath` volumes should be removed from the `busybox` pod and only the `emptyDir` volume and service account volume should remain.
## Reference Issue(s)
5661

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
volumes:
- name: vault-secret
emptyDir:
medium: Memory
- projected: {}

View file

@ -0,0 +1,18 @@
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: busybox:1.35
volumes:
- name: socket
hostPath:
path: "/var/run/foo"
- name: vault-secret
emptyDir:
medium: Memory
- name: bar
hostPath:
path: "/var/run/bar"

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: foreach-remove-elements
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,25 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: foreach-remove-elements
spec:
background: false
schemaValidation: false
rules:
- name: remove-elements
match:
any:
- resources:
kinds:
- Pod
mutate:
foreach:
- list: request.object.spec.volumes[]
preconditions:
all:
- key: hostPath
operator: AnyIn
value: "{{ element.keys(@) }}"
patchesJson6902: |-
- path: /spec/volumes/{{elementIndex}}
op: remove