Skip to content

Commit fd6668c

Browse files
committed
✨ Allow customizing generated webhook K8s Service
Allow customizing the webhook K8s Service's name, namespace, and port. ## example usage Look for the new `serviceName`, `serviceNamespace`, and `servicePort` fields. ``` ❯ GOBIN=(pwd)/bin go install ./cmd/* ❯ ./bin/controller-gen webhook -w Webhook +kubebuilder:webhook:admissionReviewVersions=<[]string>,failurePolicy=<string>,groups=<[]string>[,matchPolicy=<string>],mutating=<bool>,name=<string>[,path=<string>][,reinvocationPolicy=<string>],resources=<[]string>[,serviceName=<string>][,serviceNamespace=<string>][,servicePort=<int>][,sideEffects=<string>][,timeoutSeconds=<int>][,url=<string>],verbs=<[]string>,versions=<[]string>[,webhookVersions=<[]string>] package specifies how a webhook should be served. ... ``` contributes to #865 Signed-off-by: David Xia <[email protected]>
1 parent 0fa4366 commit fd6668c

File tree

6 files changed

+295
-4
lines changed

6 files changed

+295
-4
lines changed

pkg/webhook/parser.go

+33-4
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ import (
3838

3939
// The default {Mutating,Validating}WebhookConfiguration version to generate.
4040
const (
41-
v1 = "v1"
42-
defaultWebhookVersion = v1
41+
v1 = "v1"
42+
defaultWebhookVersion = v1
43+
defaultServiceName = "webhook-service"
44+
defaultServiceNamespace = "system"
4345
)
4446

4547
var (
@@ -118,6 +120,12 @@ type Config struct {
118120
// Name indicates the name of this webhook configuration. Should be a domain with at least three segments separated by dots
119121
Name string
120122

123+
// ServiceName indicates the name of the K8s Service the webhook uses.
124+
ServiceName string `marker:"serviceName,optional"`
125+
126+
// ServiceNamespace indicates the namespace of the K8s Service the webhook uses.
127+
ServiceNamespace string `marker:"serviceNamespace,optional"`
128+
121129
// Path specifies that path that the API server should connect to this webhook on. Must be
122130
// prefixed with a '/validate-' or '/mutate-' depending on the type, and followed by
123131
// $GROUP-$VERSION-$KIND where all values are lower-cased and the periods in the group
@@ -126,6 +134,9 @@ type Config struct {
126134
// /validate-batch-tutorial-kubebuilder-io-v1-cronjob
127135
Path string `marker:"path,optional"`
128136

137+
// ServicePort indicates the port of the K8s Service the webhook uses
138+
ServicePort *int32 `marker:"servicePort,optional"`
139+
129140
// WebhookVersions specifies the target API versions of the {Mutating,Validating}WebhookConfiguration objects
130141
// itself to generate. The only supported value is v1. Defaults to v1.
131142
WebhookVersions []string `marker:"webhookVersions,optional"`
@@ -318,11 +329,29 @@ func (c Config) clientConfig() (admissionregv1.WebhookClientConfig, error) {
318329

319330
path := c.Path
320331
if path != "" {
332+
var name, namespace string
333+
var port *int32
334+
335+
if c.ServiceName != "" {
336+
name = c.ServiceName
337+
} else {
338+
name = defaultServiceName
339+
}
340+
if c.ServiceNamespace != "" {
341+
namespace = c.ServiceNamespace
342+
} else {
343+
namespace = defaultServiceNamespace
344+
}
345+
if c.ServicePort != nil {
346+
port = c.ServicePort
347+
}
348+
321349
return admissionregv1.WebhookClientConfig{
322350
Service: &admissionregv1.ServiceReference{
323-
Name: "webhook-service",
324-
Namespace: "system",
351+
Name: name,
352+
Namespace: namespace,
325353
Path: &path,
354+
Port: port,
326355
},
327356
}, nil
328357
}

pkg/webhook/parser_integration_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,52 @@ var _ = Describe("Webhook Generation From Parsing to CustomResourceDefinition",
209209
assertSame(actualValidating, expectedValidating)
210210
})
211211

212+
It("should properly generate the webhook definition when the Service is customized with the `kubebuilder:webhook` marker", func() {
213+
By("switching into testdata to appease go modules")
214+
cwd, err := os.Getwd()
215+
Expect(err).NotTo(HaveOccurred())
216+
Expect(os.Chdir("./testdata/valid-custom-service")).To(Succeed()) // go modules are directory-sensitive
217+
defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }()
218+
219+
By("loading the roots")
220+
pkgs, err := loader.LoadRoots(".")
221+
Expect(err).NotTo(HaveOccurred())
222+
Expect(pkgs).To(HaveLen(1))
223+
224+
By("setting up the parser")
225+
reg := &markers.Registry{}
226+
Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed())
227+
Expect(reg.Register(webhook.WebhookConfigDefinition)).To(Succeed())
228+
229+
By("requesting that the manifest be generated")
230+
outputDir, err := os.MkdirTemp("", "webhook-integration-test")
231+
Expect(err).NotTo(HaveOccurred())
232+
defer os.RemoveAll(outputDir)
233+
genCtx := &genall.GenerationContext{
234+
Collector: &markers.Collector{Registry: reg},
235+
Roots: pkgs,
236+
OutputRule: genall.OutputToDirectory(outputDir),
237+
}
238+
Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed())
239+
for _, r := range genCtx.Roots {
240+
Expect(r.Errors).To(HaveLen(0))
241+
}
242+
243+
By("loading the generated v1 YAML")
244+
actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml"))
245+
Expect(err).NotTo(HaveOccurred())
246+
actualMutating, actualValidating := unmarshalBothV1(actualFile)
247+
248+
By("loading the desired v1 YAML")
249+
expectedFile, err := os.ReadFile("manifests.yaml")
250+
Expect(err).NotTo(HaveOccurred())
251+
expectedMutating, expectedValidating := unmarshalBothV1(expectedFile)
252+
253+
By("comparing the two")
254+
assertSame(actualMutating, expectedMutating)
255+
assertSame(actualValidating, expectedValidating)
256+
})
257+
212258
It("should generate the ordered webhook definitions", func() {
213259
By("switching into testdata to appease go modules")
214260
cwd, err := os.Getwd()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
//go:generate ../../../../.run-controller-gen.sh webhook paths=. output:dir=.
17+
18+
// +groupName=testdata.kubebuilder.io
19+
// +versionName=v1
20+
package cronjob
21+
22+
import (
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
)
25+
26+
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
27+
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
28+
29+
// CronJobSpec defines the desired state of CronJob
30+
type CronJobSpec struct {
31+
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
32+
Schedule string `json:"schedule"`
33+
}
34+
35+
// CronJobStatus defines the observed state of CronJob
36+
type CronJobStatus struct {
37+
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
38+
// Important: Run "make" to regenerate code after modifying this file
39+
40+
// Information when was the last time the job was successfully scheduled.
41+
// +optional
42+
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
43+
}
44+
45+
// +kubebuilder:object:root=true
46+
// +kubebuilder:subresource:status
47+
// +kubebuilder:resource:singular=mycronjob
48+
49+
// CronJob is the Schema for the cronjobs API
50+
type CronJob struct {
51+
/*
52+
*/
53+
metav1.TypeMeta `json:",inline"`
54+
metav1.ObjectMeta `json:"metadata,omitempty"`
55+
56+
Spec CronJobSpec `json:"spec,omitempty"`
57+
Status CronJobStatus `json:"status,omitempty"`
58+
}
59+
60+
// +kubebuilder:object:root=true
61+
62+
// CronJobList contains a list of CronJob
63+
type CronJobList struct {
64+
metav1.TypeMeta `json:",inline"`
65+
metav1.ListMeta `json:"metadata,omitempty"`
66+
Items []CronJob `json:"items"`
67+
}
68+
69+
func init() {
70+
SchemeBuilder.Register(&CronJob{}, &CronJobList{})
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
apiVersion: admissionregistration.k8s.io/v1
3+
kind: MutatingWebhookConfiguration
4+
metadata:
5+
name: mutating-webhook-configuration
6+
webhooks:
7+
- admissionReviewVersions:
8+
- v1
9+
- v1beta1
10+
clientConfig:
11+
service:
12+
name: FOO
13+
namespace: system
14+
path: /mutate-testdata-kubebuilder-io-v1-cronjob
15+
failurePolicy: Fail
16+
matchPolicy: Equivalent
17+
name: default.cronjob.testdata.kubebuilder.io
18+
rules:
19+
- apiGroups:
20+
- testdata.kubebuiler.io
21+
apiVersions:
22+
- v1
23+
operations:
24+
- CREATE
25+
- UPDATE
26+
resources:
27+
- cronjobs
28+
sideEffects: None
29+
timeoutSeconds: 10
30+
reinvocationPolicy: IfNeeded
31+
---
32+
apiVersion: admissionregistration.k8s.io/v1
33+
kind: ValidatingWebhookConfiguration
34+
metadata:
35+
name: validating-webhook-configuration
36+
webhooks:
37+
- admissionReviewVersions:
38+
- v1
39+
- v1beta1
40+
clientConfig:
41+
service:
42+
name: DEAD
43+
namespace: BEEF
44+
path: /validate-testdata-kubebuilder-io-v1-cronjob
45+
port: 1234
46+
failurePolicy: Fail
47+
matchPolicy: Equivalent
48+
name: validation.cronjob.testdata.kubebuilder.io
49+
rules:
50+
- apiGroups:
51+
- testdata.kubebuiler.io
52+
apiVersions:
53+
- v1
54+
operations:
55+
- CREATE
56+
- UPDATE
57+
resources:
58+
- cronjobs
59+
sideEffects: None
60+
timeoutSeconds: 10
61+
- admissionReviewVersions:
62+
- v1
63+
- v1beta1
64+
clientConfig:
65+
service:
66+
name: webhook-service
67+
namespace: system
68+
path: /validate-testdata-kubebuilder-io-v1-cronjob
69+
failurePolicy: Fail
70+
matchPolicy: Equivalent
71+
name: validation.cronjob.testdata.kubebuilder.io
72+
rules:
73+
- apiGroups:
74+
- testdata.kubebuiler.io
75+
apiVersions:
76+
- v1
77+
operations:
78+
- CREATE
79+
- UPDATE
80+
resources:
81+
- cronjobs
82+
sideEffects: NoneOnDryRun
83+
timeoutSeconds: 10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package cronjob
17+
18+
import (
19+
"k8s.io/apimachinery/pkg/runtime"
20+
ctrl "sigs.k8s.io/controller-runtime"
21+
"sigs.k8s.io/controller-runtime/pkg/webhook"
22+
)
23+
24+
func (c *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
25+
return ctrl.NewWebhookManagedBy(mgr).
26+
For(c).
27+
Complete()
28+
}
29+
30+
// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,path=/validate-testdata-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobs,serviceName=DEAD,serviceNamespace=BEEF,servicePort=1234,versions=v1,name=validation.cronjob.testdata.kubebuilder.io,sideEffects=None,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1
31+
// +kubebuilder:webhook:verbs=create;update,path=/validate-testdata-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobs,versions=v1,name=validation.cronjob.testdata.kubebuilder.io,sideEffects=NoneOnDryRun,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1
32+
// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,path=/mutate-testdata-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobs,serviceName=FOO,versions=v1,name=default.cronjob.testdata.kubebuilder.io,sideEffects=None,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1,reinvocationPolicy=IfNeeded
33+
34+
var _ webhook.Defaulter = &CronJob{}
35+
var _ webhook.Validator = &CronJob{}
36+
37+
func (c *CronJob) Default() {
38+
}
39+
40+
func (c *CronJob) ValidateCreate() error {
41+
return nil
42+
}
43+
44+
func (c *CronJob) ValidateUpdate(_ runtime.Object) error {
45+
return nil
46+
}
47+
48+
func (c *CronJob) ValidateDelete() error {
49+
return nil
50+
}

pkg/webhook/zz_generated.markerhelp.go

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)