@@ -18,12 +18,15 @@ limitations under the License.
1818package v1
1919
2020import (
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,13 @@ Then, we set up the webhook with the manager.
4649func (r * CronJob ) SetupWebhookWithManager (mgr ctrl.Manager ) error {
4750 return ctrl .NewWebhookManagedBy (mgr ).
4851 For (r ).
52+ WithValidator (& CronJobCustomValidator {}).
53+ WithDefaulter (& CronJobCustomDefaulter {
54+ DefaultConcurrencyPolicy : AllowConcurrent ,
55+ DefaultSuspend : false ,
56+ DefaultSuccessfulJobsHistoryLimit : 3 ,
57+ DefaultFailedJobsHistoryLimit : 1 ,
58+ }).
4959 Complete ()
5060}
5161
@@ -59,31 +69,63 @@ The meaning of each marker can be found [here](/reference/markers/webhook.md).
5969// +kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
6070
6171/*
62- We use the `webhook.Defaulter ` interface to set defaults to our CRD.
72+ We use the `webhook.CustomDefaulter ` interface to set defaults to our CRD.
6373A webhook will automatically be served that calls this defaulting.
6474
6575The `Default` method is expected to mutate the receiver, setting the defaults.
6676*/
6777
68- var _ webhook.Defaulter = & CronJob {}
78+ // +kubebuilder:object:generate=false
79+ // CronJobCustomDefaulter struct is responsible for setting default values on the custom resource of the
80+ // Kind CronJob when those are created or updated.
81+ //
82+ // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
83+ // as it is used only for temporary operations and does not need to be deeply copied.
84+ type CronJobCustomDefaulter struct {
85+ cronjob * CronJob
86+
87+ // Default values for various CronJob fields
88+ DefaultConcurrencyPolicy ConcurrencyPolicy
89+ DefaultSuspend bool
90+ DefaultSuccessfulJobsHistoryLimit int32
91+ DefaultFailedJobsHistoryLimit int32
92+ }
93+
94+ var _ webhook.CustomDefaulter = & CronJobCustomDefaulter {}
6995
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 )
96+ // Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind CronJob
97+ func (d * CronJobCustomDefaulter ) Default (ctx context.Context , obj runtime.Object ) error {
98+ cronjoblog .Info ("CustomDefaulter for CronJob" )
99+ cronjob , ok := obj .(* CronJob )
100+ if ! ok {
101+ return fmt .Errorf ("expected an CronJob object but got %T" , obj )
102+ }
103+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
104+
105+ // Assign the CronJob object to the CronJobCustomDefaulter struct field
106+ // so that it can be used in the defaulting logic
107+ d .cronjob = cronjob
108+
109+ d .setDefault ()
110+
111+ return nil
112+ }
73113
74- if r .Spec .ConcurrencyPolicy == "" {
75- r .Spec .ConcurrencyPolicy = AllowConcurrent
114+ func (d * CronJobCustomDefaulter ) setDefault () {
115+ if d .cronjob .Spec .ConcurrencyPolicy == "" {
116+ d .cronjob .Spec .ConcurrencyPolicy = d .DefaultConcurrencyPolicy
76117 }
77- if r .Spec .Suspend == nil {
78- r .Spec .Suspend = new (bool )
118+ if d .cronjob .Spec .Suspend == nil {
119+ d .cronjob .Spec .Suspend = new (bool )
120+ * d .cronjob .Spec .Suspend = d .DefaultSuspend
79121 }
80- if r .Spec .SuccessfulJobsHistoryLimit == nil {
81- r .Spec .SuccessfulJobsHistoryLimit = new (int32 )
82- * r . Spec .SuccessfulJobsHistoryLimit = 3
122+ if d . cronjob .Spec .SuccessfulJobsHistoryLimit == nil {
123+ d . cronjob .Spec .SuccessfulJobsHistoryLimit = new (int32 )
124+ * d . cronjob . Spec .SuccessfulJobsHistoryLimit = d . DefaultSuccessfulJobsHistoryLimit
83125 }
84- if r .Spec .FailedJobsHistoryLimit == nil {
85- r .Spec .FailedJobsHistoryLimit = new (int32 )
86- * r . Spec .FailedJobsHistoryLimit = 1
126+ if d . cronjob .Spec .FailedJobsHistoryLimit == nil {
127+ d . cronjob .Spec .FailedJobsHistoryLimit = new (int32 )
128+ * d . cronjob . Spec .FailedJobsHistoryLimit = d . DefaultFailedJobsHistoryLimit
87129 }
88130}
89131
@@ -101,7 +143,7 @@ sometimes more advanced use cases call for complex validation.
101143For instance, we'll see below that we use this to validate a well-formed cron
102144schedule without making up a long regular expression.
103145
104- If `webhook.Validator ` interface is implemented, a webhook will automatically be
146+ If `webhook.CustomValidator ` interface is implemented, a webhook will automatically be
105147served that calls the validation.
106148
107149The `ValidateCreate`, `ValidateUpdate` and `ValidateDelete` methods are expected
@@ -118,40 +160,80 @@ validate anything on deletion.
118160// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
119161// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
120162
121- var _ webhook.Validator = & CronJob {}
163+ // +kubebuilder:object:generate=false
164+ // CronJobCustomValidator struct is responsible for validating the CronJob resource
165+ // when it is created, updated, or deleted.
166+ //
167+ // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
168+ // as this struct is used only for temporary operations and does not need to be deeply copied.
169+ type CronJobCustomValidator struct {
170+ cronjob * CronJob
122171
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 )
172+ //TODO(user): Add more fields as needed for validation
173+ }
174+
175+ var _ webhook.CustomValidator = & CronJobCustomValidator {}
176+
177+ // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type CronJob
178+ func (v * CronJobCustomValidator ) ValidateCreate (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
179+ cronjoblog .Info ("Creation Validation for CronJob" )
180+ cronjob , ok := obj .(* CronJob )
181+ if ! ok {
182+ return nil , fmt .Errorf ("expected a CronJob object but got %T" , obj )
183+ }
184+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
185+
186+ // Assign the CronJob object to the CronJobCustomValidator struct field
187+ // so that it can be used in the validation logic
188+ v .cronjob = cronjob
126189
127- return nil , r .validateCronJob ()
190+ return nil , v .validateCronJob ()
128191}
129192
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 )
193+ // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type CronJob
194+ func (v * CronJobCustomValidator ) ValidateUpdate (ctx context.Context , oldObj , newObj runtime.Object ) (admission.Warnings , error ) {
195+ cronjoblog .Info ("Update Validation for CronJob" )
196+ cronjob , ok := newObj .(* CronJob )
197+ if ! ok {
198+ return nil , fmt .Errorf ("expected a CronJob object but got %T" , newObj )
199+ }
200+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
201+
202+ // Assign the CronJob object to the CronJobCustomValidator struct field
203+ // so that it can be used in the validation logic
204+ v .cronjob = cronjob
133205
134- return nil , r .validateCronJob ()
206+ return nil , v .validateCronJob ()
135207}
136208
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 )
209+ // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type CronJob
210+ func (v * CronJobCustomValidator ) ValidateDelete (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
211+ cronjoblog .Info ("Deletion Validation for CronJob" )
212+ cronjob , ok := obj .(* CronJob )
213+ if ! ok {
214+ return nil , fmt .Errorf ("expected a CronJob object but got %T" , obj )
215+ }
216+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
217+
218+ // Assign the CronJob object to the CronJobCustomValidator struct field
219+ // so that it can be used in the validation logic
220+ v .cronjob = cronjob
140221
141222 // TODO(user): fill in your validation logic upon object deletion.
223+
142224 return nil , nil
143225}
144226
145227/*
146228We validate the name and the spec of the CronJob.
147229*/
148230
149- func (r * CronJob ) validateCronJob () error {
231+ func (v * CronJobCustomValidator ) validateCronJob () error {
150232 var allErrs field.ErrorList
151- if err := r .validateCronJobName (); err != nil {
233+ if err := v .validateCronJobName (); err != nil {
152234 allErrs = append (allErrs , err )
153235 }
154- if err := r .validateCronJobSpec (); err != nil {
236+ if err := v .validateCronJobSpec (); err != nil {
155237 allErrs = append (allErrs , err )
156238 }
157239 if len (allErrs ) == 0 {
@@ -160,7 +242,7 @@ func (r *CronJob) validateCronJob() error {
160242
161243 return apierrors .NewInvalid (
162244 schema.GroupKind {Group : "batch.tutorial.kubebuilder.io" , Kind : "CronJob" },
163- r .Name , allErrs )
245+ v . cronjob . ObjectMeta .Name , allErrs )
164246}
165247
166248/*
@@ -173,11 +255,11 @@ declaring validation by running `controller-gen crd -w`,
173255or [here](/reference/markers/crd-validation.md).
174256*/
175257
176- func (r * CronJob ) validateCronJobSpec () * field.Error {
258+ func (v * CronJobCustomValidator ) validateCronJobSpec () * field.Error {
177259 // The field helpers from the kubernetes API machinery help us return nicely
178260 // structured validation errors.
179261 return validateScheduleFormat (
180- r .Spec .Schedule ,
262+ v . cronjob .Spec .Schedule ,
181263 field .NewPath ("spec" ).Child ("schedule" ))
182264}
183265
@@ -202,15 +284,15 @@ the apimachinery repo, so we can't declaratively validate it using
202284the validation schema.
203285*/
204286
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
287+ func (v * CronJobCustomValidator ) validateCronJobName () * field.Error {
288+ if len (v . cronjob .ObjectMeta .Name ) > validationutils .DNS1035LabelMaxLength - 11 {
289+ // The job name length is 63 characters like all Kubernetes objects
208290 // (which must fit in a DNS subdomain). The cronjob controller appends
209291 // a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
210292 // a job. The job name length limit is 63 characters. Therefore cronjob
211293 // names must have length <= 63-11=52. If we don't validate this here,
212294 // then job creation will fail later.
213- return field .Invalid (field .NewPath ("metadata" ).Child ("name" ), r .Name , "must be no more than 52 characters" )
295+ return field .Invalid (field .NewPath ("metadata" ).Child ("name" ), v . cronjob . ObjectMeta .Name , "must be no more than 52 characters" )
214296 }
215297 return nil
216298}
0 commit comments