From bda2f5f760d676a0cb252e5b8f0b1490bd552b63 Mon Sep 17 00:00:00 2001 From: Damiano Donati Date: Mon, 15 Dec 2025 11:57:19 +0100 Subject: [PATCH] manifests-gen: set failurePolicy to Ignore for IPAM webhooks Added functions to set the failurePolicy to Ignore for both mutating and validating webhooks handling IPAM resources. This change addresses issues during bootstrap where the Kube API Server cannot reach webhooks, preventing the creation of IPAddress and IPAddressClaim resources. The new functions ensure that resources can be created even when webhooks are unreachable, aligning with the behavior of the Machine API. --- manifests-gen/customizations.go | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/manifests-gen/customizations.go b/manifests-gen/customizations.go index 7ff883077..6cc315ec9 100644 --- a/manifests-gen/customizations.go +++ b/manifests-gen/customizations.go @@ -55,9 +55,11 @@ func processObjects(objs []unstructured.Unstructured, providerName string) []uns providerConfigMapObjs = append(providerConfigMapObjs, obj) case "MutatingWebhookConfiguration": replaceCertManagerAnnotations(&obj) + setMutatingWebhookFailurePolicyIgnoreForIPAM(&obj) providerConfigMapObjs = append(providerConfigMapObjs, obj) case "ValidatingWebhookConfiguration": replaceCertManagerAnnotations(&obj) + setValidatingWebhookFailurePolicyIgnoreForIPAM(&obj) providerConfigMapObjs = append(providerConfigMapObjs, obj) case "CustomResourceDefinition": replaceCertManagerAnnotations(&obj) @@ -247,6 +249,72 @@ func replaceCertMangerServiceSecret(obj *unstructured.Unstructured, serviceSecre } } +// setMutatingWebhookFailurePolicyIgnoreForIPAM sets failurePolicy to Ignore for mutating webhooks handling IPAM resources. +// +// During bootstrap, the bootstrap node's Kube API Server receives IPAM create requests but is unable +// to reach the webhooks in the Cluster API namespace. This is because the bootstrap node doesn't have +// a route to the pods as it doesn't have access to the pod networks. If failurePolicy is set to Fail, +// the KAS cannot reach the webhook endpoints and the request fails, preventing creation of IPAddress +// and IPAddressClaim resources. This causes a chicken-and-egg problem as it prevents IPAM provisioning +// for the workers which won't start without their IP addresses being allocated. +// +// Setting failurePolicy to Ignore allows the resources to be created even when the webhooks are +// unreachable during bootstrap, matching what Machine API also does. +// +// More context: https://redhat-internal.slack.com/archives/C0A2M43S199/p1765540108488539 +func setMutatingWebhookFailurePolicyIgnoreForIPAM(obj *unstructured.Unstructured) { + mwc := &admissionregistration.MutatingWebhookConfiguration{} + if err := scheme.Convert(obj, mwc, nil); err != nil { + panic(err) + } + + for i := range mwc.Webhooks { + if webhookRulesHandleIPAMResources(mwc.Webhooks[i].Rules) { + ignore := admissionregistration.Ignore + mwc.Webhooks[i].FailurePolicy = &ignore + } + } + + if err := scheme.Convert(mwc, obj, nil); err != nil { + panic(err) + } +} + +// setValidatingWebhookFailurePolicyIgnoreForIPAM sets failurePolicy to Ignore for validating webhooks handling IPAM resources. +// See rationale for this in setMutatingWebhookFailurePolicyIgnoreForIPAM function. +func setValidatingWebhookFailurePolicyIgnoreForIPAM(obj *unstructured.Unstructured) { + vwc := &admissionregistration.ValidatingWebhookConfiguration{} + if err := scheme.Convert(obj, vwc, nil); err != nil { + panic(err) + } + + for i := range vwc.Webhooks { + if webhookRulesHandleIPAMResources(vwc.Webhooks[i].Rules) { + ignore := admissionregistration.Ignore + vwc.Webhooks[i].FailurePolicy = &ignore + } + } + + if err := scheme.Convert(vwc, obj, nil); err != nil { + panic(err) + } +} + +// webhookRulesHandleIPAMResources returns true if any of the webhook rules handle IPAM resources. +func webhookRulesHandleIPAMResources(rules []admissionregistration.RuleWithOperations) bool { + const ipamAPIGroup = "ipam.cluster.x-k8s.io" + + for _, rule := range rules { + for _, group := range rule.APIGroups { + if group == ipamAPIGroup { + return true + } + } + } + + return false +} + // isCRDGroup checks whether the object provided is a CRD for the specified API group. func isCRDGroup(obj *unstructured.Unstructured, group string) bool { switch obj.GetKind() {