implmementing type checking

with multi-type support.
This commit is contained in:
Jiahui Feng
2023-03-07 15:49:19 -08:00
committed by Indeed
parent 54283a1d38
commit feb18b3f5f
11 changed files with 1059 additions and 8 deletions

View File

@@ -2611,6 +2611,26 @@ func withPolicyExistsLabels(labels []string, policy *admissionregistrationv1alph
return policy
}
func withGVRMatch(groups []string, versions []string, resources []string, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
policy.Spec.MatchConstraints = &admissionregistrationv1alpha1.MatchResources{
ResourceRules: []admissionregistrationv1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistrationv1alpha1.RuleWithOperations{
Operations: []admissionregistrationv1.OperationType{
"*",
},
Rule: admissionregistrationv1.Rule{
APIGroups: groups,
APIVersions: versions,
Resources: resources,
},
},
},
},
}
return policy
}
func withValidations(validations []admissionregistrationv1alpha1.Validation, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
policy.Spec.Validations = validations
return policy
@@ -2885,3 +2905,114 @@ rules:
resources: ["configmaps"]
`
)
func TestValidatingAdmissionPolicyTypeChecking(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
server, err := apiservertesting.StartTestServer(t, nil, []string{
"--enable-admission-plugins", "ValidatingAdmissionPolicy",
}, framework.SharedEtcd())
if err != nil {
t.Fatal(err)
}
defer server.TearDownFn()
config := server.ClientConfig
client, err := clientset.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
name string
policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy
assertFieldRef func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) // warning.fieldRef
assertWarnings func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) // warning.warning
}{
{
name: "deployment with correct expression",
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1alpha1.Validation{
{
Expression: "object.spec.replicas > 1",
},
}, makePolicy("replicated-deployment"))),
assertFieldRef: toHasLengthOf(0),
assertWarnings: toHasLengthOf(0),
},
{
name: "deployment with type confusion",
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1alpha1.Validation{
{
Expression: "object.spec.replicas < 100", // this one passes
},
{
Expression: "object.spec.replicas > '1'", // '1' should be int
},
}, makePolicy("confused-deployment"))),
assertFieldRef: toBe("spec.validations[1].expression"),
assertWarnings: toHasSubstring(`found no matching overload for '_>_' applied to '(int, string)'`),
},
} {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
policy, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(ctx, tc.policy, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
defer client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Delete(context.Background(), policy.Name, metav1.DeleteOptions{})
err = wait.PollImmediateWithContext(ctx, time.Second, time.Minute, func(ctx context.Context) (done bool, err error) {
name := policy.Name
// wait until the typeChecking is set, which means the type checking
// is complete.
updated, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Get(ctx, name, metav1.GetOptions{})
if err != nil {
return false, err
}
if updated.Status.TypeChecking != nil {
policy = updated
return true, nil
}
return false, nil
})
if err != nil {
t.Fatal(err)
}
tc.assertFieldRef(policy.Status.TypeChecking.ExpressionWarnings, t)
tc.assertWarnings(policy.Status.TypeChecking.ExpressionWarnings, t)
})
}
}
func toBe(expected ...string) func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
if len(expected) != len(warnings) {
t.Fatalf("mismatched length, expect %d, got %d", len(expected), len(warnings))
}
for i := range expected {
if expected[i] != warnings[i].FieldRef {
t.Errorf("expected %q but got %q", expected[i], warnings[i].FieldRef)
}
}
}
}
func toHasSubstring(substrings ...string) func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
if len(substrings) != len(warnings) {
t.Fatalf("mismatched length, expect %d, got %d", len(substrings), len(warnings))
}
for i := range substrings {
if !strings.Contains(warnings[i].Warning, substrings[i]) {
t.Errorf("missing expected substring %q in %v", substrings[i], warnings[i])
}
}
}
}
func toHasLengthOf(n int) func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
if n != len(warnings) {
t.Fatalf("mismatched length, expect %d, got %d", n, len(warnings))
}
}
}