package utils

import (
	"encoding/json"
	"testing"

	authenticationv1 "k8s.io/api/authentication/v1"
	rbacv1 "k8s.io/api/rbac/v1"

	v1 "github.com/kyverno/kyverno/api/kyverno/v1"
	"github.com/kyverno/kyverno/api/kyverno/v1beta1"
	"github.com/kyverno/kyverno/pkg/autogen"
	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestMatchesResourceDescription(t *testing.T) {
	tcs := []struct {
		Description       string
		AdmissionInfo     v1beta1.RequestInfo
		Resource          []byte
		Policy            []byte
		areErrorsExpected bool
	}{
		{
			Description: "Match Any matches the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "any-match-rule",
							"match": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["dev"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Match Any does not match the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "abc",
					"namespace" : "default"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["dev"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Match All matches the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["abc"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Match All does not match the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["xyz"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Exclude Any excludes the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "dev",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"dev"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"default"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Exclude Any does not exclude the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"dev"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"default"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Exclude All excludes the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "dev",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"dev"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"prod"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Exclude All does not exclude the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"name": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"abc"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"default"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should match pod and not exclude it",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should exclude resource since it matches the exclude block",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"system:node"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description:       "Should not fail if in sync mode, if admission info is empty it should still match resources with specific clusterRoles",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description:       "Should fail since resource does not match because of names field",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"],"names": ["dev-*"]},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description: "Should pass since resource matches a name in the names field",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"system:node"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"],"names": ["dev-*","hello-world"]},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description:       "Should fail since resource gets excluded because of the names field",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"names": ["dev-*","hello-*"]}},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description:       "Should pass since resource does not get excluded because of the names field",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"bye-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"names": ["dev-*","hello-*"]}},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should fail since resource does not match policy",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Service","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description: "Should not fail since resource does not match exclude block",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"system:node"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hello-world2","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should pass since group, version, kind match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "name": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } }, { "name": "check-cpu-memory-limits", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "validate": { "message": "Resource limits are required for CPU and memory", "pattern": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "memory": "?*", "cpu": "?*" } } } ] } } } } } } ] } }`),
			areErrorsExpected: false,
		},
		{
			Description: "Should pass since version and kind match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "myapp-pod2", "labels": { "app": "myapp2" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx" } ] } }`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-latest-tag", "annotations": { "policies.kyverno.io/category": "Workload Isolation", "policies.kyverno.io/description": "The ':latest' tag is mutable and can lead to unexpected errors if the image changes. A best practice is to use an immutable tag that maps to a specific version of an application pod." } }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "require-image-tag", "match": { "resources": { "kinds": [ "v1/Pod" ] } }, "validate": { "message": "An image tag is required", "pattern": { "spec": { "containers": [ { "image": "*:*" } ] } } } } ] } }`),
			areErrorsExpected: false,
		},
		{
			Description: "Should fail since resource does not match ",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Service","metadata":{"name":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description: "Should fail since version not match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "apps/v1beta1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "name": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } }, { "name": "check-cpu-memory-limits", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "validate": { "message": "Resource limits are required for CPU and memory", "pattern": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "memory": "?*", "cpu": "?*" } } } ] } } } } } } ] } }`),
			areErrorsExpected: true,
		},
		{
			Description: "Should fail since cluster role version not match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "kind": "ClusterRole", "apiVersion": "rbac.authorization.k8s.io/v1", "metadata": { "name": "secret-reader-demo", "namespace": "default" }, "rules": [ { "apiGroups": [ "" ], "resources": [ "secrets" ], "verbs": [ "get", "watch", "list" ] } ] }`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "check-host-path" }, "spec": { "validationFailureAction": "enforce", "background": true, "rules": [ { "name": "check-host-path", "match": { "resources": { "kinds": [ "rbac.authorization.k8s.io/v1beta1/ClusterRole" ] } }, "validate": { "message": "Host path is not allowed", "pattern": { "spec": { "volumes": [ { "name": "*", "hostPath": { "path": "" } } ] } } } } ] } }`),
			areErrorsExpected: true,
		},
		{
			Description: "Test for GVK case sensitive",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "myapp-pod2", "labels": { "app": "myapp2" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx" } ] } }`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-latest-tag", "annotations": { "policies.kyverno.io/category": "Workload Isolation", "policies.kyverno.io/description": "The ':latest' tag is mutable and can lead to unexpected errors if the image changes. A best practice is to use an immutable tag that maps to a specific version of an application pod." } }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "require-image-tag", "match": { "resources": { "kinds": [ "pod" ] } }, "validate": { "message": "An image tag is required", "pattern": { "spec": { "containers": [ { "image": "*:*" } ] } } } } ] } }`),
			areErrorsExpected: true,
		},
		{
			Description: "Test should fail for GVK case sensitive",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "name": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } } ] } }`),
			areErrorsExpected: true,
		},
	}

	for i, tc := range tcs {
		var policy v1.Policy
		err := json.Unmarshal(tc.Policy, &policy)
		if err != nil {
			t.Errorf("Testcase %d invalid policy raw", i+1)
		}
		resource, _ := kubeutils.BytesToUnstructured(tc.Resource)

		for _, rule := range autogen.ComputeRules(&policy) {
			err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, nil, "", resource.GroupVersionKind(), "", "CREATE")
			if err != nil {
				if !tc.areErrorsExpected {
					t.Errorf("Testcase %d Unexpected error: %v\nmsg: %s", i+1, err, tc.Description)
				}
			} else {
				if tc.areErrorsExpected {
					t.Errorf("Testcase %d Expected Error but received no error", i+1)
				}
			}
		}
	}
}

