2022-10-12 08:52:42 +02:00
package webhook
import (
2022-10-27 15:35:32 +05:30
"encoding/json"
2024-01-31 21:16:53 +05:30
"reflect"
"sort"
2022-10-12 08:52:42 +02:00
"testing"
2024-04-29 18:40:39 +08:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-10-16 15:24:37 +02:00
"github.com/kyverno/kyverno/pkg/autogen"
2022-10-12 08:52:42 +02:00
"gotest.tools/assert"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
2024-04-30 19:05:44 +02:00
corev1 "k8s.io/api/core/v1"
2023-03-13 10:27:49 +01:00
"k8s.io/apimachinery/pkg/runtime/schema"
2024-04-30 19:05:44 +02:00
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/ptr"
2022-10-12 08:52:42 +02:00
)
func Test_webhook_isEmpty ( t * testing . T ) {
2024-01-27 21:00:22 +08:00
empty := newWebhook ( DefaultWebhookTimeout , admissionregistrationv1 . Ignore , [ ] admissionregistrationv1 . MatchCondition { } )
2022-10-12 08:52:42 +02:00
assert . Equal ( t , empty . isEmpty ( ) , true )
2024-01-27 21:00:22 +08:00
notEmpty := newWebhook ( DefaultWebhookTimeout , admissionregistrationv1 . Ignore , [ ] admissionregistrationv1 . MatchCondition { } )
2024-01-26 16:07:42 +01:00
notEmpty . set ( GroupVersionResourceScope {
GroupVersionResource : schema . GroupVersionResource { Group : "" , Version : "v1" , Resource : "pods" } ,
Scope : admissionregistrationv1 . NamespacedScope ,
2023-03-13 15:44:39 +01:00
} )
2022-10-12 08:52:42 +02:00
assert . Equal ( t , notEmpty . isEmpty ( ) , false )
}
2022-10-27 15:35:32 +05:30
var policy = `
{
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"name" : "disallow-unsigned-images"
} ,
"spec" : {
"background" : false ,
"rules" : [
{
"name" : "replace-image-registry" ,
"match" : {
"any" : [
{
"resources" : {
"kinds" : [
"Pod"
]
}
}
]
} ,
"mutate" : {
"foreach" : [
{
"list" : "request.object.spec.containers" ,
"patchStrategicMerge" : {
"spec" : {
"containers" : [
{
"name" : "{{ element.name }}" ,
"image" : "{{ regex_replace_all_literal('.*(.*)/', '{{element.image}}', 'pratikrshah/' )}}"
}
]
}
}
}
]
}
} ,
{
"name" : "disallow-unsigned-images-rule" ,
"match" : {
"any" : [
{
"resources" : {
"kinds" : [
"Pod"
]
}
}
]
} ,
"verifyImages" : [
{
"imageReferences" : [
"*"
] ,
"verifyDigest" : false ,
"required" : null ,
"mutateDigest" : false ,
"attestors" : [
{
"count" : 1 ,
"entries" : [
{
"keys" : {
"publicKeys" : "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHsra9WSDxt9qv84KF4McNVCGjMFq\ne96mWCQxGimL9Ltj6F3iXmlo8sUalKfJ7SBXpy8hwyBfXBBAmCalsp5xEw==\n-----END PUBLIC KEY-----"
}
}
]
}
]
}
]
} ,
{
"name" : "check-image" ,
"match" : {
"any" : [
{
"resources" : {
"kinds" : [
"Pod"
]
}
}
]
} ,
"context" : [
{
"name" : "keys" ,
"configMap" : {
"name" : "keys" ,
"namespace" : "default"
}
}
] ,
"verifyImages" : [
{
"imageReferences" : [
"ghcr.io/myorg/myimage*"
] ,
"required" : true ,
"attestors" : [
{
"count" : 1 ,
"entries" : [
{
"keys" : {
"publicKeys" : "{{ keys.data.production }}"
}
}
]
}
]
}
]
}
]
}
}
`
func Test_RuleCount ( t * testing . T ) {
2024-04-29 18:40:39 +08:00
var cpol kyvernov1 . ClusterPolicy
2022-10-27 15:35:32 +05:30
err := json . Unmarshal ( [ ] byte ( policy ) , & cpol )
assert . NilError ( t , err )
status := cpol . GetStatus ( )
2024-10-16 15:24:37 +02:00
rules := autogen . Default . ComputeRules ( & cpol , "" )
2022-10-27 15:35:32 +05:30
setRuleCount ( rules , status )
assert . Equal ( t , status . RuleCount . Validate , 0 )
assert . Equal ( t , status . RuleCount . Generate , 0 )
assert . Equal ( t , status . RuleCount . Mutate , 1 )
assert . Equal ( t , status . RuleCount . VerifyImages , 2 )
}
2024-01-31 21:16:53 +05:30
2024-05-21 16:33:59 +08:00
func TestMergeOprations ( t * testing . T ) {
2024-01-31 21:16:53 +05:30
testCases := [ ] struct {
name string
inputMap map [ string ] bool
expectedResult [ ] admissionregistrationv1 . OperationType
} {
{
name : "Test Case 1" ,
inputMap : map [ string ] bool {
webhookCreate : true ,
webhookUpdate : false ,
webhookDelete : true ,
} ,
expectedResult : [ ] admissionregistrationv1 . OperationType { webhookCreate , webhookDelete } ,
} ,
{
name : "Test Case 2" ,
inputMap : map [ string ] bool {
webhookCreate : false ,
webhookUpdate : false ,
webhookDelete : false ,
webhookConnect : true ,
} ,
expectedResult : [ ] admissionregistrationv1 . OperationType { webhookConnect } ,
} ,
}
for _ , testCase := range testCases {
t . Run ( testCase . name , func ( t * testing . T ) {
2024-05-21 16:33:59 +08:00
result := mergeOperations ( testCase . inputMap , [ ] admissionregistrationv1 . OperationType { } )
2024-01-31 21:16:53 +05:30
sort . Slice ( result , func ( i , j int ) bool {
return result [ i ] < result [ j ]
} )
sort . Slice ( testCase . expectedResult , func ( i , j int ) bool {
return testCase . expectedResult [ i ] < testCase . expectedResult [ j ]
} )
if ! reflect . DeepEqual ( result , testCase . expectedResult ) {
t . Errorf ( "Expected %v, but got %v" , testCase . expectedResult , result )
}
} )
}
}
func TestComputeOperationsForMutatingWebhookConf ( t * testing . T ) {
testCases := [ ] struct {
name string
2024-04-29 18:40:39 +08:00
rules [ ] kyvernov1 . Rule
2024-01-31 21:16:53 +05:30
expectedResult map [ string ] bool
} {
{
name : "Test Case 1" ,
2024-04-29 18:40:39 +08:00
rules : [ ] kyvernov1 . Rule {
2024-01-31 21:16:53 +05:30
{
2024-09-11 03:32:10 +02:00
Mutation : & kyvernov1 . Mutation {
2024-01-31 21:16:53 +05:30
PatchesJSON6902 : "add" ,
} ,
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources {
ResourceDescription : kyvernov1 . ResourceDescription {
Operations : [ ] kyvernov1 . AdmissionOperation { webhookCreate } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
} ,
} ,
expectedResult : map [ string ] bool {
webhookCreate : true ,
} ,
} ,
{
name : "Test Case 2" ,
2024-04-29 18:40:39 +08:00
rules : [ ] kyvernov1 . Rule {
2024-01-31 21:16:53 +05:30
{
2024-09-11 03:32:10 +02:00
Mutation : & kyvernov1 . Mutation {
2024-01-31 21:16:53 +05:30
PatchesJSON6902 : "add" ,
} ,
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources { } ,
2024-09-10 13:14:49 +02:00
ExcludeResources : & kyvernov1 . MatchResources { } ,
2024-01-31 21:16:53 +05:30
} ,
{
2024-09-11 03:32:10 +02:00
Mutation : & kyvernov1 . Mutation {
2024-01-31 21:16:53 +05:30
PatchesJSON6902 : "add" ,
} ,
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources { } ,
2024-09-10 13:14:49 +02:00
ExcludeResources : & kyvernov1 . MatchResources { } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
expectedResult : map [ string ] bool {
webhookCreate : true ,
webhookUpdate : true ,
} ,
} ,
{
name : "Test Case 2" ,
2024-04-29 18:40:39 +08:00
rules : [ ] kyvernov1 . Rule {
2024-01-31 21:16:53 +05:30
{
2024-09-11 03:32:10 +02:00
Mutation : & kyvernov1 . Mutation {
2024-01-31 21:16:53 +05:30
PatchesJSON6902 : "add" ,
} ,
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources { } ,
2024-09-10 13:14:49 +02:00
ExcludeResources : & kyvernov1 . MatchResources {
2024-04-29 18:40:39 +08:00
ResourceDescription : kyvernov1 . ResourceDescription {
Operations : [ ] kyvernov1 . AdmissionOperation { webhookCreate } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
} ,
} ,
expectedResult : map [ string ] bool {
webhookCreate : false ,
webhookUpdate : true ,
} ,
} ,
}
for _ , testCase := range testCases {
t . Run ( testCase . name , func ( t * testing . T ) {
var result map [ string ] bool
for _ , r := range testCase . rules {
result = computeOperationsForMutatingWebhookConf ( r , make ( map [ string ] bool ) )
}
if ! reflect . DeepEqual ( result , testCase . expectedResult ) {
t . Errorf ( "Expected %v, but got %v" , testCase . expectedResult , result )
}
} )
}
}
func TestComputeOperationsForValidatingWebhookConf ( t * testing . T ) {
testCases := [ ] struct {
name string
2024-04-29 18:40:39 +08:00
rules [ ] kyvernov1 . Rule
2024-01-31 21:16:53 +05:30
expectedResult map [ string ] bool
} {
{
name : "Test Case 1" ,
2024-04-29 18:40:39 +08:00
rules : [ ] kyvernov1 . Rule {
2024-01-31 21:16:53 +05:30
{
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources {
ResourceDescription : kyvernov1 . ResourceDescription {
Operations : [ ] kyvernov1 . AdmissionOperation { webhookCreate } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
} ,
} ,
expectedResult : map [ string ] bool {
webhookCreate : true ,
} ,
} ,
{
name : "Test Case 2" ,
2024-04-29 18:40:39 +08:00
rules : [ ] kyvernov1 . Rule {
2024-01-31 21:16:53 +05:30
{
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources { } ,
2024-09-10 13:14:49 +02:00
ExcludeResources : & kyvernov1 . MatchResources { } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
expectedResult : map [ string ] bool {
webhookCreate : true ,
webhookUpdate : true ,
webhookConnect : true ,
webhookDelete : true ,
} ,
} ,
{
name : "Test Case 3" ,
2024-04-29 18:40:39 +08:00
rules : [ ] kyvernov1 . Rule {
2024-01-31 21:16:53 +05:30
{
2024-04-29 18:40:39 +08:00
MatchResources : kyvernov1 . MatchResources {
ResourceDescription : kyvernov1 . ResourceDescription {
Operations : [ ] kyvernov1 . AdmissionOperation { webhookCreate , webhookUpdate } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
2024-09-10 13:14:49 +02:00
ExcludeResources : & kyvernov1 . MatchResources {
2024-04-29 18:40:39 +08:00
ResourceDescription : kyvernov1 . ResourceDescription {
Operations : [ ] kyvernov1 . AdmissionOperation { webhookDelete } ,
2024-01-31 21:16:53 +05:30
} ,
} ,
} ,
} ,
expectedResult : map [ string ] bool {
webhookCreate : true ,
webhookUpdate : true ,
webhookDelete : false ,
} ,
} ,
}
for _ , testCase := range testCases {
t . Run ( testCase . name , func ( t * testing . T ) {
var result map [ string ] bool
for _ , r := range testCase . rules {
result = computeOperationsForValidatingWebhookConf ( r , make ( map [ string ] bool ) )
}
if ! reflect . DeepEqual ( result , testCase . expectedResult ) {
t . Errorf ( "Expected %v, but got %v" , testCase . expectedResult , result )
}
} )
}
}
2024-04-30 19:05:44 +02:00
func TestBuildRulesWithOperations ( t * testing . T ) {
testCases := [ ] struct {
name string
rules map [ groupVersionScope ] sets . Set [ string ]
mapResourceToOpnType map [ string ] [ ] admissionregistrationv1 . OperationType
expectedResult [ ] admissionregistrationv1 . RuleWithOperations
} {
{
name : "Test Case 1" ,
rules : map [ groupVersionScope ] sets . Set [ string ] {
groupVersionScope {
GroupVersion : corev1 . SchemeGroupVersion ,
scopeType : admissionregistrationv1 . NamespacedScope ,
} : {
"pods" : sets . Empty { } ,
"configmaps" : sets . Empty { } ,
} ,
} ,
mapResourceToOpnType : map [ string ] [ ] admissionregistrationv1 . OperationType {
2024-09-05 17:12:40 +05:30
"Pod" : { webhookCreate , webhookUpdate } ,
"ConfigMaps" : { webhookCreate } ,
2024-04-30 19:05:44 +02:00
} ,
expectedResult : [ ] admissionregistrationv1 . RuleWithOperations {
{
2024-09-05 17:12:40 +05:30
Operations : [ ] admissionregistrationv1 . OperationType { webhookCreate } ,
Rule : admissionregistrationv1 . Rule {
APIGroups : [ ] string { "" } ,
APIVersions : [ ] string { "v1" } ,
Resources : [ ] string { "configmaps" } ,
Scope : ptr . To ( admissionregistrationv1 . NamespacedScope ) ,
} ,
} , {
2024-04-30 19:05:44 +02:00
Operations : [ ] admissionregistrationv1 . OperationType { webhookCreate , webhookUpdate } ,
Rule : admissionregistrationv1 . Rule {
APIGroups : [ ] string { "" } ,
APIVersions : [ ] string { "v1" } ,
2024-09-05 17:12:40 +05:30
Resources : [ ] string { "pods" , "pods/ephemeralcontainers" } ,
2024-04-30 19:05:44 +02:00
Scope : ptr . To ( admissionregistrationv1 . NamespacedScope ) ,
} ,
} ,
} ,
} ,
}
for _ , testCase := range testCases {
t . Run ( testCase . name , func ( t * testing . T ) {
wh := & webhook {
rules : testCase . rules ,
}
result := wh . buildRulesWithOperations ( testCase . mapResourceToOpnType , [ ] admissionregistrationv1 . OperationType { webhookCreate , webhookUpdate } )
if ! reflect . DeepEqual ( result , testCase . expectedResult ) {
t . Errorf ( "Expected %v, but got %v" , testCase . expectedResult , result )
}
} )
}
}