...
1
16
17 package poddisruptionbudget
18
19 import (
20 "context"
21
22 apiequality "k8s.io/apimachinery/pkg/api/equality"
23 metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
24 "k8s.io/apimachinery/pkg/runtime"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 "k8s.io/apimachinery/pkg/util/validation/field"
27 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
28 "k8s.io/apiserver/pkg/storage/names"
29 utilfeature "k8s.io/apiserver/pkg/util/feature"
30 "k8s.io/kubernetes/pkg/api/legacyscheme"
31 "k8s.io/kubernetes/pkg/apis/policy"
32 "k8s.io/kubernetes/pkg/apis/policy/validation"
33 "k8s.io/kubernetes/pkg/features"
34 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
35 )
36
37
38 type podDisruptionBudgetStrategy struct {
39 runtime.ObjectTyper
40 names.NameGenerator
41 }
42
43
44 var Strategy = podDisruptionBudgetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
45
46
47 func (podDisruptionBudgetStrategy) NamespaceScoped() bool {
48 return true
49 }
50
51
52
53 func (podDisruptionBudgetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
54 fields := map[fieldpath.APIVersion]*fieldpath.Set{
55 "policy/v1beta1": fieldpath.NewSet(
56 fieldpath.MakePathOrDie("status"),
57 ),
58 "policy/v1": fieldpath.NewSet(
59 fieldpath.MakePathOrDie("status"),
60 ),
61 }
62
63 return fields
64 }
65
66
67 func (podDisruptionBudgetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
68 podDisruptionBudget := obj.(*policy.PodDisruptionBudget)
69
70 podDisruptionBudget.Status = policy.PodDisruptionBudgetStatus{}
71
72 podDisruptionBudget.Generation = 1
73
74 dropDisabledFields(&podDisruptionBudget.Spec, nil)
75 }
76
77
78 func (podDisruptionBudgetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
79 newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget)
80 oldPodDisruptionBudget := old.(*policy.PodDisruptionBudget)
81
82 newPodDisruptionBudget.Status = oldPodDisruptionBudget.Status
83
84
85
86
87 if !apiequality.Semantic.DeepEqual(oldPodDisruptionBudget.Spec, newPodDisruptionBudget.Spec) {
88 newPodDisruptionBudget.Generation = oldPodDisruptionBudget.Generation + 1
89 }
90
91 dropDisabledFields(&newPodDisruptionBudget.Spec, &oldPodDisruptionBudget.Spec)
92 }
93
94
95 func (podDisruptionBudgetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
96 podDisruptionBudget := obj.(*policy.PodDisruptionBudget)
97 opts := validation.PodDisruptionBudgetValidationOptions{
98 AllowInvalidLabelValueInSelector: false,
99 }
100 return validation.ValidatePodDisruptionBudget(podDisruptionBudget, opts)
101 }
102
103
104 func (podDisruptionBudgetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
105 return nil
106 }
107
108
109 func (podDisruptionBudgetStrategy) Canonicalize(obj runtime.Object) {
110 }
111
112
113 func (podDisruptionBudgetStrategy) AllowCreateOnUpdate() bool {
114 return false
115 }
116
117
118 func (podDisruptionBudgetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
119 opts := validation.PodDisruptionBudgetValidationOptions{
120 AllowInvalidLabelValueInSelector: hasInvalidLabelValueInLabelSelector(old.(*policy.PodDisruptionBudget)),
121 }
122 return validation.ValidatePodDisruptionBudget(obj.(*policy.PodDisruptionBudget), opts)
123 }
124
125
126 func (podDisruptionBudgetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
127 return nil
128 }
129
130
131
132 func (podDisruptionBudgetStrategy) AllowUnconditionalUpdate() bool {
133 return false
134 }
135
136 type podDisruptionBudgetStatusStrategy struct {
137 podDisruptionBudgetStrategy
138 }
139
140
141 var StatusStrategy = podDisruptionBudgetStatusStrategy{Strategy}
142
143
144
145 func (podDisruptionBudgetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
146 fields := map[fieldpath.APIVersion]*fieldpath.Set{
147 "policy/v1beta1": fieldpath.NewSet(
148 fieldpath.MakePathOrDie("spec"),
149 ),
150 "policy/v1": fieldpath.NewSet(
151 fieldpath.MakePathOrDie("spec"),
152 ),
153 }
154
155 return fields
156 }
157
158
159 func (podDisruptionBudgetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
160 newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget)
161 oldPodDisruptionBudget := old.(*policy.PodDisruptionBudget)
162
163 newPodDisruptionBudget.Spec = oldPodDisruptionBudget.Spec
164 }
165
166
167 func (podDisruptionBudgetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
168 var apiVersion schema.GroupVersion
169 if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
170 apiVersion = schema.GroupVersion{
171 Group: requestInfo.APIGroup,
172 Version: requestInfo.APIVersion,
173 }
174 }
175 return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status,
176 old.(*policy.PodDisruptionBudget).Status, field.NewPath("status"), apiVersion)
177 }
178
179
180 func (podDisruptionBudgetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
181 return nil
182 }
183
184 func hasInvalidLabelValueInLabelSelector(pdb *policy.PodDisruptionBudget) bool {
185 if pdb.Spec.Selector != nil {
186 labelSelectorValidationOptions := metav1validation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}
187 return len(metav1validation.ValidateLabelSelector(pdb.Spec.Selector, labelSelectorValidationOptions, nil)) > 0
188 }
189 return false
190 }
191
192
193
194 func dropDisabledFields(pdbSpec, oldPDBSpec *policy.PodDisruptionBudgetSpec) {
195 if !utilfeature.DefaultFeatureGate.Enabled(features.PDBUnhealthyPodEvictionPolicy) {
196 if !unhealthyPodEvictionPolicyInUse(oldPDBSpec) {
197 pdbSpec.UnhealthyPodEvictionPolicy = nil
198 }
199 }
200 }
201
202 func unhealthyPodEvictionPolicyInUse(oldPDBSpec *policy.PodDisruptionBudgetSpec) bool {
203 if oldPDBSpec == nil {
204 return false
205 }
206 if oldPDBSpec.UnhealthyPodEvictionPolicy != nil {
207 return true
208 }
209 return false
210 }
211
View as plain text