1
16
17 package validation
18
19 import (
20 "fmt"
21 "strconv"
22
23 apiequality "k8s.io/apimachinery/pkg/api/equality"
24 apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
27 "k8s.io/apimachinery/pkg/labels"
28 "k8s.io/apimachinery/pkg/util/intstr"
29 "k8s.io/apimachinery/pkg/util/validation"
30 "k8s.io/apimachinery/pkg/util/validation/field"
31 "k8s.io/kubernetes/pkg/apis/apps"
32 api "k8s.io/kubernetes/pkg/apis/core"
33 apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
34 )
35
36
37
38
39 func ValidateStatefulSetName(name string, prefix bool) []string {
40
41
42
43 return apimachineryvalidation.NameIsDNSLabel(name, prefix)
44 }
45
46
47 func ValidatePodTemplateSpecForStatefulSet(template *api.PodTemplateSpec, selector labels.Selector, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
48 allErrs := field.ErrorList{}
49 if template == nil {
50 allErrs = append(allErrs, field.Required(fldPath, ""))
51 } else {
52 if !selector.Empty() {
53
54 labels := labels.Set(template.Labels)
55 if !selector.Matches(labels) {
56 allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
57 }
58 }
59
60
61
62
63 allErrs = append(allErrs, unversionedvalidation.ValidateLabels(template.Labels, fldPath.Child("labels"))...)
64 allErrs = append(allErrs, apivalidation.ValidateAnnotations(template.Annotations, fldPath.Child("annotations"))...)
65 allErrs = append(allErrs, apivalidation.ValidatePodSpecificAnnotations(template.Annotations, &template.Spec, fldPath.Child("annotations"), opts)...)
66 }
67 return allErrs
68 }
69
70 func ValidatePersistentVolumeClaimRetentionPolicyType(policy apps.PersistentVolumeClaimRetentionPolicyType, fldPath *field.Path) field.ErrorList {
71 var allErrs field.ErrorList
72 switch policy {
73 case apps.RetainPersistentVolumeClaimRetentionPolicyType:
74 case apps.DeletePersistentVolumeClaimRetentionPolicyType:
75 default:
76 allErrs = append(allErrs, field.NotSupported(fldPath, policy, []string{string(apps.RetainPersistentVolumeClaimRetentionPolicyType), string(apps.DeletePersistentVolumeClaimRetentionPolicyType)}))
77 }
78 return allErrs
79 }
80
81 func ValidatePersistentVolumeClaimRetentionPolicy(policy *apps.StatefulSetPersistentVolumeClaimRetentionPolicy, fldPath *field.Path) field.ErrorList {
82 var allErrs field.ErrorList
83 if policy != nil {
84 allErrs = append(allErrs, ValidatePersistentVolumeClaimRetentionPolicyType(policy.WhenDeleted, fldPath.Child("whenDeleted"))...)
85 allErrs = append(allErrs, ValidatePersistentVolumeClaimRetentionPolicyType(policy.WhenScaled, fldPath.Child("whenScaled"))...)
86 }
87 return allErrs
88 }
89
90
91 func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
92 allErrs := field.ErrorList{}
93
94 switch spec.PodManagementPolicy {
95 case "":
96 allErrs = append(allErrs, field.Required(fldPath.Child("podManagementPolicy"), ""))
97 case apps.OrderedReadyPodManagement, apps.ParallelPodManagement:
98 default:
99 allErrs = append(allErrs, field.Invalid(fldPath.Child("podManagementPolicy"), spec.PodManagementPolicy, fmt.Sprintf("must be '%s' or '%s'", apps.OrderedReadyPodManagement, apps.ParallelPodManagement)))
100 }
101
102 switch spec.UpdateStrategy.Type {
103 case "":
104 allErrs = append(allErrs, field.Required(fldPath.Child("updateStrategy"), ""))
105 case apps.OnDeleteStatefulSetStrategyType:
106 if spec.UpdateStrategy.RollingUpdate != nil {
107 allErrs = append(
108 allErrs,
109 field.Invalid(
110 fldPath.Child("updateStrategy").Child("rollingUpdate"),
111 spec.UpdateStrategy.RollingUpdate,
112 fmt.Sprintf("only allowed for updateStrategy '%s'", apps.RollingUpdateStatefulSetStrategyType)))
113 }
114 case apps.RollingUpdateStatefulSetStrategyType:
115 if spec.UpdateStrategy.RollingUpdate != nil {
116 allErrs = append(allErrs, validateRollingUpdateStatefulSet(spec.UpdateStrategy.RollingUpdate, fldPath.Child("updateStrategy", "rollingUpdate"))...)
117
118 }
119 default:
120 allErrs = append(allErrs,
121 field.Invalid(fldPath.Child("updateStrategy"), spec.UpdateStrategy,
122 fmt.Sprintf("must be '%s' or '%s'",
123 apps.RollingUpdateStatefulSetStrategyType,
124 apps.OnDeleteStatefulSetStrategyType)))
125 }
126
127 allErrs = append(allErrs, ValidatePersistentVolumeClaimRetentionPolicy(spec.PersistentVolumeClaimRetentionPolicy, fldPath.Child("persistentVolumeClaimRetentionPolicy"))...)
128
129 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
130 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
131 if spec.Ordinals != nil {
132 replicaStartOrdinal := spec.Ordinals.Start
133 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(replicaStartOrdinal), fldPath.Child("ordinals.start"))...)
134 }
135
136 if spec.Selector == nil {
137 allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
138 } else {
139
140 allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
141 if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
142 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for statefulset"))
143 }
144 }
145
146 selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
147 if err != nil {
148 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, ""))
149 } else {
150 allErrs = append(allErrs, ValidatePodTemplateSpecForStatefulSet(&spec.Template, selector, fldPath.Child("template"), opts)...)
151 }
152
153 if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
154 allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
155 }
156 if spec.Template.Spec.ActiveDeadlineSeconds != nil {
157 allErrs = append(allErrs, field.Forbidden(fldPath.Child("template", "spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in StatefulSet is not Supported"))
158 }
159
160 return allErrs
161 }
162
163
164 func ValidateStatefulSet(statefulSet *apps.StatefulSet, opts apivalidation.PodValidationOptions) field.ErrorList {
165 allErrs := apivalidation.ValidateObjectMeta(&statefulSet.ObjectMeta, true, ValidateStatefulSetName, field.NewPath("metadata"))
166 allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...)
167 return allErrs
168 }
169
170
171 func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet, opts apivalidation.PodValidationOptions) field.ErrorList {
172
173
174
175
176
177
178 allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))
179 allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...)
180
181
182
183 newStatefulSetClone := statefulSet.DeepCopy()
184 newStatefulSetClone.Spec.Replicas = oldStatefulSet.Spec.Replicas
185 newStatefulSetClone.Spec.Template = oldStatefulSet.Spec.Template
186 newStatefulSetClone.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy
187 newStatefulSetClone.Spec.MinReadySeconds = oldStatefulSet.Spec.MinReadySeconds
188 newStatefulSetClone.Spec.Ordinals = oldStatefulSet.Spec.Ordinals
189
190 newStatefulSetClone.Spec.PersistentVolumeClaimRetentionPolicy = oldStatefulSet.Spec.PersistentVolumeClaimRetentionPolicy
191 if !apiequality.Semantic.DeepEqual(newStatefulSetClone.Spec, oldStatefulSet.Spec) {
192 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden"))
193 }
194
195 return allErrs
196 }
197
198
199 func ValidateStatefulSetStatus(status *apps.StatefulSetStatus, fieldPath *field.Path) field.ErrorList {
200 allErrs := field.ErrorList{}
201
202 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fieldPath.Child("replicas"))...)
203 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fieldPath.Child("readyReplicas"))...)
204 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentReplicas), fieldPath.Child("currentReplicas"))...)
205 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fieldPath.Child("updatedReplicas"))...)
206 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fieldPath.Child("availableReplicas"))...)
207 if status.ObservedGeneration != nil {
208 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.ObservedGeneration), fieldPath.Child("observedGeneration"))...)
209 }
210 if status.CollisionCount != nil {
211 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fieldPath.Child("collisionCount"))...)
212 }
213
214 msg := "cannot be greater than status.replicas"
215 if status.ReadyReplicas > status.Replicas {
216 allErrs = append(allErrs, field.Invalid(fieldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
217 }
218 if status.CurrentReplicas > status.Replicas {
219 allErrs = append(allErrs, field.Invalid(fieldPath.Child("currentReplicas"), status.CurrentReplicas, msg))
220 }
221 if status.UpdatedReplicas > status.Replicas {
222 allErrs = append(allErrs, field.Invalid(fieldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
223 }
224 if status.AvailableReplicas > status.Replicas {
225 allErrs = append(allErrs, field.Invalid(fieldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
226 }
227 if status.AvailableReplicas > status.ReadyReplicas {
228 allErrs = append(allErrs, field.Invalid(fieldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than status.readyReplicas"))
229 }
230 return allErrs
231 }
232
233
234 func ValidateStatefulSetStatusUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList {
235 allErrs := field.ErrorList{}
236 allErrs = append(allErrs, ValidateStatefulSetStatus(&statefulSet.Status, field.NewPath("status"))...)
237 allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))...)
238
239 if apivalidation.IsDecremented(statefulSet.Status.CollisionCount, oldStatefulSet.Status.CollisionCount) {
240 value := int32(0)
241 if statefulSet.Status.CollisionCount != nil {
242 value = *statefulSet.Status.CollisionCount
243 }
244 allErrs = append(allErrs, field.Invalid(field.NewPath("status").Child("collisionCount"), value, "cannot be decremented"))
245 }
246 return allErrs
247 }
248
249
250
251
252 var ValidateControllerRevisionName = apimachineryvalidation.NameIsDNSSubdomain
253
254
255
256
257 func ValidateControllerRevision(revision *apps.ControllerRevision) field.ErrorList {
258 errs := field.ErrorList{}
259
260 errs = append(errs, apivalidation.ValidateObjectMeta(&revision.ObjectMeta, true, ValidateControllerRevisionName, field.NewPath("metadata"))...)
261 if revision.Data == nil {
262 errs = append(errs, field.Required(field.NewPath("data"), "data is mandatory"))
263 }
264 errs = append(errs, apivalidation.ValidateNonnegativeField(revision.Revision, field.NewPath("revision"))...)
265 return errs
266 }
267
268
269
270
271 func ValidateControllerRevisionUpdate(newHistory, oldHistory *apps.ControllerRevision) field.ErrorList {
272 errs := field.ErrorList{}
273
274 errs = append(errs, apivalidation.ValidateObjectMetaUpdate(&newHistory.ObjectMeta, &oldHistory.ObjectMeta, field.NewPath("metadata"))...)
275 errs = append(errs, ValidateControllerRevision(newHistory)...)
276 errs = append(errs, apivalidation.ValidateImmutableField(newHistory.Data, oldHistory.Data, field.NewPath("data"))...)
277 return errs
278 }
279
280
281 func ValidateDaemonSet(ds *apps.DaemonSet, opts apivalidation.PodValidationOptions) field.ErrorList {
282 allErrs := apivalidation.ValidateObjectMeta(&ds.ObjectMeta, true, ValidateDaemonSetName, field.NewPath("metadata"))
283 allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, nil, field.NewPath("spec"), opts)...)
284 return allErrs
285 }
286
287
288 func ValidateDaemonSetUpdate(ds, oldDS *apps.DaemonSet, opts apivalidation.PodValidationOptions) field.ErrorList {
289 allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
290 allErrs = append(allErrs, ValidateDaemonSetSpecUpdate(&ds.Spec, &oldDS.Spec, field.NewPath("spec"))...)
291 allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, &oldDS.Spec, field.NewPath("spec"), opts)...)
292 return allErrs
293 }
294
295
296 func ValidateDaemonSetSpecUpdate(newSpec, oldSpec *apps.DaemonSetSpec, fldPath *field.Path) field.ErrorList {
297 allErrs := field.ErrorList{}
298
299
300 if newSpec.TemplateGeneration < oldSpec.TemplateGeneration {
301 allErrs = append(allErrs, field.Invalid(fldPath.Child("templateGeneration"), newSpec.TemplateGeneration, "must not be decremented"))
302 }
303
304
305 templateUpdated := !apiequality.Semantic.DeepEqual(newSpec.Template, oldSpec.Template)
306 if newSpec.TemplateGeneration == oldSpec.TemplateGeneration && templateUpdated {
307 allErrs = append(allErrs, field.Invalid(fldPath.Child("templateGeneration"), newSpec.TemplateGeneration, "must be incremented upon template update"))
308 } else if newSpec.TemplateGeneration > oldSpec.TemplateGeneration && !templateUpdated {
309 allErrs = append(allErrs, field.Invalid(fldPath.Child("templateGeneration"), newSpec.TemplateGeneration, "must not be incremented without template update"))
310 }
311
312 return allErrs
313 }
314
315
316 func validateDaemonSetStatus(status *apps.DaemonSetStatus, fldPath *field.Path) field.ErrorList {
317 allErrs := field.ErrorList{}
318 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentNumberScheduled), fldPath.Child("currentNumberScheduled"))...)
319 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberMisscheduled), fldPath.Child("numberMisscheduled"))...)
320 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredNumberScheduled), fldPath.Child("desiredNumberScheduled"))...)
321 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberReady), fldPath.Child("numberReady"))...)
322 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(status.ObservedGeneration, fldPath.Child("observedGeneration"))...)
323 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedNumberScheduled), fldPath.Child("updatedNumberScheduled"))...)
324 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberAvailable), fldPath.Child("numberAvailable"))...)
325 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberUnavailable), fldPath.Child("numberUnavailable"))...)
326 if status.CollisionCount != nil {
327 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...)
328 }
329 return allErrs
330 }
331
332
333 func ValidateDaemonSetStatusUpdate(ds, oldDS *apps.DaemonSet) field.ErrorList {
334 allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
335 allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...)
336 if apivalidation.IsDecremented(ds.Status.CollisionCount, oldDS.Status.CollisionCount) {
337 value := int32(0)
338 if ds.Status.CollisionCount != nil {
339 value = *ds.Status.CollisionCount
340 }
341 allErrs = append(allErrs, field.Invalid(field.NewPath("status").Child("collisionCount"), value, "cannot be decremented"))
342 }
343 return allErrs
344 }
345
346
347 func ValidateDaemonSetSpec(spec, oldSpec *apps.DaemonSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
348 allErrs := field.ErrorList{}
349 labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector}
350
351 allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, labelSelectorValidationOpts, fldPath.Child("selector"))...)
352
353 selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
354 if err == nil && !selector.Matches(labels.Set(spec.Template.Labels)) {
355 allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`"))
356 }
357 if spec.Selector != nil && len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
358 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for daemonset"))
359 }
360
361 allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"), opts)...)
362
363 var oldVols []api.Volume
364 if oldSpec != nil {
365 oldVols = oldSpec.Template.Spec.Volumes
366 }
367 allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, oldVols, fldPath.Child("template", "spec", "volumes"))...)
368
369 if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
370 allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
371 }
372 if spec.Template.Spec.ActiveDeadlineSeconds != nil {
373 allErrs = append(allErrs, field.Forbidden(fldPath.Child("template", "spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in DaemonSet is not Supported"))
374 }
375 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
376 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.TemplateGeneration), fldPath.Child("templateGeneration"))...)
377
378 allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...)
379 if spec.RevisionHistoryLimit != nil {
380
381 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
382 }
383 return allErrs
384 }
385
386
387 func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
388 var allErrs field.ErrorList
389
390
391 allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
392 allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
393
394
395 allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
396 allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
397
398
399 hasUnavailable := getIntOrPercentValue(rollingUpdate.MaxUnavailable) != 0
400 hasSurge := getIntOrPercentValue(rollingUpdate.MaxSurge) != 0
401 switch {
402 case hasUnavailable && hasSurge:
403 allErrs = append(allErrs, field.Invalid(fldPath.Child("maxSurge"), rollingUpdate.MaxSurge, "may not be set when maxUnavailable is non-zero"))
404 case !hasUnavailable && !hasSurge:
405 allErrs = append(allErrs, field.Required(fldPath.Child("maxUnavailable"), "cannot be 0 when maxSurge is 0"))
406 }
407
408 return allErrs
409 }
410
411
412 func validateRollingUpdateStatefulSet(rollingUpdate *apps.RollingUpdateStatefulSetStrategy, fldPath *field.Path) field.ErrorList {
413 var allErrs field.ErrorList
414 fldPathMaxUn := fldPath.Child("maxUnavailable")
415 allErrs = append(allErrs,
416 apivalidation.ValidateNonnegativeField(
417 int64(rollingUpdate.Partition),
418 fldPath.Child("partition"))...)
419 if rollingUpdate.MaxUnavailable != nil {
420 allErrs = append(allErrs, ValidatePositiveIntOrPercent(*rollingUpdate.MaxUnavailable, fldPathMaxUn)...)
421 if getIntOrPercentValue(*rollingUpdate.MaxUnavailable) == 0 {
422
423 allErrs = append(allErrs, field.Invalid(fldPathMaxUn, *rollingUpdate.MaxUnavailable, "cannot be 0"))
424 }
425
426 allErrs = append(allErrs, IsNotMoreThan100Percent(*rollingUpdate.MaxUnavailable, fldPathMaxUn)...)
427 }
428 return allErrs
429 }
430
431
432 func ValidateDaemonSetUpdateStrategy(strategy *apps.DaemonSetUpdateStrategy, fldPath *field.Path) field.ErrorList {
433 allErrs := field.ErrorList{}
434 switch strategy.Type {
435 case apps.OnDeleteDaemonSetStrategyType:
436 case apps.RollingUpdateDaemonSetStrategyType:
437
438 if strategy.RollingUpdate == nil {
439 allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), ""))
440 return allErrs
441 }
442 allErrs = append(allErrs, ValidateRollingUpdateDaemonSet(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
443 default:
444 validValues := []string{string(apps.RollingUpdateDaemonSetStrategyType), string(apps.OnDeleteDaemonSetStrategyType)}
445 allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
446 }
447 return allErrs
448 }
449
450
451
452
453 var ValidateDaemonSetName = apimachineryvalidation.NameIsDNSSubdomain
454
455
456 var ValidateDeploymentName = apimachineryvalidation.NameIsDNSSubdomain
457
458
459
460 func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList {
461 allErrs := field.ErrorList{}
462 switch intOrPercent.Type {
463 case intstr.String:
464 for _, msg := range validation.IsValidPercent(intOrPercent.StrVal) {
465 allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, msg))
466 }
467 case intstr.Int:
468 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...)
469 default:
470 allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%%')"))
471 }
472 return allErrs
473 }
474
475 func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) {
476 if intOrStringValue.Type != intstr.String {
477 return 0, false
478 }
479 if len(validation.IsValidPercent(intOrStringValue.StrVal)) != 0 {
480 return 0, false
481 }
482 value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1])
483 return value, true
484 }
485
486 func getIntOrPercentValue(intOrStringValue intstr.IntOrString) int {
487 value, isPercent := getPercentValue(intOrStringValue)
488 if isPercent {
489 return value
490 }
491 return intOrStringValue.IntValue()
492 }
493
494
495
496 func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList {
497 allErrs := field.ErrorList{}
498 value, isPercent := getPercentValue(intOrStringValue)
499 if !isPercent || value <= 100 {
500 return nil
501 }
502 allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be greater than 100%"))
503 return allErrs
504 }
505
506
507 func ValidateRollingUpdateDeployment(rollingUpdate *apps.RollingUpdateDeployment, fldPath *field.Path) field.ErrorList {
508 allErrs := field.ErrorList{}
509 allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
510 allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
511 if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 && getIntOrPercentValue(rollingUpdate.MaxSurge) == 0 {
512
513 allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "may not be 0 when `maxSurge` is 0"))
514 }
515
516 allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
517 return allErrs
518 }
519
520
521 func ValidateDeploymentStrategy(strategy *apps.DeploymentStrategy, fldPath *field.Path) field.ErrorList {
522 allErrs := field.ErrorList{}
523 switch strategy.Type {
524 case apps.RecreateDeploymentStrategyType:
525 if strategy.RollingUpdate != nil {
526 allErrs = append(allErrs, field.Forbidden(fldPath.Child("rollingUpdate"), "may not be specified when strategy `type` is '"+string(apps.RecreateDeploymentStrategyType+"'")))
527 }
528 case apps.RollingUpdateDeploymentStrategyType:
529
530 if strategy.RollingUpdate == nil {
531 allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), "this should be defaulted and never be nil"))
532 } else {
533 allErrs = append(allErrs, ValidateRollingUpdateDeployment(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
534 }
535 default:
536 validValues := []string{string(apps.RecreateDeploymentStrategyType), string(apps.RollingUpdateDeploymentStrategyType)}
537 allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
538 }
539 return allErrs
540 }
541
542
543 func ValidateRollback(rollback *apps.RollbackConfig, fldPath *field.Path) field.ErrorList {
544 allErrs := field.ErrorList{}
545 v := rollback.Revision
546 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(v), fldPath.Child("version"))...)
547 return allErrs
548 }
549
550
551 func ValidateDeploymentSpec(spec, oldSpec *apps.DeploymentSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
552 allErrs := field.ErrorList{}
553 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
554
555 if spec.Selector == nil {
556 allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
557 } else {
558
559 allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
560 if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
561 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for deployment"))
562 }
563 }
564
565 selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
566 if err != nil {
567 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
568 } else {
569
570 var oldTemplate *api.PodTemplateSpec
571 if oldSpec != nil {
572 oldTemplate = &oldSpec.Template
573 }
574 allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, oldTemplate, selector, spec.Replicas, fldPath.Child("template"), opts)...)
575 }
576
577 allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
578 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
579 if spec.RevisionHistoryLimit != nil {
580
581 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
582 }
583 if spec.RollbackTo != nil {
584 allErrs = append(allErrs, ValidateRollback(spec.RollbackTo, fldPath.Child("rollback"))...)
585 }
586 if spec.ProgressDeadlineSeconds != nil {
587 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ProgressDeadlineSeconds), fldPath.Child("progressDeadlineSeconds"))...)
588 if *spec.ProgressDeadlineSeconds <= spec.MinReadySeconds {
589 allErrs = append(allErrs, field.Invalid(fldPath.Child("progressDeadlineSeconds"), spec.ProgressDeadlineSeconds, "must be greater than minReadySeconds"))
590 }
591 }
592 return allErrs
593 }
594
595
596 func ValidateDeploymentStatus(status *apps.DeploymentStatus, fldPath *field.Path) field.ErrorList {
597 allErrs := field.ErrorList{}
598 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(status.ObservedGeneration, fldPath.Child("observedGeneration"))...)
599 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
600 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fldPath.Child("updatedReplicas"))...)
601 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
602 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
603 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UnavailableReplicas), fldPath.Child("unavailableReplicas"))...)
604 if status.CollisionCount != nil {
605 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...)
606 }
607 msg := "cannot be greater than status.replicas"
608 if status.UpdatedReplicas > status.Replicas {
609 allErrs = append(allErrs, field.Invalid(fldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
610 }
611 if status.ReadyReplicas > status.Replicas {
612 allErrs = append(allErrs, field.Invalid(fldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
613 }
614 if status.AvailableReplicas > status.Replicas {
615 allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
616 }
617 if status.AvailableReplicas > status.ReadyReplicas {
618 allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
619 }
620 return allErrs
621 }
622
623
624 func ValidateDeploymentUpdate(update, old *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
625 allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
626 allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, &old.Spec, field.NewPath("spec"), opts)...)
627 return allErrs
628 }
629
630
631
632 func ValidateDeploymentStatusUpdate(update, old *apps.Deployment) field.ErrorList {
633 allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
634 fldPath := field.NewPath("status")
635 allErrs = append(allErrs, ValidateDeploymentStatus(&update.Status, fldPath)...)
636 if apivalidation.IsDecremented(update.Status.CollisionCount, old.Status.CollisionCount) {
637 value := int32(0)
638 if update.Status.CollisionCount != nil {
639 value = *update.Status.CollisionCount
640 }
641 allErrs = append(allErrs, field.Invalid(fldPath.Child("collisionCount"), value, "cannot be decremented"))
642 }
643 return allErrs
644 }
645
646
647 func ValidateDeployment(obj *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
648 allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
649 allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, nil, field.NewPath("spec"), opts)...)
650 return allErrs
651 }
652
653
654 func ValidateDeploymentRollback(obj *apps.DeploymentRollback) field.ErrorList {
655 allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations"))
656 if len(obj.Name) == 0 {
657 allErrs = append(allErrs, field.Required(field.NewPath("name"), "name is required"))
658 }
659 allErrs = append(allErrs, ValidateRollback(&obj.RollbackTo, field.NewPath("rollback"))...)
660 return allErrs
661 }
662
663
664
665
666
667 var ValidateReplicaSetName = apimachineryvalidation.NameIsDNSSubdomain
668
669
670 func ValidateReplicaSet(rs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
671 allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata"))
672 allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, nil, field.NewPath("spec"), opts)...)
673 return allErrs
674 }
675
676
677 func ValidateReplicaSetUpdate(rs, oldRs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
678 allErrs := field.ErrorList{}
679 allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
680 allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, &oldRs.Spec, field.NewPath("spec"), opts)...)
681 return allErrs
682 }
683
684
685 func ValidateReplicaSetStatusUpdate(rs, oldRs *apps.ReplicaSet) field.ErrorList {
686 allErrs := field.ErrorList{}
687 allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
688 allErrs = append(allErrs, ValidateReplicaSetStatus(rs.Status, field.NewPath("status"))...)
689 return allErrs
690 }
691
692
693 func ValidateReplicaSetStatus(status apps.ReplicaSetStatus, fldPath *field.Path) field.ErrorList {
694 allErrs := field.ErrorList{}
695 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
696 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.FullyLabeledReplicas), fldPath.Child("fullyLabeledReplicas"))...)
697 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
698 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
699 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ObservedGeneration), fldPath.Child("observedGeneration"))...)
700 msg := "cannot be greater than status.replicas"
701 if status.FullyLabeledReplicas > status.Replicas {
702 allErrs = append(allErrs, field.Invalid(fldPath.Child("fullyLabeledReplicas"), status.FullyLabeledReplicas, msg))
703 }
704 if status.ReadyReplicas > status.Replicas {
705 allErrs = append(allErrs, field.Invalid(fldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
706 }
707 if status.AvailableReplicas > status.Replicas {
708 allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
709 }
710 if status.AvailableReplicas > status.ReadyReplicas {
711 allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
712 }
713 return allErrs
714 }
715
716
717 func ValidateReplicaSetSpec(spec, oldSpec *apps.ReplicaSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
718 allErrs := field.ErrorList{}
719
720 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
721 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
722
723 if spec.Selector == nil {
724 allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
725 } else {
726
727 allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
728 if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
729 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for deployment"))
730 }
731 }
732
733 selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
734 if err != nil {
735 allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
736 } else {
737
738 var oldTemplate *api.PodTemplateSpec
739 if oldSpec != nil {
740 oldTemplate = &oldSpec.Template
741 }
742 allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, oldTemplate, selector, spec.Replicas, fldPath.Child("template"), opts)...)
743 }
744 return allErrs
745 }
746
747
748 func ValidatePodTemplateSpecForReplicaSet(template, oldTemplate *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
749 allErrs := field.ErrorList{}
750 if template == nil {
751 allErrs = append(allErrs, field.Required(fldPath, ""))
752 } else {
753 if !selector.Empty() {
754
755 labels := labels.Set(template.Labels)
756 if !selector.Matches(labels) {
757 allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
758 }
759 }
760 allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath, opts)...)
761
762
763 var oldVols []api.Volume
764 if oldTemplate != nil {
765 oldVols = oldTemplate.Spec.Volumes
766 }
767 if replicas > 1 {
768 allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, oldVols, fldPath.Child("spec", "volumes"))...)
769 }
770
771 if template.Spec.RestartPolicy != api.RestartPolicyAlways {
772 allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
773 }
774 if template.Spec.ActiveDeadlineSeconds != nil {
775 allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in ReplicaSet is not Supported"))
776 }
777 }
778 return allErrs
779 }
780
View as plain text