k8s.io/kubernetes@v1.29.3/pkg/apis/policy/validation/validation_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 }