Skip to content

Commit 9d9683c

Browse files
authored
Merge pull request #7185 from adrianmoisey/allow-failurepolicy
VPA: Add option to set the webhook failurePolicy to Fail
2 parents d67f22b + 205b978 commit 9d9683c

File tree

4 files changed

+76
-8
lines changed

4 files changed

+76
-8
lines changed

vertical-pod-autoscaler/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- [Using CPU management with static policy](#using-cpu-management-with-static-policy)
2525
- [Controlling eviction behavior based on scaling direction and resource](#controlling-eviction-behavior-based-on-scaling-direction-and-resource)
2626
- [Limiting which namespaces are used](#limiting-which-namespaces-are-used)
27+
- [Setting the webhook failurePolicy](#setting-the-webhook-failurepolicy)
2728
- [Known limitations](#known-limitations)
2829
- [Related links](#related-links)
2930

@@ -388,6 +389,11 @@ vpa-post-processor.kubernetes.io/{containerName}_integerCPU=true
388389

389390
These options cannot be used together and are mutually exclusive.
390391

392+
### Setting the webhook failurePolicy
393+
394+
It is possible to set the failurePolicy of the webhook to `Fail` by passing `--webhook-failure-policy-fail=true` to the VPA admission controller.
395+
Please use this option with caution as it may be possible to break Pod creation if there is a failure with the VPA.
396+
Using it in conjunction with `--ignored-vpa-object-namespaces=kube-system` or `--vpa-object-namespace` to reduce risk.
391397

392398
# Known limitations
393399

vertical-pod-autoscaler/pkg/admission-controller/config.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func configTLS(cfg certsConfig, minTlsVersion, ciphers string, stop <-chan struc
9090

9191
// register this webhook admission controller with the kube-apiserver
9292
// by creating MutatingWebhookConfiguration.
93-
func selfRegistration(clientset kubernetes.Interface, caCert []byte, webHookDelay time.Duration, namespace, serviceName, url string, registerByURL bool, timeoutSeconds int32, selectedNamespace string, ignoredNamespaces []string) {
93+
func selfRegistration(clientset kubernetes.Interface, caCert []byte, webHookDelay time.Duration, namespace, serviceName, url string, registerByURL bool, timeoutSeconds int32, selectedNamespace string, ignoredNamespaces []string, webHookFailurePolicy bool) {
9494
time.Sleep(webHookDelay)
9595
client := clientset.AdmissionregistrationV1().MutatingWebhookConfigurations()
9696
_, err := client.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
@@ -109,7 +109,14 @@ func selfRegistration(clientset kubernetes.Interface, caCert []byte, webHookDela
109109
RegisterClientConfig.URL = &url
110110
}
111111
sideEffects := admissionregistration.SideEffectClassNone
112-
failurePolicy := admissionregistration.Ignore
112+
113+
var failurePolicy admissionregistration.FailurePolicyType
114+
if webHookFailurePolicy {
115+
failurePolicy = admissionregistration.Fail
116+
} else {
117+
failurePolicy = admissionregistration.Ignore
118+
}
119+
113120
RegisterClientConfig.CABundle = caCert
114121

115122
var namespaceSelector metav1.LabelSelector

vertical-pod-autoscaler/pkg/admission-controller/config_test.go

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func TestSelfRegistrationBase(t *testing.T) {
4040
selectedNamespace := ""
4141
ignoredNamespaces := []string{}
4242

43-
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces)
43+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, false)
4444

4545
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
4646
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
@@ -83,7 +83,7 @@ func TestSelfRegistrationWithURL(t *testing.T) {
8383
selectedNamespace := ""
8484
ignoredNamespaces := []string{}
8585

86-
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces)
86+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, false)
8787

8888
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
8989
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
@@ -111,7 +111,7 @@ func TestSelfRegistrationWithOutURL(t *testing.T) {
111111
selectedNamespace := ""
112112
ignoredNamespaces := []string{}
113113

114-
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces)
114+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, false)
115115

116116
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
117117
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
@@ -141,7 +141,7 @@ func TestSelfRegistrationWithIgnoredNamespaces(t *testing.T) {
141141
selectedNamespace := ""
142142
ignoredNamespaces := []string{"test"}
143143

144-
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces)
144+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, false)
145145

146146
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
147147
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
@@ -172,7 +172,7 @@ func TestSelfRegistrationWithSelectedNamespaces(t *testing.T) {
172172
selectedNamespace := "test"
173173
ignoredNamespaces := []string{}
174174

175-
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces)
175+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, false)
176176

177177
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
178178
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
@@ -190,3 +190,57 @@ func TestSelfRegistrationWithSelectedNamespaces(t *testing.T) {
190190
assert.Equal(t, matchExpression.Operator, metav1.LabelSelectorOpIn, "expected namespace operator to be OpIn")
191191
assert.Equal(t, matchExpression.Values, []string{selectedNamespace}, "expected namespace selector match expression to be equal")
192192
}
193+
194+
func TestSelfRegistrationWithFailurePolicy(t *testing.T) {
195+
196+
testClientSet := fake.NewSimpleClientset()
197+
caCert := []byte("fake")
198+
webHookDelay := 0 * time.Second
199+
namespace := "default"
200+
serviceName := "vpa-service"
201+
url := "http://example.com/"
202+
registerByURL := false
203+
timeoutSeconds := int32(32)
204+
selectedNamespace := "test"
205+
ignoredNamespaces := []string{}
206+
207+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, true)
208+
209+
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
210+
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
211+
212+
assert.NoError(t, err, "expected no error fetching webhook configuration")
213+
214+
assert.Len(t, webhookConfig.Webhooks, 1, "expected one webhook configuration")
215+
webhook := webhookConfig.Webhooks[0]
216+
217+
assert.NotNil(t, *webhook.FailurePolicy, "expected failurePolicy not to be nil")
218+
assert.Equal(t, *webhook.FailurePolicy, admissionregistration.Fail, "expected failurePolicy to be Fail")
219+
}
220+
221+
func TestSelfRegistrationWithOutFailurePolicy(t *testing.T) {
222+
223+
testClientSet := fake.NewSimpleClientset()
224+
caCert := []byte("fake")
225+
webHookDelay := 0 * time.Second
226+
namespace := "default"
227+
serviceName := "vpa-service"
228+
url := "http://example.com/"
229+
registerByURL := false
230+
timeoutSeconds := int32(32)
231+
selectedNamespace := "test"
232+
ignoredNamespaces := []string{}
233+
234+
selfRegistration(testClientSet, caCert, webHookDelay, namespace, serviceName, url, registerByURL, timeoutSeconds, selectedNamespace, ignoredNamespaces, false)
235+
236+
webhookConfigInterface := testClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations()
237+
webhookConfig, err := webhookConfigInterface.Get(context.TODO(), webhookConfigName, metav1.GetOptions{})
238+
239+
assert.NoError(t, err, "expected no error fetching webhook configuration")
240+
241+
assert.Len(t, webhookConfig.Webhooks, 1, "expected one webhook configuration")
242+
webhook := webhookConfig.Webhooks[0]
243+
244+
assert.NotNil(t, *webhook.FailurePolicy, "expected namespace selector not to be nil")
245+
assert.Equal(t, *webhook.FailurePolicy, admissionregistration.Ignore, "expected failurePolicy to be Ignore")
246+
}

vertical-pod-autoscaler/pkg/admission-controller/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ var (
7575
webhookAddress = flag.String("webhook-address", "", "Address under which webhook is registered. Used when registerByURL is set to true.")
7676
webhookPort = flag.String("webhook-port", "", "Server Port for Webhook")
7777
webhookTimeout = flag.Int("webhook-timeout-seconds", 30, "Timeout in seconds that the API server should wait for this webhook to respond before failing.")
78+
webHookFailurePolicy = flag.Bool("webhook-failure-policy-fail", false, "If set to true, will configure the admission webhook failurePolicy to \"Fail\". Use with caution.")
7879
registerWebhook = flag.Bool("register-webhook", true, "If set to true, admission webhook object will be created on start up to register with the API server.")
7980
registerByURL = flag.Bool("register-by-url", false, "If set to true, admission webhook will be registered by URL (webhookAddress:webhookPort) instead of by service name")
8081
vpaObjectNamespace = flag.String("vpa-object-namespace", apiv1.NamespaceAll, "Namespace to search for VPA objects. Empty means all namespaces will be used. Must not be used if ignored-vpa-object-namespaces is set.")
@@ -146,7 +147,7 @@ func main() {
146147
ignoredNamespaces := strings.Split(*ignoredVpaObjectNamespaces, ",")
147148
go func() {
148149
if *registerWebhook {
149-
selfRegistration(kubeClient, readFile(*certsConfiguration.clientCaFile), webHookDelay, namespace, *serviceName, url, *registerByURL, int32(*webhookTimeout), *vpaObjectNamespace, ignoredNamespaces)
150+
selfRegistration(kubeClient, readFile(*certsConfiguration.clientCaFile), webHookDelay, namespace, *serviceName, url, *registerByURL, int32(*webhookTimeout), *vpaObjectNamespace, ignoredNamespaces, *webHookFailurePolicy)
150151
}
151152
// Start status updates after the webhook is initialized.
152153
statusUpdater.Run(stopCh)

0 commit comments

Comments
 (0)