1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/mutate/strategicPreprocessing_test.go

988 lines
18 KiB
Go
Raw Normal View History

package mutate
import (
"encoding/json"
"testing"
anchor "github.com/kyverno/kyverno/pkg/engine/anchor/common"
"gotest.tools/assert"
"sigs.k8s.io/controller-runtime/pkg/log"
yaml "sigs.k8s.io/kustomize/kyaml/yaml"
)
func areEqualJSONs(t *testing.T, s1, s2 []byte) {
var o1 interface{}
var o2 interface{}
var err error
err = json.Unmarshal(s1, &o1)
assert.NilError(t, err)
err = json.Unmarshal(s2, &o2)
assert.NilError(t, err)
assert.DeepEqual(t, o1, o2)
}
func Test_preProcessStrategicMergePatch_multipleAnchors(t *testing.T) {
testCases := []struct {
rawPolicy []byte
rawResource []byte
expectedPatch []byte
}{
{
rawPolicy: []byte(`{
"metadata": null
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "busybox"
}
]
}
}`),
expectedPatch: []byte(`{
"metadata": null
}`),
},
{
rawPolicy: []byte(`{
"spec": {
"containers": [
{
"(name)": "*",
"image": "gcr.io/google-containers/busybox:latest"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "busybox"
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"containers": [
{
"name": "hello",
"image": "gcr.io/google-containers/busybox:latest"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"spec": {
"containers": [
{
"(name)": "*",
"(image)": "gcr.io/google-containers/busybox:*",
"new_filed": "must be inserted"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello2"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "gcr.io/google-containers/busybox:latest"
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"containers": [{
"name": "hello",
"new_filed": "must be inserted"
}],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"spec": {
"containers": [
{
"(image)": "gcr.io/google-containers/busybox:latest"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello2"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "gcr.io/google-containers/busybox:latest"
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"containers": [
{
"name": "hello"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"spec": {
"containers": [
{
"(image)": "gcr.io/google-containers/busybox:*"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello2"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "gcr.io/google-containers/busybox:latest"
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"containers": [
{
"name": "hello"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
},
{
// only the third container does not match the given condition
rawPolicy: []byte(`{
"spec": {
"containers": [
{
"(image)": "gcr.io/google-containers/busybox:*"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "gcr.io/google-containers/busybox:latest"
},
{
"name": "hello2",
"image": "gcr.io/google-containers/busybox:latest"
},
{
"name": "hello3",
"image": "gcr.io/google-containers/nginx:latest"
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"containers": [
{
"name": "hello"
},
{
"name": "hello2"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"annotations": {
"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
},
"labels": {
"+(add-labels)": "add"
}
},
"spec": {
"volumes": [
{
"(hostPath)": {
"path": "*"
}
}
]
}
}`),
rawResource: []byte(`{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx"
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest",
"imagePullPolicy": "Never",
"volumeMounts": [
{
"mountPath": "/cache",
"name": "cache-volume"
}
]
}
],
"volumes": [
{
"name": "cache-volume",
"hostPath": {
"path": "/data",
"type": "Directory"
}
}
]
}
}`),
expectedPatch: []byte(`{
"metadata": {
"annotations": {
"cluster-autoscaler.kubernetes.io/safe-to-evict": true
},
"labels": {
"add-labels": "add"
}
},
"spec": {
"volumes": [
{
"name": "cache-volume"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"annotations": {
"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
}
},
"spec": {
"volumes": [
{
"(hostPath)": {
"path": "*"
}
}
]
}
}`),
rawResource: []byte(`{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx",
"annotations": {
"cluster-autoscaler.kubernetes.io/safe-to-evict": "false"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest",
"imagePullPolicy": "Never",
"volumeMounts": [
{
"mountPath": "/cache",
"name": "cache-volume"
}
]
}
],
"volumes": [
{
"name": "cache-volume",
"hostPath": {
"path": "/data",
"type": "Directory"
}
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"volumes": [
{
"name": "cache-volume"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"annotations": {
"+(alb.ingress.kubernetes.io/backend-protocol)": "HTTPS",
"+(alb.ingress.kubernetes.io/healthcheck-protocol)": "HTTPS",
"+(alb.ingress.kubernetes.io/scheme)": "internal",
"+(alb.ingress.kubernetes.io/target-type)": "ip",
"+(kubernetes.io/ingress.class)": "alb"
}
}
}`),
rawResource: []byte(`{
"apiVersion": "extensions/v1beta1",
"kind": "Ingress",
"metadata": {
"annotations": {
"alb.ingress.kubernetes.io/backend-protocol": "HTTPS",
"alb.ingress.kubernetes.io/healthcheck-protocol": "HTTPS",
"alb.ingress.kubernetes.io/scheme": "internal",
"alb.ingress.kubernetes.io/target-type": "ip",
"external-dns.alpha.kubernetes.io/hostname": "argo",
"kubernetes.io/ingress.class": "test"
},
"labels": {
"app": "argocd-server",
"app.kubernetes.io/name": "argocd-server"
},
"name": "argocd",
"namespace": "default"
}
}`),
expectedPatch: []byte(`{}`),
},
{
rawPolicy: []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(name)": "*",
"resources": {
"limits": {
"+(memory)": "300Mi",
"+(cpu)": "100"
}
}
}
]
}
}
}
}`),
rawResource: []byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "qos-demo",
"labels": {
"test": "qos"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "nginx"
}
},
"template": {
"metadata": {
"labels": {
"app": "nginx"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest",
"resources": {
"limits": {
"cpu": "50m"
}
}
}
]
}
}
}
}`),
expectedPatch: []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"resources": {
"limits": {
"memory": "300Mi"
}
},
"name": "nginx"
}
]
}
}
}
}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"annotations": {
"+(annotation1)": "atest1",
"+(annotation2)": "atest2"
},
"labels": {
"+(label1)": "test1"
}
},
"spec": {
"(volumes)": [
{
"(hostPath)": {
"path": "/var/run/docker.sock"
}
}
],
"containers": [
{
"(image)": "*:latest",
"command": [
"ls"
],
"imagePullPolicy": "Always"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"annotations": {
"annotation1": "atest2"
},
"labels": {
"label1": "test2",
"label2": "test2"
},
"name": "check-root-user"
},
"spec": {
"containers": [
{
"command": [
"ll"
],
"image": "nginx:latest",
"imagePullPolicy": "Never",
"name": "nginx"
},
{
"image": "busybox:latest",
"imagePullPolicy": "Never",
"name": "busybox"
}
],
"volumes": [
{
"hostPath": {
"path": "/var/run/docker.sock"
},
"name": "test-volume"
}
]
}
}`),
expectedPatch: []byte(`{
"metadata": {
"annotations": {
"annotation2": "atest2"
},
"labels": {}
},
"spec": {
"containers": [
{
"command": [
"ls"
],
"imagePullPolicy": "Always",
"name": "nginx"
},
{
"command": [
"ls"
],
"imagePullPolicy": "Always",
"name": "busybox"
}
]
}
}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"annotations": {
"+(annotation1)": "atest1",
}
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"annotations": {
"annotation1": "atest2"
},
"labels": {
"label1": "test2",
"label2": "test2"
},
"name": "check-root-user"
}
}`),
expectedPatch: []byte(`{}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"annotations": {
"annotation1": null
}
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"annotations": {
"annotation1": "atest2"
},
"labels": {
"label1": "test2",
"label2": "test2"
},
"name": "check-root-user"
}
}`),
expectedPatch: []byte(`{
"metadata": {
"annotations": {
"annotation1": null
}
}
}`),
},
{
rawPolicy: []byte(`{
"metadata": {
"labels": {
"(key1)": "value1",
}
},
"spec": {
"containers": [
{
"name": "busybox",
"image": "gcr.io/google-containers/busybox:latest"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
rawResource: []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello"
},
"spec": {
"containers": [
{
"name": "hello",
"image": "busybox"
}
]
}
}`),
expectedPatch: []byte(`{
"spec": {
"containers": [
{
"name": "busybox",
"image": "gcr.io/google-containers/busybox:latest"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`),
},
}
for i, test := range testCases {
t.Logf("Running test %d...", i+1)
preProcessedPolicy, err := preProcessStrategicMergePatch(log.Log, string(test.rawPolicy), string(test.rawResource))
assert.NilError(t, err)
output, err := preProcessedPolicy.MarshalJSON()
assert.NilError(t, err)
// has assertions inside
areEqualJSONs(t, test.expectedPatch, output)
}
}
func Test_FilterKeys_NoConditions(t *testing.T) {
patternRaw := []byte(`{
"key1": "value1",
"key2": "value2"
}`)
pattern := yaml.MustParse(string(patternRaw))
conditions, err := filterKeys(pattern, anchor.IsConditionAnchor)
assert.NilError(t, err)
assert.Equal(t, len(conditions), 0)
}
func Test_FilterKeys_ConditionsArePresent(t *testing.T) {
patternRaw := []byte(`{
"key1": "value1",
"(key2)": "value2",
"(key3)": "value3"
}`)
pattern := yaml.MustParse(string(patternRaw))
conditions, err := filterKeys(pattern, anchor.IsConditionAnchor)
assert.NilError(t, err)
assert.Equal(t, len(conditions), 2)
assert.Equal(t, conditions[0], "(key2)")
assert.Equal(t, conditions[1], "(key3)")
}
func Test_FilterKeys_EmptyList(t *testing.T) {
patternRaw := []byte(`{}`)
pattern := yaml.MustParse(string(patternRaw))
conditions, err := filterKeys(pattern, anchor.IsConditionAnchor)
assert.NilError(t, err)
assert.Equal(t, len(conditions), 0)
}
func Test_CheckConditionAnchor_Matches(t *testing.T) {
patternRaw := []byte(`{ "key1": "value*" }`)
resourceRaw := []byte(`{ "key1": "value1" }`)
pattern := yaml.MustParse(string(patternRaw))
resource := yaml.MustParse(string(resourceRaw))
err := checkCondition(log.Log, pattern, resource)
assert.NilError(t, err)
}
func Test_CheckConditionAnchor_DoesNotMatch(t *testing.T) {
patternRaw := []byte(`{ "key1": "value*" }`)
resourceRaw := []byte(`{ "key1": "sample" }`)
pattern := yaml.MustParse(string(patternRaw))
resource := yaml.MustParse(string(resourceRaw))
err := checkCondition(log.Log, pattern, resource)
assert.Error(t, err, "Condition failed: Validation rule failed at '/key1/' to validate value 'sample' with pattern 'value*'")
}
func Test_ValidateConditions_MapWithOneCondition_Matches(t *testing.T) {
patternRaw := []byte(`{
"(key1)": "value*",
"key2": "value2"
}`)
resourceRaw := []byte(`{
"key1": "value1",
"key2": "sample"
}`)
pattern := yaml.MustParse(string(patternRaw))
resource := yaml.MustParse(string(resourceRaw))
err := validateConditions(log.Log, pattern, resource)
assert.NilError(t, err)
}
func Test_ValidateConditions_MapWithOneCondition_DoesNotMatch(t *testing.T) {
patternRaw := []byte(`{
"(key1)": "value*",
"key2": "value2"
}`)
resourceRaw := []byte(`{
"key1": "text",
"key2": "sample"
}`)
pattern := yaml.MustParse(string(patternRaw))
resource := yaml.MustParse(string(resourceRaw))
err := validateConditions(log.Log, pattern, resource)
_, ok := err.(ConditionError)
assert.Assert(t, ok)
}
func Test_RenameField(t *testing.T) {
patternRaw := []byte(`{
"+(key1)": "value",
}`)
pattern := yaml.MustParse(string(patternRaw))
renameField("+(key1)", "key1", pattern)
actual := pattern.Field("key1").Value.YNode().Value
expected := "value"
fields, err := pattern.Fields()
assert.NilError(t, err)
assert.Equal(t, len(fields), 1)
assert.Equal(t, actual, expected)
}
func Test_RenameField_NonExisting(t *testing.T) {
patternRaw := []byte(`{
"+(key1)": "value",
}`)
pattern := yaml.MustParse(string(patternRaw))
renameField("non_existing_field", "key1", pattern)
actual := pattern.Field("+(key1)").Value.YNode().Value
expected := "value"
fields, err := pattern.Fields()
assert.NilError(t, err)
assert.Equal(t, len(fields), 1)
assert.Equal(t, actual, expected)
}
func Test_deleteRNode(t *testing.T) {
patternRaw := []byte(`{
"list": [
"first": {
"a": "b"
},
"second": {
"a": "b"
},
"third": {
"a": "b"
},
],
}`)
pattern := yaml.MustParse(string(patternRaw))
list := pattern.Field("list").Value
elements, err := list.Elements()
assert.NilError(t, err)
assert.Equal(t, len(elements), 3)
deleteListElement(list, 0)
elements, err = list.Elements()
assert.Equal(t, len(elements), 2)
}
func Test_DeleteConditions(t *testing.T) {
patternRaw := []byte(`{
"spec": {
"containers": [
{
"(name)": "*",
"image": "gcr.io/google-containers/busybox:latest"
},
{
"image": "gcr.io/google-containers/busybox:latest",
"name": "hello"
}
],
"imagePullSecrets": [
{
"name": "regcred"
}
]
}
}`)
pattern := yaml.MustParse(string(patternRaw))
containers, err := pattern.Field("spec").Value.Field("containers").Value.Elements()
assert.NilError(t, err)
assert.Equal(t, len(containers), 2)
_, err = deleteAnchors(pattern)
assert.NilError(t, err)
containers, err = pattern.Field("spec").Value.Field("containers").Value.Elements()
assert.NilError(t, err)
assert.Equal(t, len(containers), 1)
name := containers[0].Field("name").Value.YNode().Value
assert.Equal(t, name, "hello")
}
func Test_ConditionCheck_SeveralElementsMatchExceptOne(t *testing.T) {
patternRaw := []byte(`{
"containers": [
{
"(image)": "gcr.io/google-containers/busybox:*"
}
]
}`)
containersRaw := []byte(`{
"containers": [
{
"name": "hello",
"image": "gcr.io/google-containers/busybox:latest"
},
{
"name": "hello2",
"image": "gcr.io/google-containers/busybox:latest"
},
{
"name": "hello3",
"image": "gcr.io/google-containers/nginx:latest"
}
]
}`)
pattern := yaml.MustParse(string(patternRaw))
containers := yaml.MustParse(string(containersRaw))
err := preProcessPattern(log.Log, pattern, containers)
assert.NilError(t, err)
containersElements, err := pattern.Field("containers").Value.Elements()
assert.NilError(t, err)
assert.Equal(t, len(containersElements), 2)
}