@@ -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,8 @@ 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 {}).
49
54
Complete ()
50
55
}
51
56
@@ -65,26 +70,42 @@ A webhook will automatically be served that calls this defaulting.
65
70
The `Default` method is expected to mutate the receiver, setting the defaults.
66
71
*/
67
72
68
- var _ webhook. Defaulter = & CronJob {}
73
+ type CronJobCustomDefaulter struct {}
69
74
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 {}
73
76
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 )
76
83
}
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
79
95
}
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 )
83
98
}
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
87
102
}
103
+ if castedObj .Spec .FailedJobsHistoryLimit == nil {
104
+ castedObj .Spec .FailedJobsHistoryLimit = new (int32 )
105
+ * castedObj .Spec .FailedJobsHistoryLimit = 1
106
+ }
107
+
108
+ return nil
88
109
}
89
110
90
111
/*
@@ -118,40 +139,80 @@ validate anything on deletion.
118
139
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
119
140
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
120
141
121
- var _ webhook. Validator = & CronJob {}
142
+ type CronJobCustomValidator struct {}
122
143
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 {}
126
145
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 )
128
164
}
129
165
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 ())
133
181
134
- return nil , r .validateCronJob ()
182
+ return nil , v .validateCronJob (castedObj )
135
183
}
136
184
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 ())
140
200
141
201
// TODO(user): fill in your validation logic upon object deletion.
202
+
142
203
return nil , nil
143
204
}
144
205
145
206
/*
146
207
We validate the name and the spec of the CronJob.
147
208
*/
148
209
149
- func (r * CronJob ) validateCronJob () error {
210
+ func (v * CronJobCustomValidator ) validateCronJob (castedObj * CronJob ) error {
150
211
var allErrs field.ErrorList
151
- if err := r .validateCronJobName (); err != nil {
212
+ if err := v .validateCronJobName (castedObj ); err != nil {
152
213
allErrs = append (allErrs , err )
153
214
}
154
- if err := r .validateCronJobSpec (); err != nil {
215
+ if err := v .validateCronJobSpec (castedObj ); err != nil {
155
216
allErrs = append (allErrs , err )
156
217
}
157
218
if len (allErrs ) == 0 {
@@ -160,7 +221,7 @@ func (r *CronJob) validateCronJob() error {
160
221
161
222
return apierrors .NewInvalid (
162
223
schema.GroupKind {Group : "batch.tutorial.kubebuilder.io" , Kind : "CronJob" },
163
- r .Name , allErrs )
224
+ castedObj .Name , allErrs )
164
225
}
165
226
166
227
/*
@@ -173,11 +234,11 @@ declaring validation by running `controller-gen crd -w`,
173
234
or [here](/reference/markers/crd-validation.md).
174
235
*/
175
236
176
- func (r * CronJob ) validateCronJobSpec () * field.Error {
237
+ func (v * CronJobCustomValidator ) validateCronJobSpec (castedObj * CronJob ) * field.Error {
177
238
// The field helpers from the kubernetes API machinery help us return nicely
178
239
// structured validation errors.
179
240
return validateScheduleFormat (
180
- r .Spec .Schedule ,
241
+ castedObj .Spec .Schedule ,
181
242
field .NewPath ("spec" ).Child ("schedule" ))
182
243
}
183
244
@@ -202,15 +263,15 @@ the apimachinery repo, so we can't declaratively validate it using
202
263
the validation schema.
203
264
*/
204
265
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
208
269
// (which must fit in a DNS subdomain). The cronjob controller appends
209
270
// a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
210
271
// a job. The job name length limit is 63 characters. Therefore cronjob
211
272
// names must have length <= 63-11=52. If we don't validate this here,
212
273
// 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" )
214
275
}
215
276
return nil
216
277
}
0 commit comments