@@ -20,6 +20,7 @@ import (
2020 "context"
2121 "fmt"
2222 "sync"
23+ "sync/atomic"
2324 "testing"
2425 "time"
2526
@@ -617,6 +618,12 @@ func TestBasicPolicyDefinitionFailure(t *testing.T) {
617618 require .ErrorContains (t , err , `Denied` )
618619}
619620
621+ type validatorFunc func (a admission.Attributes , o admission.ObjectInterfaces , params runtime.Object , matchKind schema.GroupVersionKind ) ([]policyDecision , error )
622+
623+ func (f validatorFunc ) Validate (a admission.Attributes , o admission.ObjectInterfaces , params runtime.Object , matchKind schema.GroupVersionKind ) ([]policyDecision , error ) {
624+ return f (a , o , params , matchKind )
625+ }
626+
620627type testValidator struct {
621628}
622629
@@ -1065,3 +1072,130 @@ func TestEmptyParamSource(t *testing.T) {
10651072 require .ErrorContains (t , err , `Denied` )
10661073 require .Equal (t , 1 , numCompiles )
10671074}
1075+
1076+ // Shows what happens when multiple policies share one param type, then
1077+ // one policy stops using the param. The expectation is the second policy
1078+ // keeps behaving normally
1079+ func TestMultiplePoliciesSharedParamType (t * testing.T ) {
1080+ testContext , testContextCancel := context .WithCancel (context .Background ())
1081+ defer testContextCancel ()
1082+
1083+ compiler := & fakeCompiler {
1084+ // Match everything by default
1085+ DefaultMatch : true ,
1086+ }
1087+ handler , paramTracker , tracker , controller := setupFakeTest (t , compiler )
1088+
1089+ // Use ConfigMap native-typed param
1090+ policy1 := * denyPolicy
1091+ policy1 .Name = "denypolicy1.example.com"
1092+
1093+ policy2 := * denyPolicy
1094+ policy2 .Name = "denypolicy2.example.com"
1095+
1096+ binding1 := * denyBinding
1097+ binding2 := * denyBinding
1098+
1099+ binding1 .Name = "denybinding1.example.com"
1100+ binding1 .Spec .PolicyName = policy1 .Name
1101+ binding2 .Name = "denybinding2.example.com"
1102+ binding2 .Spec .PolicyName = policy2 .Name
1103+
1104+ compiles1 := atomic.Int64 {}
1105+ evaluations1 := atomic.Int64 {}
1106+
1107+ compiles2 := atomic.Int64 {}
1108+ evaluations2 := atomic.Int64 {}
1109+
1110+ compiler .RegisterDefinition (& policy1 , func (vap * v1alpha1.ValidatingAdmissionPolicy ) Validator {
1111+ compiles1 .Add (1 )
1112+
1113+ return validatorFunc (func (a admission.Attributes , o admission.ObjectInterfaces , params runtime.Object , matchKind schema.GroupVersionKind ) ([]policyDecision , error ) {
1114+ evaluations1 .Add (1 )
1115+ return []policyDecision {
1116+ {
1117+ action : actionAdmit ,
1118+ },
1119+ }, nil
1120+ })
1121+ }, nil )
1122+
1123+ compiler .RegisterDefinition (& policy2 , func (vap * v1alpha1.ValidatingAdmissionPolicy ) Validator {
1124+ compiles2 .Add (1 )
1125+
1126+ return validatorFunc (func (a admission.Attributes , o admission.ObjectInterfaces , params runtime.Object , matchKind schema.GroupVersionKind ) ([]policyDecision , error ) {
1127+ evaluations2 .Add (1 )
1128+ return []policyDecision {
1129+ {
1130+ action : actionDeny ,
1131+ message : "Policy2Denied" ,
1132+ },
1133+ }, nil
1134+ })
1135+ }, nil )
1136+
1137+ require .NoError (t , tracker .Create (definitionsGVR , & policy1 , policy1 .Namespace ))
1138+ require .NoError (t , tracker .Create (bindingsGVR , & binding1 , binding1 .Namespace ))
1139+ require .NoError (t , paramTracker .Add (fakeParams ))
1140+
1141+ // Wait for controller to reconcile given objects
1142+ require .NoError (t ,
1143+ waitForReconcile (
1144+ testContext , controller ,
1145+ & binding1 , & policy1 , fakeParams ))
1146+
1147+ // Make sure policy 1 is created and bound to the params type first
1148+ require .NoError (t , tracker .Create (definitionsGVR , & policy2 , policy2 .Namespace ))
1149+ require .NoError (t , tracker .Create (bindingsGVR , & binding2 , binding2 .Namespace ))
1150+
1151+ // Wait for controller to reconcile given objects
1152+ require .NoError (t ,
1153+ waitForReconcile (
1154+ testContext , controller ,
1155+ & binding1 , & binding2 , & policy1 , & policy2 , fakeParams ))
1156+
1157+ err := handler .Validate (
1158+ testContext ,
1159+ // Object is irrelevant/unchecked for this test. Just test that
1160+ // the evaluator is executed, and returns admit meaning the params
1161+ // passed was a configmap
1162+ attributeRecord (nil , fakeParams , admission .Create ),
1163+ & admission.RuntimeObjectInterfaces {},
1164+ )
1165+
1166+ require .ErrorContains (t , err , `Denied` )
1167+ require .EqualValues (t , 1 , compiles1 .Load ())
1168+ require .EqualValues (t , 1 , evaluations1 .Load ())
1169+ require .EqualValues (t , 1 , compiles2 .Load ())
1170+ require .EqualValues (t , 1 , evaluations2 .Load ())
1171+
1172+ // Remove param type from policy1
1173+ // Show that policy2 evaluator is still being passed the configmaps
1174+ policy1 .Spec .ParamKind = nil
1175+ policy1 .ResourceVersion = "2"
1176+
1177+ binding1 .Spec .ParamRef = nil
1178+ binding1 .ResourceVersion = "2"
1179+
1180+ require .NoError (t , tracker .Update (definitionsGVR , & policy1 , policy1 .Namespace ))
1181+ require .NoError (t , tracker .Update (bindingsGVR , & binding1 , binding1 .Namespace ))
1182+ require .NoError (t ,
1183+ waitForReconcile (
1184+ testContext , controller ,
1185+ & binding1 , & policy1 ))
1186+
1187+ err = handler .Validate (
1188+ testContext ,
1189+ // Object is irrelevant/unchecked for this test. Just test that
1190+ // the evaluator is executed, and returns admit meaning the params
1191+ // passed was a configmap
1192+ attributeRecord (nil , fakeParams , admission .Create ),
1193+ & admission.RuntimeObjectInterfaces {},
1194+ )
1195+
1196+ require .ErrorContains (t , err , `Policy2Denied` )
1197+ require .EqualValues (t , 2 , compiles1 .Load ())
1198+ require .EqualValues (t , 2 , evaluations1 .Load ())
1199+ require .EqualValues (t , 1 , compiles2 .Load ())
1200+ require .EqualValues (t , 2 , evaluations2 .Load ())
1201+ }
0 commit comments