@@ -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,32 +69,61 @@ 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
73
108
74
- if r .Spec .ConcurrencyPolicy == "" {
75
- r .Spec .ConcurrencyPolicy = AllowConcurrent
109
+ // Set default values using the values from the struct
110
+ if cronjob .Spec .ConcurrencyPolicy == "" {
111
+ cronjob .Spec .ConcurrencyPolicy = d .DefaultConcurrencyPolicy
76
112
}
77
- if r .Spec .Suspend == nil {
78
- r .Spec .Suspend = new (bool )
113
+ if cronjob .Spec .Suspend == nil {
114
+ cronjob .Spec .Suspend = new (bool )
115
+ * cronjob .Spec .Suspend = d .DefaultSuspend
79
116
}
80
- if r .Spec .SuccessfulJobsHistoryLimit == nil {
81
- r .Spec .SuccessfulJobsHistoryLimit = new (int32 )
82
- * r .Spec .SuccessfulJobsHistoryLimit = 3
117
+ if cronjob .Spec .SuccessfulJobsHistoryLimit == nil {
118
+ cronjob .Spec .SuccessfulJobsHistoryLimit = new (int32 )
119
+ * cronjob .Spec .SuccessfulJobsHistoryLimit = d . DefaultSuccessfulJobsHistoryLimit
83
120
}
84
- if r .Spec .FailedJobsHistoryLimit == nil {
85
- r .Spec .FailedJobsHistoryLimit = new (int32 )
86
- * r .Spec .FailedJobsHistoryLimit = 1
121
+ if cronjob .Spec .FailedJobsHistoryLimit == nil {
122
+ cronjob .Spec .FailedJobsHistoryLimit = new (int32 )
123
+ * cronjob .Spec .FailedJobsHistoryLimit = d . DefaultFailedJobsHistoryLimit
87
124
}
125
+
126
+ return nil
88
127
}
89
128
90
129
/*
@@ -101,7 +140,7 @@ sometimes more advanced use cases call for complex validation.
101
140
For instance, we'll see below that we use this to validate a well-formed cron
102
141
schedule without making up a long regular expression.
103
142
104
- If `webhook.Validator ` interface is implemented, a webhook will automatically be
143
+ If `webhook.CustomValidator ` interface is implemented, a webhook will automatically be
105
144
served that calls the validation.
106
145
107
146
The `ValidateCreate`, `ValidateUpdate` and `ValidateDelete` methods are expected
@@ -118,40 +157,80 @@ validate anything on deletion.
118
157
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
119
158
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
120
159
121
- var _ webhook.Validator = & CronJob {}
160
+ // +kubebuilder:object:generate=false
161
+ // CronJobCustomValidator struct is responsible for validating the CronJob resource
162
+ // when it is created, updated, or deleted.
163
+ //
164
+ // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
165
+ // as this struct is used only for temporary operations and does not need to be deeply copied.
166
+ type CronJobCustomValidator struct {
167
+ cronjob * CronJob
122
168
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 )
169
+ //TODO(user): Add more fields as needed for validation
170
+ }
126
171
127
- return nil , r .validateCronJob ()
172
+ var _ webhook.CustomValidator = & CronJobCustomValidator {}
173
+
174
+ // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type CronJob
175
+ func (v * CronJobCustomValidator ) ValidateCreate (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
176
+ cronjoblog .Info ("Creation Validation for CronJob" )
177
+ cronjob , ok := obj .(* CronJob )
178
+ if ! ok {
179
+ return nil , fmt .Errorf ("expected a CronJob object but got %T" , obj )
180
+ }
181
+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
182
+
183
+ // Assign the CronJob object to the CronJobCustomValidator struct field
184
+ // so that it can be used in the validation logic
185
+ v .cronjob = cronjob
186
+
187
+ return nil , v .validateCronJob ()
128
188
}
129
189
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 )
190
+ // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type CronJob
191
+ func (v * CronJobCustomValidator ) ValidateUpdate (ctx context.Context , oldObj , newObj runtime.Object ) (admission.Warnings , error ) {
192
+ cronjoblog .Info ("Update Validation for CronJob" )
193
+ cronjob , ok := newObj .(* CronJob )
194
+ if ! ok {
195
+ return nil , fmt .Errorf ("expected a CronJob object but got %T" , newObj )
196
+ }
197
+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
133
198
134
- return nil , r .validateCronJob ()
199
+ // Assign the CronJob object to the CronJobCustomValidator struct field
200
+ // so that it can be used in the validation logic
201
+ v .cronjob = cronjob
202
+
203
+ return nil , v .validateCronJob ()
135
204
}
136
205
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 )
206
+ // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type CronJob
207
+ func (v * CronJobCustomValidator ) ValidateDelete (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
208
+ cronjoblog .Info ("Deletion Validation for CronJob" )
209
+ cronjob , ok := obj .(* CronJob )
210
+ if ! ok {
211
+ return nil , fmt .Errorf ("expected a CronJob object but got %T" , obj )
212
+ }
213
+ cronjoblog .Info ("default" , "name" , cronjob .GetName ())
214
+
215
+ // Assign the CronJob object to the CronJobCustomValidator struct field
216
+ // so that it can be used in the validation logic
217
+ v .cronjob = cronjob
140
218
141
219
// TODO(user): fill in your validation logic upon object deletion.
220
+
142
221
return nil , nil
143
222
}
144
223
145
224
/*
146
225
We validate the name and the spec of the CronJob.
147
226
*/
148
227
149
- func (r * CronJob ) validateCronJob () error {
228
+ func (v * CronJobCustomValidator ) validateCronJob () error {
150
229
var allErrs field.ErrorList
151
- if err := r .validateCronJobName (); err != nil {
230
+ if err := v .validateCronJobName (); err != nil {
152
231
allErrs = append (allErrs , err )
153
232
}
154
- if err := r .validateCronJobSpec (); err != nil {
233
+ if err := v .validateCronJobSpec (); err != nil {
155
234
allErrs = append (allErrs , err )
156
235
}
157
236
if len (allErrs ) == 0 {
@@ -160,7 +239,7 @@ func (r *CronJob) validateCronJob() error {
160
239
161
240
return apierrors .NewInvalid (
162
241
schema.GroupKind {Group : "batch.tutorial.kubebuilder.io" , Kind : "CronJob" },
163
- r .Name , allErrs )
242
+ v . cronjob . ObjectMeta .Name , allErrs )
164
243
}
165
244
166
245
/*
@@ -173,11 +252,11 @@ declaring validation by running `controller-gen crd -w`,
173
252
or [here](/reference/markers/crd-validation.md).
174
253
*/
175
254
176
- func (r * CronJob ) validateCronJobSpec () * field.Error {
255
+ func (v * CronJobCustomValidator ) validateCronJobSpec () * field.Error {
177
256
// The field helpers from the kubernetes API machinery help us return nicely
178
257
// structured validation errors.
179
258
return validateScheduleFormat (
180
- r .Spec .Schedule ,
259
+ v . cronjob .Spec .Schedule ,
181
260
field .NewPath ("spec" ).Child ("schedule" ))
182
261
}
183
262
@@ -202,15 +281,15 @@ the apimachinery repo, so we can't declaratively validate it using
202
281
the validation schema.
203
282
*/
204
283
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
284
+ func (v * CronJobCustomValidator ) validateCronJobName () * field.Error {
285
+ if len (v . cronjob .ObjectMeta .Name ) > validationutils .DNS1035LabelMaxLength - 11 {
286
+ // The job name length is 63 characters like all Kubernetes objects
208
287
// (which must fit in a DNS subdomain). The cronjob controller appends
209
288
// a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
210
289
// a job. The job name length limit is 63 characters. Therefore cronjob
211
290
// names must have length <= 63-11=52. If we don't validate this here,
212
291
// then job creation will fail later.
213
- return field .Invalid (field .NewPath ("metadata" ).Child ("name" ), r .Name , "must be no more than 52 characters" )
292
+ return field .Invalid (field .NewPath ("metadata" ).Child ("name" ), v . cronjob . ObjectMeta .Name , "must be no more than 52 characters" )
214
293
}
215
294
return nil
216
295
}
0 commit comments