Skip to content

Commit e916e67

Browse files
Merge pull request #115185 from alexzielenski/automated-cherry-pick-of-#115147-upstream-release-1.26
Automated cherry pick of #115147: fix bug with param controllers being removed if used by more Kubernetes-commit: 96dacc59433163ed2f9fbd7a1246af0c06b04f5e
2 parents 72bda9b + 83adc63 commit e916e67

File tree

2 files changed

+140
-2
lines changed

2 files changed

+140
-2
lines changed

pkg/admission/plugin/validatingadmissionpolicy/admission_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
620627
type 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+
}

pkg/admission/plugin/validatingadmissionpolicy/controller_reconcile.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,12 @@ func (c *celAdmissionController) reconcilePolicyDefinition(namespace, name strin
119119
return info.configurationError
120120
}
121121

122-
// Start watching the param CRD
123-
if _, ok := c.paramsCRDControllers[*paramSource]; !ok {
122+
if info, ok := c.paramsCRDControllers[*paramSource]; ok {
123+
// If a param controller is already active for this paramsource, make
124+
// sure it is tracking this policy's dependency upon it
125+
info.dependentDefinitions.Insert(nn)
126+
127+
} else {
124128
instanceContext, instanceCancel := context.WithCancel(c.runningContext)
125129

126130
// Watch for new instances of this policy

0 commit comments

Comments
 (0)