@@ -18,12 +18,15 @@ limitations under the License.
18
18
package v1
19
19
20
20
import (
21
+ "context"
22
+ "fmt"
21
23
"github.com/robfig/cron"
22
24
apierrors "k8s.io/apimachinery/pkg/api/errors"
23
- "k8s.io/apimachinery/pkg/runtime"
24
25
"k8s.io/apimachinery/pkg/runtime/schema"
25
26
validationutils "k8s.io/apimachinery/pkg/util/validation"
26
27
"k8s.io/apimachinery/pkg/util/validation/field"
28
+
29
+ "k8s.io/apimachinery/pkg/runtime"
27
30
ctrl "sigs.k8s.io/controller-runtime"
28
31
logf "sigs.k8s.io/controller-runtime/pkg/log"
29
32
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -46,6 +49,13 @@ Then, we set up the webhook with the manager.
46
49
func (r * CronJob ) SetupWebhookWithManager (mgr ctrl.Manager ) error {
47
50
return ctrl .NewWebhookManagedBy (mgr ).
48
51
For (r ).
52
+ WithValidator (& CronJobCustomValidator {}).
53
+ WithDefaulter (& CronJobCustomDefaulter {
54
+ DefaultConcurrencyPolicy : AllowConcurrent ,
55
+ DefaultSuspend : false ,
56
+ DefaultSuccessfulJobsHistoryLimit : 3 ,
57
+ DefaultFailedJobsHistoryLimit : 1 ,
58
+ }).
49
59
Complete ()
50
60
}
51
61
@@ -59,31 +69,63 @@ The meaning of each marker can be found [here](/reference/markers/webhook.md).
59
69
// +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
60
70
61
71
/*
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.
63
73
A webhook will automatically be served that calls this defaulting.
64
74
65
75
The `Default` method is expected to mutate the receiver, setting the defaults.
66
76
*/
67
77
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 {}
69
95
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
+ }
73
113
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
76
117
}
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
79
121
}
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
83
125
}
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
87
129
}
88
130
}
89
131
@@ -101,7 +143,7 @@ sometimes more advanced use cases call for complex validation.
101
143
For instance, we'll see below that we use this to validate a well-formed cron
102
144
schedule without making up a long regular expression.
103
145
104
- If `webhook.Validator ` interface is implemented, a webhook will automatically be
146
+ If `webhook.CustomValidator ` interface is implemented, a webhook will automatically be
105
147
served that calls the validation.
106
148
107
149
The `ValidateCreate`, `ValidateUpdate` and `ValidateDelete` methods are expected
@@ -118,40 +160,80 @@ validate anything on deletion.
118
160
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
119
161
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
120
162
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
122
171
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
126
189
127
- return nil , r .validateCronJob ()
190
+ return nil , v .validateCronJob ()
128
191
}
129
192
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
133
205
134
- return nil , r .validateCronJob ()
206
+ return nil , v .validateCronJob ()
135
207
}
136
208
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
140
221
141
222
// TODO(user): fill in your validation logic upon object deletion.
223
+
142
224
return nil , nil
143
225
}
144
226
145
227
/*
146
228
We validate the name and the spec of the CronJob.
147
229
*/
148
230
149
- func (r * CronJob ) validateCronJob () error {
231
+ func (v * CronJobCustomValidator ) validateCronJob () error {
150
232
var allErrs field.ErrorList
151
- if err := r .validateCronJobName (); err != nil {
233
+ if err := v .validateCronJobName (); err != nil {
152
234
allErrs = append (allErrs , err )
153
235
}
154
- if err := r .validateCronJobSpec (); err != nil {
236
+ if err := v .validateCronJobSpec (); err != nil {
155
237
allErrs = append (allErrs , err )
156
238
}
157
239
if len (allErrs ) == 0 {
@@ -160,7 +242,7 @@ func (r *CronJob) validateCronJob() error {
160
242
161
243
return apierrors .NewInvalid (
162
244
schema.GroupKind {Group : "batch.tutorial.kubebuilder.io" , Kind : "CronJob" },
163
- r .Name , allErrs )
245
+ v . cronjob . ObjectMeta .Name , allErrs )
164
246
}
165
247
166
248
/*
@@ -173,11 +255,11 @@ declaring validation by running `controller-gen crd -w`,
173
255
or [here](/reference/markers/crd-validation.md).
174
256
*/
175
257
176
- func (r * CronJob ) validateCronJobSpec () * field.Error {
258
+ func (v * CronJobCustomValidator ) validateCronJobSpec () * field.Error {
177
259
// The field helpers from the kubernetes API machinery help us return nicely
178
260
// structured validation errors.
179
261
return validateScheduleFormat (
180
- r .Spec .Schedule ,
262
+ v . cronjob .Spec .Schedule ,
181
263
field .NewPath ("spec" ).Child ("schedule" ))
182
264
}
183
265
@@ -202,15 +284,15 @@ the apimachinery repo, so we can't declaratively validate it using
202
284
the validation schema.
203
285
*/
204
286
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
208
290
// (which must fit in a DNS subdomain). The cronjob controller appends
209
291
// a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
210
292
// a job. The job name length limit is 63 characters. Therefore cronjob
211
293
// names must have length <= 63-11=52. If we don't validate this here,
212
294
// 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" )
214
296
}
215
297
return nil
216
298
}
0 commit comments