Skip to content

Commit 8c7eab9

Browse files
Remove the usage of deprecated functions for webhooks
Motivation: kubernetes-sigs/controller-runtime#2877
1 parent b5235ef commit 8c7eab9

31 files changed

+1356
-229
lines changed

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

Lines changed: 98 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ limitations under the License.
1818
package v1
1919

2020
import (
21+
"context"
22+
"fmt"
2123
"github.com/robfig/cron"
2224
apierrors "k8s.io/apimachinery/pkg/api/errors"
23-
"k8s.io/apimachinery/pkg/runtime"
2425
"k8s.io/apimachinery/pkg/runtime/schema"
2526
validationutils "k8s.io/apimachinery/pkg/util/validation"
2627
"k8s.io/apimachinery/pkg/util/validation/field"
28+
29+
"k8s.io/apimachinery/pkg/runtime"
2730
ctrl "sigs.k8s.io/controller-runtime"
2831
logf "sigs.k8s.io/controller-runtime/pkg/log"
2932
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -46,6 +49,8 @@ Then, we set up the webhook with the manager.
4649
func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
4750
return ctrl.NewWebhookManagedBy(mgr).
4851
For(r).
52+
WithValidator(&CronJobCustomValidator{}).
53+
WithDefaulter(&CronJobCustomDefaulter{}).
4954
Complete()
5055
}
5156

@@ -65,26 +70,42 @@ A webhook will automatically be served that calls this defaulting.
6570
The `Default` method is expected to mutate the receiver, setting the defaults.
6671
*/
6772

68-
var _ webhook.Defaulter = &CronJob{}
73+
type CronJobCustomDefaulter struct{}
6974

