mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
1e09f22e59
Signed-off-by: Arnaud Tournier <ltearno@gmail.com>
1317 lines
25 KiB
Go
1317 lines
25 KiB
Go
package patch
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/go-logr/logr"
|
|
"github.com/kyverno/kyverno/pkg/engine/anchor"
|
|
"gotest.tools/assert"
|
|
yaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
func Test_preProcessStrategicMergePatch_multipleAnchors(t *testing.T) {
|
|
testCases := []struct {
|
|
rawPolicy []byte
|
|
rawResource []byte
|
|
expectedPatch []byte
|
|
}{
|
|
{
|
|
rawPolicy: []byte(`{
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"(name)": "*",
|
|
"securityContext": {
|
|
"+(allowPrivilegeEscalation)": false,
|
|
"+(capabilities)": {
|
|
"drop": [
|
|
"NET_CAP"
|
|
]
|
|
},
|
|
"+(privileged)": false
|
|
}
|
|
}
|
|
],
|
|
"initContainers": [
|
|
{
|
|
"(name)": "*",
|
|
"securityContext": {
|
|
"+(allowPrivilegeEscalation)": false,
|
|
"+(capabilities)": {
|
|
"drop": [
|
|
"NET_ADMIN"
|
|
]
|
|
},
|
|
"+(privileged)": false
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
rawResource: []byte(`{
|
|
"apiVersion": "v1",
|
|
"kind": "Pod",
|
|
"metadata": {
|
|
"name": "mutation-debug",
|
|
"namespace": "amritapuri"
|
|
},
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"name": "sleepy-container-1",
|
|
"image": "docker.io/library/ubuntu"
|
|
},
|
|
{
|
|
"name": "sleepy-container-2",
|
|
"image": "docker.io/library/ubuntu"
|
|
}
|
|
],
|
|
"initContainers": [
|
|
{
|
|
"name": "init-container-1",
|
|
"image": "docker.io/library/ubuntu"
|
|
},
|
|
{
|
|
"name": "init-container-2",
|
|
"image": "docker.io/library/ubuntu"
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
expectedPatch: []byte(`{
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"name": "sleepy-container-1",
|
|
"securityContext": {
|
|
"allowPrivilegeEscalation": false,
|
|
"capabilities": {
|
|
"drop": [
|
|
"NET_CAP"
|
|
]
|
|
},
|
|
"privileged": false
|
|
}
|
|
},
|
|
{
|
|
"name": "sleepy-container-2",
|
|
"securityContext": {
|
|
"allowPrivilegeEscalation": false,
|
|
"capabilities": {
|
|
"drop": [
|
|
"NET_CAP"
|
|
]
|
|
},
|
|
"privileged": false
|
|
}
|
|
}
|
|
],
|
|
"initContainers": [
|
|
{
|
|
"name": "init-container-1",
|
|
"securityContext": {
|
|
"allowPrivilegeEscalation": false,
|
|
"capabilities": {
|
|
"drop": [
|
|
"NET_ADMIN"
|
|
]
|
|
},
|
|
"privileged": false
|
|
}
|
|
},
|
|
{
|
|
"name": "init-container-2",
|
|
"securityContext": {
|
|
"allowPrivilegeEscalation": false,
|
|
"capabilities": {
|
|
"drop": [
|
|
"NET_ADMIN"
|
|
]
|
|
},
|
|
"privileged": false
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
},
|
|
{
|
|
rawPolicy: []byte(`{
|
|
"metadata": {
|
|
"annotations": {
|
|
"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": "true"
|
|
}
|
|
},
|
|
"spec": {
|
|
"volumes": [
|
|
{
|
|
"<(emptyDir)": {}
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
rawResource: []byte(`{
|
|
"apiVersion": "v1",
|
|
"kind": "Pod",
|
|
"metadata": {
|
|
"name": "static-web",
|
|
"labels": {
|
|
"role": "myrole"
|
|
}
|
|
},
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"name": "web",
|
|
"image": "1nginx"
|
|
}
|
|
],
|
|
"volumes": [
|
|
{
|
|
"emptyDir": {},
|
|
"name": "cache-volume"
|
|
},
|
|
{
|
|
"secret": {
|
|
"secretName": "default-token-6gplg"
|
|
},
|
|
"name": "default-token-6gplg"
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
expectedPatch: []byte(`{
|
|
"metadata": {
|
|
"annotations": {
|
|
"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
{
|
|
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": {
|
|
"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": {
|
|
"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": {
|
|
"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"
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
{
|
|
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(`{}`),
|
|
},
|
|
{
|
|
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"
|
|
}
|
|
},
|
|
"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": {
|
|
"annotations": {
|
|
"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
|
|
}
|
|
},
|
|
"spec": {
|
|
"volumes": [
|
|
{
|
|
"hostPath": {
|
|
"<(path)": "*data"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
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
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
{
|
|
rawPolicy: []byte(`{
|
|
"spec": {
|
|
"securityContext": {
|
|
"runAsNonRoot": true
|
|
},
|
|
"initContainers": [
|
|
{
|
|
"(name)": "*",
|
|
"securityContext": {
|
|
"runAsNonRoot": true
|
|
}
|
|
}
|
|
],
|
|
"containers": [
|
|
{
|
|
"(name)": "*",
|
|
"securityContext": {
|
|
"runAsNonRoot": true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
rawResource: []byte(`{
|
|
"spec":{
|
|
"initContainers":[
|
|
{
|
|
"name":"initbusy",
|
|
"image":"busybox:1.28",
|
|
"command":[
|
|
"sleep",
|
|
"9999"
|
|
]
|
|
}
|
|
],
|
|
"containers":[
|
|
{
|
|
"image":"busybox:1.28",
|
|
"name":"busybox",
|
|
"command":[
|
|
"sleep",
|
|
"9999"
|
|
]
|
|
}
|
|
],
|
|
"affinity":{
|
|
"podAntiAffinity":{
|
|
"requiredDuringSchedulingIgnoredDuringExecution":[
|
|
{
|
|
"labelSelector":{
|
|
"matchExpressions":[
|
|
{
|
|
"key":"app",
|
|
"operator":"In",
|
|
"values":[
|
|
"foo",
|
|
"bar"
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"topologyKey":"kubernetes.io/hostname"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}`),
|
|
expectedPatch: []byte(`{
|
|
"spec": {
|
|
"securityContext": {
|
|
"runAsNonRoot": true
|
|
},
|
|
"initContainers": [
|
|
{
|
|
"name": "initbusy",
|
|
"securityContext": {
|
|
"runAsNonRoot": true
|
|
}
|
|
}
|
|
],
|
|
"containers": [
|
|
{
|
|
"name": "busybox",
|
|
"securityContext": {
|
|
"runAsNonRoot": true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}`),
|
|
},
|
|
}
|
|
|
|
for i, test := range testCases {
|
|
t.Logf("Running test %d...", i)
|
|
preProcessedPolicy, err := preProcessStrategicMergePatch(logr.Discard(), string(test.rawPolicy), string(test.rawResource))
|
|
assert.NilError(t, err)
|
|
|
|
output, err := preProcessedPolicy.MarshalJSON()
|
|
assert.NilError(t, err)
|
|
|
|
assert.DeepEqual(t, toJSON(t, []byte(test.expectedPatch)), toJSON(t, output))
|
|
}
|
|
}
|
|
|
|
func toJSON(t *testing.T, b []byte) interface{} {
|
|
var i interface{}
|
|
err := json.Unmarshal(b, &i)
|
|
assert.NilError(t, err)
|
|
return i
|
|
}
|
|
|
|
func Test_FilterKeys_NoConditions(t *testing.T) {
|
|
patternRaw := []byte(`{
|
|
"key1": "value1",
|
|
"key2": "value2"
|
|
}`)
|
|
|
|
pattern := yaml.MustParse(string(patternRaw))
|
|
conditions, err := filterKeys(pattern, anchor.IsCondition)
|
|
|
|
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.IsCondition)
|
|
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, len(conditions), 2)
|
|
assert.Equal(t, conditions[0].String(), "(key2)")
|
|
assert.Equal(t, conditions[1].String(), "(key3)")
|
|
}
|
|
|
|
func Test_FilterKeys_EmptyList(t *testing.T) {
|
|
patternRaw := []byte(`{}`)
|
|
pattern := yaml.MustParse(string(patternRaw))
|
|
conditions, err := filterKeys(pattern, anchor.IsCondition)
|
|
|
|
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(logr.Discard(), pattern, resource)
|
|
assert.Equal(t, err, nil)
|
|
}
|
|
|
|
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(logr.Discard(), pattern, resource)
|
|
assert.Error(t, err, "resource value 'sample' does not match 'value*' at path /key1/")
|
|
}
|
|
|
|
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(logr.Discard(), 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(logr.Discard(), 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.NilError(t, err)
|
|
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, false, false)
|
|
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": [
|
|
{
|
|
"(name)": "hello?",
|
|
"image": "gcr.io/google-containers/busybox:1"
|
|
}
|
|
]
|
|
}`)
|
|
|
|
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(logr.Discard(), pattern, containers)
|
|
assert.NilError(t, err)
|
|
|
|
patternContainers := pattern.Field("containers")
|
|
assert.Assert(t, patternContainers != nil)
|
|
assert.Assert(t, patternContainers.Value != nil)
|
|
|
|
elements, err := patternContainers.Value.Elements()
|
|
assert.NilError(t, err)
|
|
|
|
assert.Equal(t, len(elements), 2)
|
|
}
|
|
|
|
func Test_NonExistingKeyMustFailPreprocessing(t *testing.T) {
|
|
rawPattern := []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"
|
|
}
|
|
]
|
|
}
|
|
}`)
|
|
|
|
pattern := yaml.MustParse(string(rawPattern))
|
|
resource := yaml.MustParse(string(rawResource))
|
|
err := PreProcessPattern(logr.Discard(), pattern, resource)
|
|
assert.Error(t, err, "condition failed: could not found \"key1\" key in the resource")
|
|
}
|
|
|
|
func Test_NestedConditionals(t *testing.T) {
|
|
rawPattern := `{"spec":{"template":{"spec":{"volumes":[{"(emptyDir)":{"+(sizeLimit)":"20Mi"},"name":"*"}]}}}}`
|
|
rawResource := `{"spec":{"template":{"spec":{"volumes":[{"emptyDir":{},"name":"vol02"}]}}}}`
|
|
expectedPattern := `{"spec":{"template":{"spec":{"volumes":[{"emptyDir":{"sizeLimit":"20Mi"},"name":"vol02"}]}}}}`
|
|
|
|
pattern := yaml.MustParse(rawPattern)
|
|
resource := yaml.MustParse(rawResource)
|
|
err := PreProcessPattern(logr.Discard(), pattern, resource)
|
|
assert.NilError(t, err)
|
|
resultPattern, _ := pattern.String()
|
|
|
|
assert.DeepEqual(t, toJSON(t, []byte(expectedPattern)), toJSON(t, []byte(resultPattern)))
|
|
}
|
|
|
|
func Test_GlobalCondition_Fail(t *testing.T) {
|
|
rawPattern := []byte(`{
|
|
"metadata": {
|
|
"annotations": {
|
|
"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": "true"
|
|
}
|
|
},
|
|
"spec": {
|
|
"volumes": [
|
|
{
|
|
"<(emptyDir)": {}
|
|
}
|
|
]
|
|
}
|
|
}`)
|
|
rawResource := []byte(`{
|
|
"apiVersion": "v1",
|
|
"kind": "Pod",
|
|
"metadata": {
|
|
"name": "pod-without-emptydir-hostpath"
|
|
},
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"name": "nginx",
|
|
"image": "nginx"
|
|
}
|
|
]
|
|
}
|
|
}`)
|
|
|
|
pattern := yaml.MustParse(string(rawPattern))
|
|
resource := yaml.MustParse(string(rawResource))
|
|
err := PreProcessPattern(logr.Discard(), pattern, resource)
|
|
assert.Error(t, err, "global condition failed: could not found \"emptyDir\" key in the resource")
|
|
}
|