func TestMatchesResourceDescription_GenerateName(t *testing.T) {
	tcs := []struct {
		Description       string
		AdmissionInfo     v1beta1.RequestInfo
		Resource          []byte
		Policy            []byte
		areErrorsExpected bool
	}{
		{
			Description: "Match Any matches the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "any-match-rule",
							"match": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["dev"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Match Any does not match the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "abc",
					"namespace" : "default"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["dev"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Match All matches the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["abc"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Match All does not match the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names" : ["xyz"]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces" : ["prod"]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Exclude Any excludes the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "dev",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"dev"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"default"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Exclude Any does not exclude the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"any": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"dev"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"default"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Exclude All excludes the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "dev",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"dev"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"prod"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: true,
		},
		{
			Description: "Exclude All does not exclude the Pod",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"generateName": "abc",
					"namespace" : "prod"
				},
				"spec": {
					"containers": [
						{
							"name": "cont-name",
							"image": "cont-img",
							"ports": [
								{
									"containerPort": 81
								}
							],
							"resources": {
								"limits": {
									"memory": "30Mi",
									"cpu": "0.2"
								},
								"requests": {
									"memory": "20Mi",
									"cpu": "0.1"
								}
							}
						}
					]
				}
			}`),
			Policy: []byte(`{
				"apiVersion": "kyverno.io/v1",
				"kind": "ClusterPolicy",
				"metadata": {
					"name": "test-policy"
				},
				"spec": {
					"background": false,
					"rules": [
						{
							"name": "test-rule",
							"match": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											]
										}
									}
								]
							},
							"exclude": {
								"all": [
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"names": [
												"abc"
											]
										}
									},
									{
										"resources": {
											"kinds": [
												"Pod"
											],
											"namespaces": [
												"default"
											]
										}
									}
								]
							},
							"mutate": {
								"overlay": {
									"spec": {
										"containers": [
											{
												"(image)": "*",
												"imagePullPolicy": "IfNotPresent"
											}
										]
									}
								}
							}
						}
					]
				}
			}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should match pod and not exclude it",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should exclude resource since it matches the exclude block",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"system:node"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description:       "Should not fail if in sync mode, if admission info is empty it should still match resources with specific clusterRoles",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description:       "Should fail since resource does not match because of names field",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"],"names": ["dev-*"]},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description: "Should pass since resource matches a name in the names field",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"system:node"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"],"names": ["dev-*","hello-world"]},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description:       "Should fail since resource gets excluded because of the names field",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"names": ["dev-*","hello-*"]}},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description:       "Should pass since resource does not get excluded because of the names field",
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"bye-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"names": ["dev-*","hello-*"]}},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should fail since resource does not match policy",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Service","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description: "Should not fail since resource does not match exclude block",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"system:node"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"generateName":"hello-world2","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: false,
		},
		{
			Description: "Should pass since group, version, kind match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "generateName": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } }, { "name": "check-cpu-memory-limits", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "validate": { "message": "Resource limits are required for CPU and memory", "pattern": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "memory": "?*", "cpu": "?*" } } } ] } } } } } } ] } }`),
			areErrorsExpected: false,
		},
		{
			Description: "Should pass since version and kind match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "v1", "kind": "Pod", "metadata": { "generateName": "myapp-pod2", "labels": { "app": "myapp2" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx" } ] } }`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-latest-tag", "annotations": { "policies.kyverno.io/category": "Workload Isolation", "policies.kyverno.io/description": "The ':latest' tag is mutable and can lead to unexpected errors if the image changes. A best practice is to use an immutable tag that maps to a specific version of an application pod." } }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "require-image-tag", "match": { "resources": { "kinds": [ "v1/Pod" ] } }, "validate": { "message": "An image tag is required", "pattern": { "spec": { "containers": [ { "image": "*:*" } ] } } } } ] } }`),
			areErrorsExpected: false,
		},
		{
			Description: "Should fail since resource does not match ",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{"apiVersion":"v1","kind":"Service","metadata":{"generateName":"hello-world","labels":{"name":"hello-world"}},"spec":{"containers":[{"name":"hello-world","image":"hello-world","ports":[{"containerPort":81}],"resources":{"limits":{"memory":"30Mi","cpu":"0.2"},"requests":{"memory":"20Mi","cpu":"0.1"}}}]}}`),
			Policy:            []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"hello-world-policy"},"spec":{"background":false,"rules":[{"name":"hello-world-policy","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"name":"hello-world"},"clusterRoles":["system:node"]},"mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}]}}`),
			areErrorsExpected: true,
		},
		{
			Description: "Should fail since version not match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "apps/v1beta1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "generateName": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } }, { "name": "check-cpu-memory-limits", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "validate": { "message": "Resource limits are required for CPU and memory", "pattern": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "memory": "?*", "cpu": "?*" } } } ] } } } } } } ] } }`),
			areErrorsExpected: true,
		},
		{
			Description: "Should fail since cluster role version not match",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "kind": "ClusterRole", "apiVersion": "rbac.authorization.k8s.io/v1", "metadata": { "generateName": "secret-reader-demo", "namespace": "default" }, "rules": [ { "apiGroups": [ "" ], "resources": [ "secrets" ], "verbs": [ "get", "watch", "list" ] } ] }`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "check-host-path" }, "spec": { "validationFailureAction": "enforce", "background": true, "rules": [ { "name": "check-host-path", "match": { "resources": { "kinds": [ "rbac.authorization.k8s.io/v1beta1/ClusterRole" ] } }, "validate": { "message": "Host path is not allowed", "pattern": { "spec": { "volumes": [ { "name": "*", "hostPath": { "path": "" } } ] } } } } ] } }`),
			areErrorsExpected: true,
		},
		{
			Description: "Test for GVK case sensitive",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "v1", "kind": "Pod", "metadata": { "generateName": "myapp-pod2", "labels": { "app": "myapp2" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx" } ] } }`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-latest-tag", "annotations": { "policies.kyverno.io/category": "Workload Isolation", "policies.kyverno.io/description": "The ':latest' tag is mutable and can lead to unexpected errors if the image changes. A best practice is to use an immutable tag that maps to a specific version of an application pod." } }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "require-image-tag", "match": { "resources": { "kinds": [ "pod" ] } }, "validate": { "message": "An image tag is required", "pattern": { "spec": { "containers": [ { "image": "*:*" } ] } } } } ] } }`),
			areErrorsExpected: true,
		},
		{
			Description: "Test should fail for GVK case sensitive",
			AdmissionInfo: v1beta1.RequestInfo{
				ClusterRoles: []string{"admin"},
			},
			Resource:          []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "generateName": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
			Policy:            []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } } ] } }`),
			areErrorsExpected: true,
		},
	}

	for i, tc := range tcs {
		var policy v1.Policy
		err := json.Unmarshal(tc.Policy, &policy)
		if err != nil {
			t.Errorf("Testcase %d invalid policy raw", i+1)
		}
		resource, _ := kubeutils.BytesToUnstructured(tc.Resource)

		for _, rule := range autogen.ComputeRules(&policy) {
			err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, nil, "", resource.GroupVersionKind(), "", "CREATE")
			if err != nil {
				if !tc.areErrorsExpected {
					t.Errorf("Testcase %d Unexpected error: %v\nmsg: %s", i+1, err, tc.Description)
				}
			} else {
				if tc.areErrorsExpected {
					t.Errorf("Testcase %d Expected Error but received no error", i+1)
				}
			}
		}
	}
}

// Match multiple kinds
func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "name": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment", "Pods"},
		Selector: &metav1.LabelSelector{
			MatchLabels:      nil,
			MatchExpressions: nil,
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {

	// slightly simplified ingress controller pod that lives in the ingress-nginx namespace
	rawResource := []byte(`{
		"apiVersion": "v1",
		"kind": "Pod",
		"metadata": {
			"name": "ingress-nginx-controller-57bc6474bb-mcpt2",
			"namespace": "ingress-nginx"
		},
		"spec": {
			"containers": [
				{
					"args": ["/nginx-ingress-controller"],
					"env": [],
					"image": "registry.k8s.io/ingress-nginx/controller:v1.5.1",
					"imagePullPolicy": "IfNotPresent",
					"name": "controller",
					"securityContext": {
						"allowPrivilegeEscalation": true,
						"capabilities": {
							"add": [
								"NET_BIND_SERVICE"
							],
							"drop": [
								"ALL"
							]
						},
						"runAsUser": 101
					},
					"volumeMounts": [
						{
							"mountPath": "/usr/local/certificates/",
							"name": "webhook-cert",
							"readOnly": true
						},
						{
							"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
							"name": "kube-api-access-vc2qz",
							"readOnly": true
						}
					]
				}
			],
			"securityContext": {},
			"serviceAccount": "ingress-nginx",
			"serviceAccountName": "ingress-nginx"
		}
	}`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}

	// this rule should match only pods in the user1-restricted namespace, and also pods of User:user1.
	rule := v1.Rule{
		MatchResources: v1.MatchResources{Any: v1.ResourceFilters{
			// pods in user1-restricted namespace
			v1.ResourceFilter{
				ResourceDescription: v1.ResourceDescription{
					Kinds: []string{"Pod"},
					Namespaces: []string{
						"user1-restricted",
					},
				},
			},
			// pods for User user1 account
			v1.ResourceFilter{
				ResourceDescription: v1.ResourceDescription{
					Kinds: []string{"Pod"},
				},
				UserInfo: v1.UserInfo{
					Subjects: []rbacv1.Subject{
						{
							Kind: "User",
							Name: "user1",
						},
					},
				},
			},
		}},
		ExcludeResources: v1.MatchResources{},
	}

	// this is the request info that was also passed with the mocked pod
	requestInfo := v1beta1.RequestInfo{
		AdmissionUserInfo: authenticationv1.UserInfo{
			Username: "system:serviceaccount:kube-system:replicaset-controller",
			UID:      "8f36cad4-eb68-4931-bea8-8a42dd1fee4c",
			Groups: []string{
				"system:serviceaccounts",
				"system:serviceaccounts:kube-system",
				"system:authenticated",
			},
		},
	}

	// First test: confirm that this above rule produces errors (and raise an error if err == nil)
	if err := MatchesResourceDescription(*resource, rule, requestInfo, nil, "", resource.GroupVersionKind(), "", "CREATE"); err == nil {
		t.Error("Testcase was expected to fail, but err was nil")
	}

	// This next rule *should* match, because we explicitly match the ingress-nginx namespace this time.
	rule2 := v1.Rule{
		MatchResources: v1.MatchResources{Any: v1.ResourceFilters{
			v1.ResourceFilter{
				ResourceDescription: v1.ResourceDescription{
					Kinds: []string{"Pod"},
					Namespaces: []string{
						"ingress-nginx",
					},
				},
			},
		}},
		ExcludeResources: v1.MatchResources{Any: v1.ResourceFilters{}},
	}

	// Second test: confirm that matching this rule does not create any errors (and raise if err != nil)
	if err := MatchesResourceDescription(*resource, rule2, requestInfo, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase was expected to not fail, but err was %s", err)
	}

	// Now we extend the previous rule to have an Exclude part. Making it 'not-empty' should make the exclude-code run.
	rule2.ExcludeResources = v1.MatchResources{Any: v1.ResourceFilters{
		v1.ResourceFilter{
			ResourceDescription: v1.ResourceDescription{
				Kinds: []string{"Pod"},
			},
		},
	}}

	// Third test: confirm that now the custom exclude-snippet should run in CheckSubjects() and that should result in this rule failing (raise if err == nil for that reason)
	if err := MatchesResourceDescription(*resource, rule2, requestInfo, nil, "", resource.GroupVersionKind(), "", "CREATE"); err == nil {
		t.Error("Testcase was expected to fail, but err was nil #1!")
	}
}

// Match resource name
func TestResourceDescriptionMatch_Name(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "name": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-deployment",
		Selector: &metav1.LabelSelector{
			MatchLabels:      nil,
			MatchExpressions: nil,
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

func TestResourceDescriptionMatch_GenerateName(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "generateName": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-deployment",
		Selector: &metav1.LabelSelector{
			MatchLabels:      nil,
			MatchExpressions: nil,
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

// Match resource regex
func TestResourceDescriptionMatch_Name_Regex(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "name": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-*",
		Selector: &metav1.LabelSelector{
			MatchLabels:      nil,
			MatchExpressions: nil,
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

func TestResourceDescriptionMatch_GenerateName_Regex(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "generateName": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-*",
		Selector: &metav1.LabelSelector{
			MatchLabels:      nil,
			MatchExpressions: nil,
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

// Match expressions for labels to not match
func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "name": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-*",
		Selector: &metav1.LabelSelector{
			MatchLabels: nil,
			MatchExpressions: []metav1.LabelSelectorRequirement{
				{
					Key:      "label2",
					Operator: "NotIn",
					Values: []string{
						"sometest1",
					},
				},
			},
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

// Match label expression in matching set
func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "name": "nginx-deployment",
		   "labels": {
			  "app": "nginx"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-*",
		Selector: &metav1.LabelSelector{
			MatchLabels: nil,
			MatchExpressions: []metav1.LabelSelectorRequirement{
				{
					Key:      "app",
					Operator: "NotIn",
					Values: []string{
						"nginx1",
						"nginx2",
					},
				},
			},
		},
	}
	rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err != nil {
		t.Errorf("Testcase has failed due to the following:%v", err)
	}
}

// check for exclude conditions
func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
	rawResource := []byte(`{
		"apiVersion": "apps/v1",
		"kind": "Deployment",
		"metadata": {
		   "name": "nginx-deployment",
		   "labels": {
			  "app": "nginx",
			  "block": "true"
		   }
		},
		"spec": {
		   "replicas": 3,
		   "selector": {
			  "matchLabels": {
				 "app": "nginx"
			  }
		   },
		   "template": {
			  "metadata": {
				 "labels": {
					"app": "nginx"
				 }
			  },
			  "spec": {
				 "containers": [
					{
					   "name": "nginx",
					   "image": "nginx:1.7.9",
					   "ports": [
						  {
							 "containerPort": 80
						  }
					   ]
					}
				 ]
			  }
		   }
		}
	 }`)
	resource, err := kubeutils.BytesToUnstructured(rawResource)
	if err != nil {
		t.Errorf("unable to convert raw resource to unstructured: %v", err)
	}
	resourceDescription := v1.ResourceDescription{
		Kinds: []string{"Deployment"},
		Name:  "nginx-*",
		Selector: &metav1.LabelSelector{
			MatchLabels: nil,
			MatchExpressions: []metav1.LabelSelectorRequirement{
				{
					Key:      "app",
					Operator: "NotIn",
					Values: []string{
						"nginx1",
						"nginx2",
					},
				},
			},
		},
	}

	resourceDescriptionExclude := v1.ResourceDescription{
		Selector: &metav1.LabelSelector{
			MatchLabels: map[string]string{
				"block": "true",
			},
		},
	}

	rule := v1.Rule{
		MatchResources:   v1.MatchResources{ResourceDescription: resourceDescription},
		ExcludeResources: v1.MatchResources{ResourceDescription: resourceDescriptionExclude},
	}

	if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err == nil {
		t.Errorf("Testcase has failed due to the following:\n Function has returned no error, even though it was supposed to fail")
	}
}