70-
// Default implements webhook.Defaulter so a webhook will be registered for the type
71-
func (r *CronJob) Default() {
72-
cronjoblog.Info("default", "name", r.Name)
75+
var _ webhook.CustomDefaulter = &CronJobCustomDefaulter{}
7376

74-
if r.Spec.ConcurrencyPolicy == "" {
75-
r.Spec.ConcurrencyPolicy = AllowConcurrent
77+
// Default implements webhook.CustomDefaulter so a webhook will be registered for the type
78+
func (d *CronJobCustomDefaulter) Default(ctx context.Context, obj runtime.Object) error {
79+
cronjoblog.Info("CustomDefaulter for Admiral")
80+
req, err := admission.RequestFromContext(ctx)
81+
if err != nil {
82+
return fmt.Errorf("expected admission.Request in ctx: %w", err)
7683
}
77-
if r.Spec.Suspend == nil {
78-
r.Spec.Suspend = new(bool)
84+
if req.Kind.Kind != "CronJob" {
85+
return fmt.Errorf("expected Kind Admiral got %q", req.Kind.Kind)
86+
}
87+
castedObj, ok := obj.(*CronJob)
88+
if !ok {
89+
return fmt.Errorf("expected an CronJob object but got %T", obj)
90+
}
91+
cronjoblog.Info("default", "name", castedObj.GetName())
92+
93+
if castedObj.Spec.ConcurrencyPolicy == "" {
94+
castedObj.Spec.ConcurrencyPolicy = AllowConcurrent
7995
}
80-
if r.Spec.SuccessfulJobsHistoryLimit == nil {
81-
r.Spec.SuccessfulJobsHistoryLimit = new(int32)
82-
*r.Spec.SuccessfulJobsHistoryLimit = 3
96+
if castedObj.Spec.Suspend == nil {
97+
castedObj.Spec.Suspend = new(bool)
8398
}
84-
if r.Spec.FailedJobsHistoryLimit == nil {
85-
r.Spec.FailedJobsHistoryLimit = new(int32)
86-
*r.Spec.FailedJobsHistoryLimit = 1
99+
if castedObj.Spec.SuccessfulJobsHistoryLimit == nil {
100+
castedObj.Spec.SuccessfulJobsHistoryLimit = new(int32)
101+
*castedObj.Spec.SuccessfulJobsHistoryLimit = 3
87102
}
103+
if castedObj.Spec.FailedJobsHistoryLimit == nil {
104+
castedObj.Spec.FailedJobsHistoryLimit = new(int32)
105+
*castedObj.Spec.FailedJobsHistoryLimit = 1
106+
}
107+
108+
return nil
88109
}
89110

90111
/*
@@ -118,40 +139,80 @@ validate anything on deletion.
118139
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
119140
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
120141

121-
var _ webhook.Validator = &CronJob{}
142+
type CronJobCustomValidator struct{}
122143

123-
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
124-
func (r *CronJob) ValidateCreate() (admission.Warnings, error) {
125-
cronjoblog.Info("validate create", "name", r.Name)
144+
var _ webhook.CustomValidator = &CronJobCustomValidator{}
126145

127-
return nil, r.validateCronJob()
146+
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type
147+
func (v *CronJobCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
148+
cronjoblog.Info("Creation Validation for CronJob")
149+
150+
req, err := admission.RequestFromContext(ctx)
151+
if err != nil {
152+
return nil, fmt.Errorf("expected admission.Request in ctx: %w", err)
153+
}
154+
if req.Kind.Kind != "CronJob" {
155+
return nil, fmt.Errorf("expected Kind CronJob got %q", req.Kind.Kind)
156+
}
157+
castedObj, ok := obj.(*CronJob)
158+
if !ok {
159+
return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
160+
}
161+
cronjoblog.Info("default", "name", castedObj.GetName())
162+
163+
return nil, v.validateCronJob(castedObj)
128164
}
129165

130-
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
131-
func (r *CronJob) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
132-
cronjoblog.Info("validate update", "name", r.Name)
166+
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type
167+
func (v *CronJobCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
168+
cronjoblog.Info("Update Validation for CronJob")
169+
req, err := admission.RequestFromContext(ctx)
170+
if err != nil {
171+
return nil, fmt.Errorf("expected admission.Request in ctx: %w", err)
172+
}
173+
if req.Kind.Kind != "CronJob" {
174+
return nil, fmt.Errorf("expected Kind CronJob got %q", req.Kind.Kind)
175+
}
176+
castedObj, ok := newObj.(*CronJob)
177+
if !ok {
178+
return nil, fmt.Errorf("expected a CronJob object but got %T", newObj)
179+
}
180+
cronjoblog.Info("default", "name", castedObj.GetName())
133181

134-
return nil, r.validateCronJob()
182+
return nil, v.validateCronJob(castedObj)
135183
}
136184

137-
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
138-
func (r *CronJob) ValidateDelete() (admission.Warnings, error) {
139-
cronjoblog.Info("validate delete", "name", r.Name)
185+
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type
186+
func (v *CronJobCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
187+
cronjoblog.Info("Deletion Validation for CronJob")
188+
req, err := admission.RequestFromContext(ctx)
189+
if err != nil {
190+
return nil, fmt.Errorf("expected admission.Request in ctx: %w", err)
191+
}
192+
if req.Kind.Kind != "CronJob" {
193+
return nil, fmt.Errorf("expected Kind CronJob got %q", req.Kind.Kind)
194+
}
195+
castedObj, ok := obj.(*CronJob)
196+
if !ok {
197+
return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
198+
}
199+
cronjoblog.Info("default", "name", castedObj.GetName())
140200

141201
// TODO(user): fill in your validation logic upon object deletion.
202+
142203
return nil, nil
143204
}
144205

145206
/*
146207
We validate the name and the spec of the CronJob.
147208
*/
148209

149-
func (r *CronJob) validateCronJob() error {
210+
func (v *CronJobCustomValidator) validateCronJob(castedObj *CronJob) error {
150211
var allErrs field.ErrorList
151-
if err := r.validateCronJobName(); err != nil {
212+
if err := v.validateCronJobName(castedObj); err != nil {
152213
allErrs = append(allErrs, err)
153214
}
154-
if err := r.validateCronJobSpec(); err != nil {
215+
if err := v.validateCronJobSpec(castedObj); err != nil {
155216
allErrs = append(allErrs, err)
156217
}
157218
if len(allErrs) == 0 {
@@ -160,7 +221,7 @@ func (r *CronJob) validateCronJob() error {
160221

161222
return apierrors.NewInvalid(
162223
schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"},
163-
r.Name, allErrs)
224+
castedObj.Name, allErrs)
164225
}
165226

166227
/*
@@ -173,11 +234,11 @@ declaring validation by running `controller-gen crd -w`,
173234
or [here](/reference/markers/crd-validation.md).
174235
*/
175236

176-
func (r *CronJob) validateCronJobSpec() *field.Error {
237+
func (v *CronJobCustomValidator) validateCronJobSpec(castedObj *CronJob) *field.Error {
177238
// The field helpers from the kubernetes API machinery help us return nicely
178239
// structured validation errors.
179240
return validateScheduleFormat(
180-
r.Spec.Schedule,
241+
castedObj.Spec.Schedule,
181242
field.NewPath("spec").Child("schedule"))
182243
}
183244

@@ -202,15 +263,15 @@ the apimachinery repo, so we can't declaratively validate it using
202263
the validation schema.
203264
*/
204265

205-
func (r *CronJob) validateCronJobName() *field.Error {
206-
if len(r.ObjectMeta.Name) > validationutils.DNS1035LabelMaxLength-11 {
207-
// The job name length is 63 character like all Kubernetes objects
266+
func (v *CronJobCustomValidator) validateCronJobName(castedObj *CronJob) *field.Error {
267+
if len(castedObj.ObjectMeta.Name) > validationutils.DNS1035LabelMaxLength-11 {
268+
// The job name length is 63 characters like all Kubernetes objects
208269
// (which must fit in a DNS subdomain). The cronjob controller appends
209270
// a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
210271
// a job. The job name length limit is 63 characters. Therefore cronjob
211272
// names must have length <= 63-11=52. If we don't validate this here,
212273
// then job creation will fail later.
213-
return field.Invalid(field.NewPath("metadata").Child("name"), r.Name, "must be no more than 52 characters")
274+
return field.Invalid(field.NewPath("metadata").Child("name"), castedObj.Name, "must be no more than 52 characters")
214275
}
215276
return nil
216277
}

docs/book/src/cronjob-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,79 @@
11
module sigs.k8s.io/kubebuilder/v4
22

3-
go 1.22
3+
go 1.22.0
4+
5+
toolchain go1.22.3
46

57
require (
68
github.com/gobuffalo/flect v1.0.2
79
github.com/onsi/ginkgo/v2 v2.19.1
810
github.com/onsi/gomega v1.34.1
11+
github.com/robfig/cron v1.2.0
912
github.com/sirupsen/logrus v1.9.3
1013
github.com/spf13/afero v1.11.0
1114
github.com/spf13/cobra v1.8.1
1215
github.com/spf13/pflag v1.0.5
1316
golang.org/x/text v0.16.0
1417
golang.org/x/tools v0.23.0
18+
k8s.io/apimachinery v0.30.3
19+
sigs.k8s.io/controller-runtime v0.18.4
1520
sigs.k8s.io/yaml v1.4.0
1621
)
1722

1823
require (
24+
github.com/beorn7/perks v1.0.1 // indirect
25+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
26+
github.com/davecgh/go-spew v1.1.1 // indirect
27+
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
28+
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
29+
github.com/fsnotify/fsnotify v1.7.0 // indirect
1930
github.com/go-logr/logr v1.4.2 // indirect
31+
github.com/go-openapi/jsonpointer v0.19.6 // indirect
32+
github.com/go-openapi/jsonreference v0.20.2 // indirect
33+
github.com/go-openapi/swag v0.22.3 // indirect
2034
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
35+
github.com/gogo/protobuf v1.3.2 // indirect
36+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
37+
github.com/golang/protobuf v1.5.4 // indirect
38+
github.com/google/gnostic-models v0.6.8 // indirect
2139
github.com/google/go-cmp v0.6.0 // indirect
40+
github.com/google/gofuzz v1.2.0 // indirect
2241
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
42+
github.com/google/uuid v1.4.0 // indirect
43+
github.com/imdario/mergo v0.3.6 // indirect
2344
github.com/inconshreveable/mousetrap v1.1.0 // indirect
45+
github.com/josharian/intern v1.0.0 // indirect
46+
github.com/json-iterator/go v1.1.12 // indirect
47+
github.com/mailru/easyjson v0.7.7 // indirect
48+
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
49+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
50+
github.com/modern-go/reflect2 v1.0.2 // indirect
51+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
52+
github.com/pkg/errors v0.9.1 // indirect
53+
github.com/prometheus/client_golang v1.16.0 // indirect
54+
github.com/prometheus/client_model v0.4.0 // indirect
55+
github.com/prometheus/common v0.44.0 // indirect
56+
github.com/prometheus/procfs v0.12.0 // indirect
2457
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
2558
golang.org/x/mod v0.19.0 // indirect
2659
golang.org/x/net v0.27.0 // indirect
60+
golang.org/x/oauth2 v0.15.0 // indirect
2761
golang.org/x/sync v0.7.0 // indirect
2862
golang.org/x/sys v0.22.0 // indirect
63+
golang.org/x/term v0.22.0 // indirect
64+
golang.org/x/time v0.5.0 // indirect
65+
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
66+
google.golang.org/appengine v1.6.7 // indirect
67+
google.golang.org/protobuf v1.34.1 // indirect
68+
gopkg.in/inf.v0 v0.9.1 // indirect
69+
gopkg.in/yaml.v2 v2.4.0 // indirect
2970
gopkg.in/yaml.v3 v3.0.1 // indirect
71+
k8s.io/api v0.30.1 // indirect
72+
k8s.io/apiextensions-apiserver v0.30.1 // indirect
73+
k8s.io/client-go v0.30.1 // indirect
74+
k8s.io/klog/v2 v2.120.1 // indirect
75+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
76+
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
77+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
78+
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
3079
)

0 commit comments

Comments
 (0)