Skip to content

Commit baeeb66

Browse files
committed
Update tests
1 parent 4c64aa7 commit baeeb66

File tree

2 files changed

+298
-7
lines changed

2 files changed

+298
-7
lines changed

test/integration/apiserver/admissionwebhook/match_conditions_test.go

Lines changed: 296 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"crypto/x509"
2323
"encoding/json"
2424
"io"
25+
"k8s.io/apimachinery/pkg/util/version"
2526
"net/http"
2627
"net/http/httptest"
2728
"strconv"
@@ -111,7 +112,6 @@ func newMatchConditionHandler(recorder *admissionRecorder) http.Handler {
111112

112113
// TestMatchConditions tests ValidatingWebhookConfigurations and MutatingWebhookConfigurations that validates different cases of matchCondition fields
113114
func TestMatchConditions(t *testing.T) {
114-
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.StrictCostEnforcementForWebhooks, false)
115115
fail := admissionregistrationv1.Fail
116116
ignore := admissionregistrationv1.Ignore
117117

@@ -289,6 +289,296 @@ func TestMatchConditions(t *testing.T) {
289289
matchConditionsTestPod("test2", "default"),
290290
},
291291
},
292+
}
293+
294+
roots := x509.NewCertPool()
295+
if !roots.AppendCertsFromPEM(localhostCert) {
296+
t.Fatal("Failed to append Cert from PEM")
297+
}
298+
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
299+
if err != nil {
300+
t.Fatalf("Failed to build cert with error: %+v", err)
301+
}
302+
303+
recorder := &admissionRecorder{requests: []*admissionv1.AdmissionRequest{}}
304+
305+
webhookServer := httptest.NewUnstartedServer(newMatchConditionHandler(recorder))
306+
webhookServer.TLS = &tls.Config{
307+
RootCAs: roots,
308+
Certificates: []tls.Certificate{cert},
309+
}
310+
webhookServer.StartTLS()
311+
defer webhookServer.Close()
312+
313+
dryRunCreate := metav1.CreateOptions{
314+
DryRun: []string{metav1.DryRunAll},
315+
}
316+
317+
for _, testcase := range testcases {
318+
t.Run(testcase.name, func(t *testing.T) {
319+
upCh := recorder.Reset()
320+
321+
server, err := apiservertesting.StartTestServer(t, nil, []string{
322+
"--disable-admission-plugins=ServiceAccount",
323+
}, framework.SharedEtcd())
324+
if err != nil {
325+
t.Fatal(err)
326+
}
327+
defer server.TearDownFn()
328+
329+
config := server.ClientConfig
330+
331+
client, err := clientset.NewForConfig(config)
332+
if err != nil {
333+
t.Fatal(err)
334+
}
335+
336+
// Write markers to a separate namespace to avoid cross-talk
337+
markerNs := "marker"
338+
_, err = client.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: markerNs}}, metav1.CreateOptions{})
339+
if err != nil {
340+
t.Fatal(err)
341+
}
342+
343+
// Create a marker object to use to check for the webhook configurations to be ready.
344+
marker, err := client.CoreV1().Pods(markerNs).Create(context.TODO(), newMarkerPod(markerNs), metav1.CreateOptions{})
345+
if err != nil {
346+
t.Fatal(err)
347+
}
348+
349+
endpoint := webhookServer.URL
350+
markerEndpoint := webhookServer.URL + "/marker"
351+
validatingwebhook := &admissionregistrationv1.ValidatingWebhookConfiguration{
352+
ObjectMeta: metav1.ObjectMeta{
353+
Name: "admission.integration.test",
354+
},
355+
Webhooks: []admissionregistrationv1.ValidatingWebhook{
356+
{
357+
Name: "admission.integration.test",
358+
Rules: []admissionregistrationv1.RuleWithOperations{{
359+
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
360+
Rule: admissionregistrationv1.Rule{
361+
APIGroups: []string{""},
362+
APIVersions: []string{"v1"},
363+
Resources: []string{"pods"},
364+
},
365+
}},
366+
ClientConfig: admissionregistrationv1.WebhookClientConfig{
367+
URL: &endpoint,
368+
CABundle: localhostCert,
369+
},
370+
// ignore pods in the marker namespace
371+
NamespaceSelector: &metav1.LabelSelector{
372+
MatchExpressions: []metav1.LabelSelectorRequirement{
373+
{
374+
Key: corev1.LabelMetadataName,
375+
Operator: metav1.LabelSelectorOpNotIn,
376+
Values: []string{"marker"},
377+
},
378+
}},
379+
FailurePolicy: testcase.failPolicy,
380+
SideEffects: &noSideEffects,
381+
AdmissionReviewVersions: []string{"v1"},
382+
MatchConditions: testcase.matchConditions,
383+
},
384+
{
385+
Name: "admission.integration.test.marker",
386+
Rules: []admissionregistrationv1.RuleWithOperations{{
387+
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll},
388+
Rule: admissionregistrationv1.Rule{APIGroups: []string{""}, APIVersions: []string{"v1"}, Resources: []string{"pods"}},
389+
}},
390+
ClientConfig: admissionregistrationv1.WebhookClientConfig{
391+
URL: &markerEndpoint,
392+
CABundle: localhostCert,
393+
},
394+
NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{
395+
corev1.LabelMetadataName: "marker",
396+
}},
397+
ObjectSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"marker": "true"}},
398+
FailurePolicy: testcase.failPolicy,
399+
SideEffects: &noSideEffects,
400+
AdmissionReviewVersions: []string{"v1"},
401+
},
402+
},
403+
}
404+
405+
validatingcfg, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.TODO(), validatingwebhook, metav1.CreateOptions{})
406+
if err != nil {
407+
t.Fatal(err)
408+
}
409+
410+
vhwHasBeenCleanedUp := false
411+
defer func() {
412+
if !vhwHasBeenCleanedUp {
413+
err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.TODO(), validatingcfg.GetName(), metav1.DeleteOptions{})
414+
if err != nil {
415+
t.Fatal(err)
416+
}
417+
}
418+
}()
419+
420+
// wait until new webhook is called the first time
421+
if err := wait.PollUntilContextTimeout(context.Background(), time.Millisecond*5, wait.ForeverTestTimeout, true, func(_ context.Context) (bool, error) {
422+
_, err = client.CoreV1().Pods(markerNs).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
423+
select {
424+
case <-upCh:
425+
return true, nil
426+
default:
427+
t.Logf("Waiting for webhook to become effective, getting marker object: %v", err)
428+
return false, nil
429+
}
430+
}); err != nil {
431+
t.Fatal(err)
432+
}
433+
434+
for _, pod := range testcase.pods {
435+
_, err := client.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, dryRunCreate)
436+
if !testcase.expectErrorPod && err != nil {
437+
t.Fatalf("unexpected error creating test pod: %v", err)
438+
} else if testcase.expectErrorPod && err == nil {
439+
t.Fatal("expected error creating pods")
440+
} else if testcase.expectErrorPod && err != nil && !strings.Contains(err.Error(), testcase.errMessage) {
441+
t.Fatalf("expected error message includes: %v, but get %v", testcase.errMessage, err)
442+
}
443+
}
444+
445+
if len(recorder.requests) != len(testcase.matchedPods) {
446+
t.Errorf("unexpected requests %v, expected %v", recorder.requests, testcase.matchedPods)
447+
}
448+
449+
for i, request := range recorder.requests {
450+
if request.Name != testcase.matchedPods[i].Name {
451+
t.Errorf("unexpected pod name %v, expected %v", request.Name, testcase.matchedPods[i].Name)
452+
}
453+
if request.Namespace != testcase.matchedPods[i].Namespace {
454+
t.Errorf("unexpected pod namespace %v, expected %v", request.Namespace, testcase.matchedPods[i].Namespace)
455+
}
456+
}
457+
458+
// Reset and rerun against mutating webhook configuration
459+
// TODO: private helper function for validation after creating vwh or mwh
460+
upCh = recorder.Reset()
461+
err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.TODO(), validatingcfg.GetName(), metav1.DeleteOptions{})
462+
if err != nil {
463+
t.Fatal(err)
464+
} else {
465+
vhwHasBeenCleanedUp = true
466+
}
467+
468+
mutatingwebhook := &admissionregistrationv1.MutatingWebhookConfiguration{
469+
ObjectMeta: metav1.ObjectMeta{
470+
Name: "admission.integration.test",
471+
},
472+
Webhooks: []admissionregistrationv1.MutatingWebhook{
473+
{
474+
Name: "admission.integration.test",
475+
Rules: []admissionregistrationv1.RuleWithOperations{{
476+
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
477+
Rule: admissionregistrationv1.Rule{
478+
APIGroups: []string{""},
479+
APIVersions: []string{"v1"},
480+
Resources: []string{"pods"},
481+
},
482+
}},
483+
ClientConfig: admissionregistrationv1.WebhookClientConfig{
484+
URL: &endpoint,
485+
CABundle: localhostCert,
486+
},
487+
// ignore pods in the marker namespace
488+
NamespaceSelector: &metav1.LabelSelector{
489+
MatchExpressions: []metav1.LabelSelectorRequirement{
490+
{
491+
Key: corev1.LabelMetadataName,
492+
Operator: metav1.LabelSelectorOpNotIn,
493+
Values: []string{"marker"},
494+
},
495+
}},
496+
FailurePolicy: testcase.failPolicy,
497+
SideEffects: &noSideEffects,
498+
AdmissionReviewVersions: []string{"v1"},
499+
MatchConditions: testcase.matchConditions,
500+
},
501+
{
502+
Name: "admission.integration.test.marker",
503+
Rules: []admissionregistrationv1.RuleWithOperations{{
504+
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll},
505+
Rule: admissionregistrationv1.Rule{APIGroups: []string{""}, APIVersions: []string{"v1"}, Resources: []string{"pods"}},
506+
}},
507+
ClientConfig: admissionregistrationv1.WebhookClientConfig{
508+
URL: &markerEndpoint,
509+
CABundle: localhostCert,
510+
},
511+
NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{
512+
corev1.LabelMetadataName: "marker",
513+
}},
514+
ObjectSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"marker": "true"}},
515+
FailurePolicy: testcase.failPolicy,
516+
SideEffects: &noSideEffects,
517+
AdmissionReviewVersions: []string{"v1"},
518+
},
519+
},
520+
}
521+
522+
mutatingcfg, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.TODO(), mutatingwebhook, metav1.CreateOptions{})
523+
if err != nil {
524+
t.Fatal(err)
525+
}
526+
defer func() {
527+
err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.TODO(), mutatingcfg.GetName(), metav1.DeleteOptions{})
528+
if err != nil {
529+
t.Fatal(err)
530+
}
531+
}()
532+
533+
// wait until new webhook is called the first time
534+
if err := wait.PollUntilContextTimeout(context.Background(), time.Millisecond*5, wait.ForeverTestTimeout, true, func(_ context.Context) (bool, error) {
535+
_, err = client.CoreV1().Pods(markerNs).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
536+
select {
537+
case <-upCh:
538+
return true, nil
539+
default:
540+
t.Logf("Waiting for webhook to become effective, getting marker object: %v", err)
541+
return false, nil
542+
}
543+
}); err != nil {
544+
t.Fatal(err)
545+
}
546+
547+
for _, pod := range testcase.pods {
548+
_, err = client.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, dryRunCreate)
549+
if testcase.expectErrorPod == false && err != nil {
550+
t.Fatalf("unexpected error creating test pod: %v", err)
551+
} else if testcase.expectErrorPod == true && err == nil {
552+
t.Fatal("expected error creating pods")
553+
}
554+
}
555+
556+
if len(recorder.requests) != len(testcase.matchedPods) {
557+
t.Errorf("unexpected requests %v, expected %v", recorder.requests, testcase.matchedPods)
558+
}
559+
560+
for i, request := range recorder.requests {
561+
if request.Name != testcase.matchedPods[i].Name {
562+
t.Errorf("unexpected pod name %v, expected %v", request.Name, testcase.matchedPods[i].Name)
563+
}
564+
if request.Namespace != testcase.matchedPods[i].Namespace {
565+
t.Errorf("unexpected pod namespace %v, expected %v", request.Namespace, testcase.matchedPods[i].Namespace)
566+
}
567+
}
568+
})
569+
}
570+
}
571+
572+
func TestMatchConditionsWithoutStrictCostEnforcement(t *testing.T) {
573+
testcases := []struct {
574+
name string
575+
matchConditions []admissionregistrationv1.MatchCondition
576+
pods []*corev1.Pod
577+
matchedPods []*corev1.Pod
578+
expectErrorPod bool
579+
failPolicy *admissionregistrationv1.FailurePolicyType
580+
errMessage string
581+
}{
292582
{
293583
name: "without strict cost enforcement: Authz check does not exceed per call limit",
294584
matchConditions: []admissionregistrationv1.MatchCondition{
@@ -303,7 +593,6 @@ func TestMatchConditions(t *testing.T) {
303593
matchedPods: []*corev1.Pod{
304594
matchConditionsTestPod("test1", "kube-system"),
305595
},
306-
failPolicy: &fail,
307596
expectErrorPod: false,
308597
},
309598
{
@@ -315,7 +604,6 @@ func TestMatchConditions(t *testing.T) {
315604
matchedPods: []*corev1.Pod{
316605
matchConditionsTestPod("test1", "kube-system"),
317606
},
318-
failPolicy: &fail,
319607
expectErrorPod: false,
320608
},
321609
}
@@ -346,8 +634,9 @@ func TestMatchConditions(t *testing.T) {
346634
for _, testcase := range testcases {
347635
t.Run(testcase.name, func(t *testing.T) {
348636
upCh := recorder.Reset()
349-
350-
server, err := apiservertesting.StartTestServer(t, nil, []string{
637+
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31"))
638+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.StrictCostEnforcementForWebhooks, false)
639+
server, err := apiservertesting.StartTestServer(t, &apiservertesting.TestServerInstanceOptions{EmulationVersion: "1.31"}, []string{
351640
"--disable-admission-plugins=ServiceAccount",
352641
}, framework.SharedEtcd())
353642
if err != nil {
@@ -447,7 +736,7 @@ func TestMatchConditions(t *testing.T) {
447736
}()
448737

449738
// wait until new webhook is called the first time
450-
if err := wait.PollImmediate(time.Millisecond*5, wait.ForeverTestTimeout, func() (bool, error) {
739+
if err := wait.PollUntilContextTimeout(context.Background(), time.Millisecond*5, wait.ForeverTestTimeout, true, func(_ context.Context) (bool, error) {
451740
_, err = client.CoreV1().Pods(markerNs).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
452741
select {
453742
case <-upCh:
@@ -876,7 +1165,7 @@ func TestMatchConditionsWithStrictCostEnforcement(t *testing.T) {
8761165
}()
8771166

8781167
// wait until new webhook is called the first time
879-
if err := wait.PollImmediate(time.Millisecond*5, wait.ForeverTestTimeout, func() (bool, error) {
1168+
if err := wait.PollUntilContextTimeout(context.Background(), time.Millisecond*5, wait.ForeverTestTimeout, true, func(_ context.Context) (bool, error) {
8801169
_, err = client.CoreV1().Pods(markerNs).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
8811170
select {
8821171
case <-upCh:

test/integration/apiserver/cel/validatingadmissionpolicy_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"encoding/json"
2222
"fmt"
23+
"k8s.io/apimachinery/pkg/util/version"
2324
"net/http"
2425
"net/http/httptest"
2526
"os"
@@ -2234,6 +2235,7 @@ func Test_CostLimitForValidation(t *testing.T) {
22342235
func Test_CostLimitForValidationWithFeatureDisabled(t *testing.T) {
22352236
resetPolicyRefreshInterval := generic.SetPolicyRefreshIntervalForTests(policyRefreshInterval)
22362237
defer resetPolicyRefreshInterval()
2238+
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31"))
22372239
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.StrictCostEnforcementForVAP, false)
22382240
server, err := apiservertesting.StartTestServer(t, &apiservertesting.TestServerInstanceOptions{EmulationVersion: "1.31"}, []string{
22392241
"--enable-admission-plugins", "ValidatingAdmissionPolicy",

0 commit comments

Comments
 (0)