1
16
17 package validation
18
19 import (
20 "fmt"
21 "testing"
22 "time"
23
24 policyv1beta1 "k8s.io/api/policy/v1beta1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "k8s.io/apimachinery/pkg/util/intstr"
28 "k8s.io/apimachinery/pkg/util/validation/field"
29 "k8s.io/kubernetes/pkg/apis/policy"
30 )
31
32 func TestValidatePodDisruptionBudgetSpec(t *testing.T) {
33 minAvailable := intstr.FromString("0%")
34 maxUnavailable := intstr.FromString("10%")
35
36 spec := policy.PodDisruptionBudgetSpec{
37 MinAvailable: &minAvailable,
38 MaxUnavailable: &maxUnavailable,
39 }
40 errs := ValidatePodDisruptionBudgetSpec(spec, PodDisruptionBudgetValidationOptions{true}, field.NewPath("foo"))
41 if len(errs) == 0 {
42 t.Errorf("unexpected success for %v", spec)
43 }
44 }
45
46 func TestValidateMinAvailablePodDisruptionBudgetSpec(t *testing.T) {
47 successCases := []intstr.IntOrString{
48 intstr.FromString("0%"),
49 intstr.FromString("1%"),
50 intstr.FromString("100%"),
51 intstr.FromInt32(0),
52 intstr.FromInt32(1),
53 intstr.FromInt32(100),
54 }
55 for _, c := range successCases {
56 spec := policy.PodDisruptionBudgetSpec{
57 MinAvailable: &c,
58 }
59 errs := ValidatePodDisruptionBudgetSpec(spec, PodDisruptionBudgetValidationOptions{true}, field.NewPath("foo"))
60 if len(errs) != 0 {
61 t.Errorf("unexpected failure %v for %v", errs, spec)
62 }
63 }
64
65 failureCases := []intstr.IntOrString{
66 intstr.FromString("1.1%"),
67 intstr.FromString("nope"),
68 intstr.FromString("-1%"),
69 intstr.FromString("101%"),
70 intstr.FromInt32(-1),
71 }
72 for _, c := range failureCases {
73 spec := policy.PodDisruptionBudgetSpec{
74 MinAvailable: &c,
75 }
76 errs := ValidatePodDisruptionBudgetSpec(spec, PodDisruptionBudgetValidationOptions{true}, field.NewPath("foo"))
77 if len(errs) == 0 {
78 t.Errorf("unexpected success for %v", spec)
79 }
80 }
81 }
82
83 func TestValidateMinAvailablePodAndMaxUnavailableDisruptionBudgetSpec(t *testing.T) {
84 c1 := intstr.FromString("10%")
85 c2 := intstr.FromInt32(1)
86
87 spec := policy.PodDisruptionBudgetSpec{
88 MinAvailable: &c1,
89 MaxUnavailable: &c2,
90 }
91 errs := ValidatePodDisruptionBudgetSpec(spec, PodDisruptionBudgetValidationOptions{true}, field.NewPath("foo"))
92 if len(errs) == 0 {
93 t.Errorf("unexpected success for %v", spec)
94 }
95 }
96
97 func TestValidateUnhealthyPodEvictionPolicyDisruptionBudgetSpec(t *testing.T) {
98 c1 := intstr.FromString("10%")
99 alwaysAllowPolicy := policy.AlwaysAllow
100 invalidPolicy := policy.UnhealthyPodEvictionPolicyType("Invalid")
101
102 testCases := []struct {
103 name string
104 pdbSpec policy.PodDisruptionBudgetSpec
105 expectErr bool
106 }{{
107 name: "valid nil UnhealthyPodEvictionPolicy",
108 pdbSpec: policy.PodDisruptionBudgetSpec{
109 MinAvailable: &c1,
110 UnhealthyPodEvictionPolicy: nil,
111 },
112 expectErr: false,
113 }, {
114 name: "valid UnhealthyPodEvictionPolicy",
115 pdbSpec: policy.PodDisruptionBudgetSpec{
116 MinAvailable: &c1,
117 UnhealthyPodEvictionPolicy: &alwaysAllowPolicy,
118 },
119 expectErr: false,
120 }, {
121 name: "empty UnhealthyPodEvictionPolicy",
122 pdbSpec: policy.PodDisruptionBudgetSpec{
123 MinAvailable: &c1,
124 UnhealthyPodEvictionPolicy: new(policy.UnhealthyPodEvictionPolicyType),
125 },
126 expectErr: true,
127 }, {
128 name: "invalid UnhealthyPodEvictionPolicy",
129 pdbSpec: policy.PodDisruptionBudgetSpec{
130 MinAvailable: &c1,
131 UnhealthyPodEvictionPolicy: &invalidPolicy,
132 },
133 expectErr: true,
134 }}
135
136 for _, tc := range testCases {
137 t.Run(tc.name, func(t *testing.T) {
138 errs := ValidatePodDisruptionBudgetSpec(tc.pdbSpec, PodDisruptionBudgetValidationOptions{true}, field.NewPath("foo"))
139 if len(errs) == 0 && tc.expectErr {
140 t.Errorf("unexpected success for %v", tc.pdbSpec)
141 }
142 if len(errs) != 0 && !tc.expectErr {
143 t.Errorf("unexpected failure for %v", tc.pdbSpec)
144 }
145 })
146 }
147 }
148
149 func TestValidatePodDisruptionBudgetStatus(t *testing.T) {
150 const expectNoErrors = false
151 const expectErrors = true
152 testCases := []struct {
153 name string
154 pdbStatus policy.PodDisruptionBudgetStatus
155 expectErrForVersion map[schema.GroupVersion]bool
156 }{{
157 name: "DisruptionsAllowed: 10",
158 pdbStatus: policy.PodDisruptionBudgetStatus{
159 DisruptionsAllowed: 10,
160 },
161 expectErrForVersion: map[schema.GroupVersion]bool{
162 policy.SchemeGroupVersion: expectNoErrors,
163 policyv1beta1.SchemeGroupVersion: expectNoErrors,
164 },
165 }, {
166 name: "CurrentHealthy: 5",
167 pdbStatus: policy.PodDisruptionBudgetStatus{
168 CurrentHealthy: 5,
169 },
170 expectErrForVersion: map[schema.GroupVersion]bool{
171 policy.SchemeGroupVersion: expectNoErrors,
172 policyv1beta1.SchemeGroupVersion: expectNoErrors,
173 },
174 }, {
175 name: "DesiredHealthy: 3",
176 pdbStatus: policy.PodDisruptionBudgetStatus{
177 DesiredHealthy: 3,
178 },
179 expectErrForVersion: map[schema.GroupVersion]bool{
180 policy.SchemeGroupVersion: expectNoErrors,
181 policyv1beta1.SchemeGroupVersion: expectNoErrors,
182 },
183 }, {
184 name: "ExpectedPods: 2",
185 pdbStatus: policy.PodDisruptionBudgetStatus{
186 ExpectedPods: 2,
187 },
188 expectErrForVersion: map[schema.GroupVersion]bool{
189 policy.SchemeGroupVersion: expectNoErrors,
190 policyv1beta1.SchemeGroupVersion: expectNoErrors,
191 },
192 }, {
193 name: "DisruptionsAllowed: -10",
194 pdbStatus: policy.PodDisruptionBudgetStatus{
195 DisruptionsAllowed: -10,
196 },
197 expectErrForVersion: map[schema.GroupVersion]bool{
198 policy.SchemeGroupVersion: expectErrors,
199 policyv1beta1.SchemeGroupVersion: expectNoErrors,
200 },
201 }, {
202 name: "CurrentHealthy: -5",
203 pdbStatus: policy.PodDisruptionBudgetStatus{
204 CurrentHealthy: -5,
205 },
206 expectErrForVersion: map[schema.GroupVersion]bool{
207 policy.SchemeGroupVersion: expectErrors,
208 policyv1beta1.SchemeGroupVersion: expectNoErrors,
209 },
210 }, {
211 name: "DesiredHealthy: -3",
212 pdbStatus: policy.PodDisruptionBudgetStatus{
213 DesiredHealthy: -3,
214 },
215 expectErrForVersion: map[schema.GroupVersion]bool{
216 policy.SchemeGroupVersion: expectErrors,
217 policyv1beta1.SchemeGroupVersion: expectNoErrors,
218 },
219 }, {
220 name: "ExpectedPods: -2",
221 pdbStatus: policy.PodDisruptionBudgetStatus{
222 ExpectedPods: -2,
223 },
224 expectErrForVersion: map[schema.GroupVersion]bool{
225 policy.SchemeGroupVersion: expectErrors,
226 policyv1beta1.SchemeGroupVersion: expectNoErrors,
227 },
228 }, {
229 name: "Conditions valid",
230 pdbStatus: policy.PodDisruptionBudgetStatus{
231 Conditions: []metav1.Condition{{
232 Type: policyv1beta1.DisruptionAllowedCondition,
233 Status: metav1.ConditionTrue,
234 LastTransitionTime: metav1.Time{
235 Time: time.Now().Add(-5 * time.Minute),
236 },
237 Reason: policyv1beta1.SufficientPodsReason,
238 Message: "message",
239 ObservedGeneration: 3,
240 }},
241 },
242 expectErrForVersion: map[schema.GroupVersion]bool{
243 policy.SchemeGroupVersion: expectNoErrors,
244 policyv1beta1.SchemeGroupVersion: expectNoErrors,
245 },
246 }, {
247 name: "Conditions not valid",
248 pdbStatus: policy.PodDisruptionBudgetStatus{
249 Conditions: []metav1.Condition{{
250 Type: policyv1beta1.DisruptionAllowedCondition,
251 Status: metav1.ConditionTrue,
252 }, {
253 Type: policyv1beta1.DisruptionAllowedCondition,
254 Status: metav1.ConditionFalse,
255 }},
256 },
257 expectErrForVersion: map[schema.GroupVersion]bool{
258 policy.SchemeGroupVersion: expectErrors,
259 policyv1beta1.SchemeGroupVersion: expectErrors,
260 },
261 }}
262
263 for _, tc := range testCases {
264 for apiVersion, expectErrors := range tc.expectErrForVersion {
265 t.Run(fmt.Sprintf("apiVersion: %s, %s", apiVersion.String(), tc.name), func(t *testing.T) {
266 errors := ValidatePodDisruptionBudgetStatusUpdate(tc.pdbStatus, policy.PodDisruptionBudgetStatus{},
267 field.NewPath("status"), apiVersion)
268 errCount := len(errors)
269
270 if errCount > 0 && !expectErrors {
271 t.Errorf("unexpected failure %v for %v", errors, tc.pdbStatus)
272 }
273
274 if errCount == 0 && expectErrors {
275 t.Errorf("expected errors but didn't one for %v", tc.pdbStatus)
276 }
277 })
278 }
279 }
280 }
281
282 func TestIsValidSysctlPattern(t *testing.T) {
283 valid := []string{
284 "a.b.c.d",
285 "a",
286 "a_b",
287 "a-b",
288 "abc",
289 "abc.def",
290 "*",
291 "a.*",
292 "*",
293 "abc*",
294 "a.abc*",
295 "a.b.*",
296 "a/b/c/d",
297 "a/*",
298 "a/b/*",
299 "a.b/c*",
300 "a.b/c.d",
301 "a/b.c/d",
302 }
303 invalid := []string{
304 "",
305 "รค",
306 "a_",
307 "_",
308 "_a",
309 "_a._b",
310 "__",
311 "-",
312 ".",
313 "a.",
314 ".a",
315 "a.b.",
316 "a*.b",
317 "a*b",
318 "*a",
319 "Abc",
320 "/",
321 "a/",
322 "/a",
323 "a*/b",
324 func(n int) string {
325 x := make([]byte, n)
326 for i := range x {
327 x[i] = byte('a')
328 }
329 return string(x)
330 }(256),
331 }
332 for _, s := range valid {
333 if !IsValidSysctlPattern(s) {
334 t.Errorf("%q expected to be a valid sysctl pattern", s)
335 }
336 }
337 for _, s := range invalid {
338 if IsValidSysctlPattern(s) {
339 t.Errorf("%q expected to be an invalid sysctl pattern", s)
340 }
341 }
342 }
343
View as plain text