Skip to content

Commit 509ce87

Browse files
Add support for custom webhook paths
Users can now set custom paths for webhooks using --defaulting-path and --validation-path flags. Requires controller-runtime v0.20+. Assisted-by: Cursor
1 parent c580bca commit 509ce87

File tree

46 files changed

+1463
-29
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1463
-29
lines changed

docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ validate anything on deletion.
155155
/*
156156
This marker is responsible for generating a validation webhook manifest.
157157
*/
158+
159+
// NOTE: If you want to customise the 'path', use the flags '--defaulting-path' or '--validation-path'.
158160
// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=vcronjob-v1.kb.io,admissionReviewVersions=v1
159161

160162
// CronJobCustomValidator struct is responsible for validating the CronJob resource

docs/book/src/cronjob-tutorial/webhook-implementation.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,37 @@ Kubebuilder takes care of the rest for you, such as
1111
1. Creating handlers for your webhooks.
1212
1. Registering each handler with a path in your server.
1313

14-
First, let's scaffold the webhooks for our CRD (CronJob). Well need to run the following command with the `--defaulting` and `--programmatic-validation` flags (since our test project will use defaulting and validating webhooks):
14+
First, let's scaffold the webhooks for our CRD (CronJob). We'll need to run the following command with the `--defaulting` and `--programmatic-validation` flags (since our test project will use defaulting and validating webhooks):
1515

1616
```bash
1717
kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
1818
```
1919

2020
This will scaffold the webhook functions and register your webhook with the manager in your `main.go` for you.
2121

22+
## Custom Webhook Paths
23+
24+
You can specify custom HTTP paths for your webhooks using the `--defaulting-path` and `--validation-path` flags:
25+
26+
```bash
27+
# Custom path for defaulting webhook
28+
kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --defaulting-path=/my-custom-mutate-path
29+
30+
# Custom path for validation webhook
31+
kubebuilder create webhook --group batch --version v1 --kind CronJob --programmatic-validation --validation-path=/my-custom-validate-path
32+
33+
# Both webhooks with different custom paths
34+
kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation \
35+
--defaulting-path=/custom-mutate --validation-path=/custom-validate
36+
```
37+
38+
This changes the path in the webhook marker annotation but does not change where the webhook files are scaffolded. The webhook files will still be created in `internal/webhook/v1/`.
39+
40+
<aside class="note">
41+
<h1>Version Requirements</h1>
42+
43+
Custom webhook paths require **controller-runtime v0.21+**. In earlier versions (< v0.20), the webhook path must follow a specific pattern and cannot be customized. The path is automatically generated based on the resource's group, version, and kind (e.g., `/mutate-batch-v1-cronjob`).
44+
45+
</aside>
46+
2247
{{#literatego ./testdata/project/internal/webhook/v1/cronjob_webhook.go}}

docs/book/src/multiversion-tutorial/conversion.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ The above command will generate the `cronjob_conversion.go` next to our
1313
`cronjob_types.go` file, to avoid
1414
cluttering up our main types file with extra functions.
1515

16+
<aside class="note">
17+
<h1>Conversion Webhooks and Custom Paths</h1>
18+
19+
Unlike defaulting and validation webhooks, conversion webhooks do not support custom paths
20+
via command-line flags. Conversion webhooks use CRD conversion configuration
21+
(`.spec.conversion.webhook.clientConfig.service.path` in the CRD) rather than webhook
22+
marker annotations. The path for conversion webhooks is managed differently and cannot
23+
be customized through kubebuilder flags.
24+
</aside>
25+
1626
## Hub...
1727

1828
First, we'll implement the hub. We'll choose the v1 version as the hub:

docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ validate anything on deletion.
160160
/*
161161
This marker is responsible for generating a validation webhook manifest.
162162
*/
163+
163164
// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=vcronjob-v1.kb.io,admissionReviewVersions=v1
164165

165166
// CronJobCustomValidator struct is responsible for validating the CronJob resource

docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ func (d *CronJobCustomDefaulter) Default(_ context.Context, obj runtime.Object)
8888
}
8989

9090
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
91-
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
92-
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
91+
// NOTE: If you want to customise the 'path', use the flags '--defaulting-path' or '--validation-path'.
9392
// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v2-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v2,name=vcronjob-v2.kb.io,admissionReviewVersions=v1
9493

9594
// CronJobCustomValidator struct is responsible for validating the CronJob resource

docs/book/src/reference/admission-webhook.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,37 @@ object after your validation has accepted it.
3030

3131
</aside>
3232

33+
## Custom Webhook Paths
34+
35+
By default, Kubebuilder generates webhook paths based on the resource's group, version, and kind. For example:
36+
- Mutating webhook for `batch/v1/CronJob`: `/mutate-batch-v1-cronjob`
37+
- Validating webhook for `batch/v1/CronJob`: `/validate-batch-v1-cronjob`
38+
39+
You can specify custom paths for webhooks using dedicated flags:
40+
41+
```bash
42+
# Custom path for defaulting webhook
43+
kubebuilder create webhook --group batch --version v1 --kind CronJob \
44+
--defaulting --defaulting-path=/my-custom-mutate-path
45+
46+
# Custom path for validation webhook
47+
kubebuilder create webhook --group batch --version v1 --kind CronJob \
48+
--programmatic-validation --validation-path=/my-custom-validate-path
49+
50+
# Both webhooks with different custom paths
51+
kubebuilder create webhook --group batch --version v1 --kind CronJob \
52+
--defaulting --programmatic-validation \
53+
--defaulting-path=/custom-mutate --validation-path=/custom-validate
54+
```
55+
56+
<aside class="note">
57+
<h1>Version Requirements</h1>
58+
59+
Custom webhook paths require **controller-runtime v0.21+**. In earlier versions (< v0.20), the webhook path follows a
60+
fixed pattern based on the resource's group, version, and kind, and cannot be customized.
61+
</aside>
62+
63+
3364
## Handling Resource Status in Admission Webhooks
3465

3566
<aside class="warning">

hack/docs/internal/cronjob-tutorial/generate_cronjob.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,13 +471,12 @@ Then, we set up the webhook with the manager.
471471
err = pluginutil.ReplaceInFile(
472472
filepath.Join(sp.ctx.Dir, "internal/webhook/v1/cronjob_webhook.go"),
473473
`// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!`, webhooksNoticeMarker)
474-
hackutils.CheckError("fixing cronjob_webhook.go by replacing note about path attribute", err)
474+
hackutils.CheckError("fixing cronjob_webhook.go by replacing note about path attribute for webhook notice ", err)
475475

476476
err = pluginutil.ReplaceInFile(
477477
filepath.Join(sp.ctx.Dir, "internal/webhook/v1/cronjob_webhook.go"),
478-
`// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
479-
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.`, explanationValidateCRD)
480-
hackutils.CheckError("fixing cronjob_webhook.go by replacing note about path attribute", err)
478+
`// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.`, explanationValidateCRD)
479+
hackutils.CheckError("fixing cronjob_webhook.go by replacing note about path attribute for explanation validate CRD", err)
481480

