k8s.io/kubernetes@v1.29.3/test/integration/apiserver/admissionwebhook/broken_webhook_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 // There is no guarantee on how long it takes the apiserver to honor the configuration and there is 69 // no API to determine if the configuration is being honored, so we will just wait 10s, which is long enough 70 // in most cases. 71 time.Sleep(10 * time.Second) 72 73 // test whether the webhook blocks requests 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 // test whether the webhook still blocks requests after restarting 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 // The webhook deletion is honored in 10s. 105 time.Sleep(10 * time.Second) 106 107 // test if the deleted webhook no longer blocks requests 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 // This client config references a non existent service 171 // so it should always fail. 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 }