1
16
17 package admissionwebhook
18
19 import (
20 "context"
21 "fmt"
22 "testing"
23 "time"
24
25 admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
26 appsv1 "k8s.io/api/apps/v1"
27 corev1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/client-go/kubernetes"
30 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
31 "k8s.io/kubernetes/test/integration/framework"
32 )
33
34 var (
35 brokenWebhookName = "integration-broken-webhook-test-webhook-config"
36 deploymentNamePrefix = "integration-broken-webhook-test-deployment"
37 )
38
39 func TestBrokenWebhook(t *testing.T) {
40 var tearDownFn kubeapiservertesting.TearDownFunc
41 defer func() {
42 if tearDownFn != nil {
43 tearDownFn()
44 }
45 }()
46
47 etcdConfig := framework.SharedEtcd()
48 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, etcdConfig)
49 tearDownFn = server.TearDownFn
50
51 client, err := kubernetes.NewForConfig(server.ClientConfig)
52 if err != nil {
53 t.Fatalf("unexpected error: %v", err)
54 }
55
56 t.Logf("Creating Deployment to ensure apiserver is functional")
57 _, err = client.AppsV1().Deployments("default").Create(context.TODO(), exampleDeployment(generateDeploymentName(0)), metav1.CreateOptions{})
58 if err != nil {
59 t.Fatalf("Failed to create deployment: %v", err)
60 }
61
62 t.Logf("Creating Broken Webhook that will block all operations on all objects")
63 _, err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.TODO(), brokenWebhookConfig(brokenWebhookName), metav1.CreateOptions{})
64 if err != nil {
65 t.Fatalf("Failed to register broken webhook: %v", err)
66 }
67
68
69
70
71 time.Sleep(10 * time.Second)
72
73
74 t.Logf("Attempt to create Deployment which should fail due to the webhook")
75 _, err = client.AppsV1().Deployments("default").Create(context.TODO(), exampleDeployment(generateDeploymentName(1)), metav1.CreateOptions{})
76 if err == nil {
77 t.Fatalf("Expected the broken webhook to cause creating a deployment to fail, but it succeeded.")
78 }
79
80 t.Logf("Restarting apiserver")
81 tearDownFn = nil
82 server.TearDownFn()
83 server = kubeapiservertesting.StartTestServerOrDie(t, nil, nil, etcdConfig)
84 tearDownFn = server.TearDownFn
85
86 client, err = kubernetes.NewForConfig(server.ClientConfig)
87 if err != nil {
88 t.Fatalf("unexpected error: %v", err)
89 }
90
91
92 t.Logf("Attempt again to create Deployment which should fail due to the webhook")
93 _, err = client.AppsV1().Deployments("default").Create(context.TODO(), exampleDeployment(generateDeploymentName(2)), metav1.CreateOptions{})
94 if err == nil {
95 t.Fatalf("Expected the broken webhook to cause creating a deployment to fail, but it succeeded.")
96 }
97
98 t.Logf("Deleting the broken webhook to fix the cluster")
99 err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.TODO(), brokenWebhookName, metav1.DeleteOptions{})
100 if err != nil {
101 t.Fatalf("Failed to delete broken webhook: %v", err)
102 }
103
104
105 time.Sleep(10 * time.Second)
106
107
108 t.Logf("Creating Deployment to ensure webhook is deleted")
109 _, err = client.AppsV1().Deployments("default").Create(context.TODO(), exampleDeployment(generateDeploymentName(3)), metav1.CreateOptions{})
110 if err != nil {
111 t.Fatalf("Failed to create deployment: %v", err)
112 }
113 }
114
115 func generateDeploymentName(suffix int) string {
116 return fmt.Sprintf("%v-%v", deploymentNamePrefix, suffix)
117 }
118
119 func exampleDeployment(name string) *appsv1.Deployment {
120 var replicas int32 = 1
121 return &appsv1.Deployment{
122 TypeMeta: metav1.TypeMeta{
123 Kind: "Deployment",
124 APIVersion: "apps/v1",
125 },
126 ObjectMeta: metav1.ObjectMeta{
127 Namespace: "default",
128 Name: name,
129 },
130 Spec: appsv1.DeploymentSpec{
131 Replicas: &replicas,
132 Selector: &metav1.LabelSelector{
133 MatchLabels: map[string]string{"foo": "bar"},
134 },
135 Template: corev1.PodTemplateSpec{
136 ObjectMeta: metav1.ObjectMeta{
137 Labels: map[string]string{"foo": "bar"},
138 },
139 Spec: corev1.PodSpec{
140 Containers: []corev1.Container{
141 {
142 Name: "foo",
143 Image: "foo",
144 },
145 },
146 },
147 },
148 },
149 }
150 }
151
152 func brokenWebhookConfig(name string) *admissionregistrationv1.ValidatingWebhookConfiguration {
153 var path string
154 failurePolicy := admissionregistrationv1.Fail
155 return &admissionregistrationv1.ValidatingWebhookConfiguration{
156 ObjectMeta: metav1.ObjectMeta{
157 Name: name,
158 },
159 Webhooks: []admissionregistrationv1.ValidatingWebhook{
160 {
161 Name: "broken-webhook.k8s.io",
162 Rules: []admissionregistrationv1.RuleWithOperations{{
163 Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll},
164 Rule: admissionregistrationv1.Rule{
165 APIGroups: []string{"*"},
166 APIVersions: []string{"*"},
167 Resources: []string{"*/*"},
168 },
169 }},
170
171
172 ClientConfig: admissionregistrationv1.WebhookClientConfig{
173 Service: &admissionregistrationv1.ServiceReference{
174 Namespace: "default",
175 Name: "invalid-webhook-service",
176 Path: &path,
177 },
178 CABundle: nil,
179 },
180 FailurePolicy: &failurePolicy,
181 SideEffects: &noSideEffects,
182 AdmissionReviewVersions: []string{"v1"},
183 },
184 },
185 }
186 }
187
View as plain text