482481
err = pluginutil.ReplaceInFile(
483482
filepath.Join(sp.ctx.Dir, "internal/webhook/v1/cronjob_webhook.go"),

hack/docs/internal/cronjob-tutorial/webhook_implementation.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ validate anything on deletion.
9090
9191
/*
9292
This marker is responsible for generating a validation webhook manifest.
93-
*/`
93+
*/
94+
95+
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.`
9496

9597
const customInterfaceDefaultInfo = `/*
9698
We use the ` + "`" + `webhook.CustomDefaulter` + "`" + `interface to set defaults to our CRD.

pkg/cli/alpha/internal/generate.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,15 @@ func getWebhookResourceFlags(res resource.Resource) []string {
459459
}
460460
if res.HasValidationWebhook() {
461461
args = append(args, "--programmatic-validation")
462+
if res.Webhooks.ValidationPath != "" {
463+
args = append(args, "--validation-path", res.Webhooks.ValidationPath)
464+
}
462465
}
463466
if res.HasDefaultingWebhook() {
464467
args = append(args, "--defaulting")
468+
if res.Webhooks.DefaultingPath != "" {
469+
args = append(args, "--defaulting-path", res.Webhooks.DefaultingPath)
470+
}
465471
}
466472
if res.HasConversionWebhook() {
467473
args = append(args, "--conversion")
@@ -470,6 +476,7 @@ func getWebhookResourceFlags(res resource.Resource) []string {
470476
args = append(args, "--spoke", spoke)
471477
}
472478
}
479+
// Note: conversion webhooks don't use custom path flags
473480
}
474481
return args
475482
}

pkg/model/resource/webhooks.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ type Webhooks struct {
3535
Conversion bool `json:"conversion,omitempty"`
3636

3737
Spoke []string `json:"spoke,omitempty"`
38+
39+
// DefaultingPath holds the custom path for the defaulting/mutating webhook.
40+
// This path is used in the +kubebuilder:webhook marker annotation.
41+
DefaultingPath string `json:"defaultingPath,omitempty"`
42+
43+
// ValidationPath holds the custom path for the validation webhook.
44+
// This path is used in the +kubebuilder:webhook marker annotation.
45+
ValidationPath string `json:"validationPath,omitempty"`
3846
}
3947

4048
// Validate checks that the Webhooks is valid.
@@ -73,6 +81,8 @@ func (webhooks Webhooks) Copy() Webhooks {
7381
Validation: webhooks.Validation,
7482
Conversion: webhooks.Conversion,
7583
Spoke: spokeCopy,
84+
DefaultingPath: webhooks.DefaultingPath,
85+
ValidationPath: webhooks.ValidationPath,
7686
}
7787
}
7888

@@ -114,14 +124,23 @@ func (webhooks *Webhooks) Update(other *Webhooks) error {
114124
}
115125
}
116126

127+
// Update custom paths (other takes precedence if not empty)
128+
if other.DefaultingPath != "" {
129+
webhooks.DefaultingPath = other.DefaultingPath
130+
}
131+
if other.ValidationPath != "" {
132+
webhooks.ValidationPath = other.ValidationPath
133+
}
134+
117135
return nil
118136
}
119137

120138
// IsEmpty returns if the Webhooks' fields all contain zero-values.
121139
func (webhooks Webhooks) IsEmpty() bool {
122140
return webhooks.WebhookVersion == "" &&
123141
!webhooks.Defaulting && !webhooks.Validation &&
124-
!webhooks.Conversion && len(webhooks.Spoke) == 0
142+
!webhooks.Conversion && len(webhooks.Spoke) == 0 &&
143+
webhooks.DefaultingPath == "" && webhooks.ValidationPath == ""
125144
}
126145

127146
// AddSpoke adds a new spoke version to the Webhooks configuration.

0 commit comments

Comments
 (0)