github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/validation/objectmeta_test.go (about) 1 /* 2 Copyright 2017 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 "math/rand" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 27 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation/field" 28 ) 29 30 const ( 31 maxLengthErrMsg = "must be no more than" 32 namePartErrMsg = "name part must consist of" 33 nameErrMsg = "a qualified name must consist of" 34 ) 35 36 // Ensure custom name functions are allowed 37 func TestValidateObjectMetaCustomName(t *testing.T) { 38 errs := ValidateObjectMeta( 39 &metav1.ObjectMeta{Name: "test", GenerateName: "foo"}, 40 false, 41 func(s string, prefix bool) []string { 42 if s == "test" { 43 return nil 44 } 45 return []string{"name-gen"} 46 }, 47 field.NewPath("field")) 48 if len(errs) != 1 { 49 t.Fatalf("unexpected errors: %v", errs) 50 } 51 if !strings.Contains(errs[0].Error(), "name-gen") { 52 t.Errorf("unexpected error message: %v", errs) 53 } 54 } 55 56 // Ensure namespace names follow dns label format 57 func TestValidateObjectMetaNamespaces(t *testing.T) { 58 errs := ValidateObjectMeta( 59 &metav1.ObjectMeta{Name: "test", Namespace: "foo.bar"}, 60 true, 61 func(s string, prefix bool) []string { 62 return nil 63 }, 64 field.NewPath("field")) 65 if len(errs) != 1 { 66 t.Fatalf("unexpected errors: %v", errs) 67 } 68 if !strings.Contains(errs[0].Error(), `Invalid value: "foo.bar"`) { 69 t.Errorf("unexpected error message: %v", errs) 70 } 71 maxLength := 63 72 letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 73 b := make([]rune, maxLength+1) 74 for i := range b { 75 b[i] = letters[rand.Intn(len(letters))] 76 } 77 errs = ValidateObjectMeta( 78 &metav1.ObjectMeta{Name: "test", Namespace: string(b)}, 79 true, 80 func(s string, prefix bool) []string { 81 return nil 82 }, 83 field.NewPath("field")) 84 if len(errs) != 2 { 85 t.Fatalf("unexpected errors: %v", errs) 86 } 87 if !strings.Contains(errs[0].Error(), "Invalid value") || !strings.Contains(errs[1].Error(), "Invalid value") { 88 t.Errorf("unexpected error message: %v", errs) 89 } 90 } 91 92 func TestValidateObjectMetaOwnerReferences(t *testing.T) { 93 trueVar := true 94 falseVar := false 95 testCases := []struct { 96 description string 97 ownerReferences []metav1.OwnerReference 98 expectError bool 99 expectedErrorMessage string 100 }{ 101 { 102 description: "simple success - third party extension.", 103 ownerReferences: []metav1.OwnerReference{ 104 { 105 APIVersion: "customresourceVersion", 106 Kind: "customresourceKind", 107 Name: "name", 108 UID: "1", 109 }, 110 }, 111 expectError: false, 112 expectedErrorMessage: "", 113 }, 114 { 115 description: "simple failures - event shouldn't be set as an owner", 116 ownerReferences: []metav1.OwnerReference{ 117 { 118 APIVersion: "v1", 119 Kind: "Event", 120 Name: "name", 121 UID: "1", 122 }, 123 }, 124 expectError: true, 125 expectedErrorMessage: "is disallowed from being an owner", 126 }, 127 { 128 description: "simple controller ref success - one reference with Controller set", 129 ownerReferences: []metav1.OwnerReference{ 130 { 131 APIVersion: "customresourceVersion", 132 Kind: "customresourceKind", 133 Name: "name", 134 UID: "1", 135 Controller: &falseVar, 136 }, 137 { 138 APIVersion: "customresourceVersion", 139 Kind: "customresourceKind", 140 Name: "name", 141 UID: "2", 142 Controller: &trueVar, 143 }, 144 { 145 APIVersion: "customresourceVersion", 146 Kind: "customresourceKind", 147 Name: "name", 148 UID: "3", 149 Controller: &falseVar, 150 }, 151 { 152 APIVersion: "customresourceVersion", 153 Kind: "customresourceKind", 154 Name: "name", 155 UID: "4", 156 }, 157 }, 158 expectError: false, 159 expectedErrorMessage: "", 160 }, 161 { 162 description: "simple controller ref failure - two references with Controller set", 163 ownerReferences: []metav1.OwnerReference{ 164 { 165 APIVersion: "customresourceVersion", 166 Kind: "customresourceKind1", 167 Name: "name", 168 UID: "1", 169 Controller: &falseVar, 170 }, 171 { 172 APIVersion: "customresourceVersion", 173 Kind: "customresourceKind2", 174 Name: "name", 175 UID: "2", 176 Controller: &trueVar, 177 }, 178 { 179 APIVersion: "customresourceVersion", 180 Kind: "customresourceKind3", 181 Name: "name", 182 UID: "3", 183 Controller: &trueVar, 184 }, 185 { 186 APIVersion: "customresourceVersion", 187 Kind: "customresourceKind4", 188 Name: "name", 189 UID: "4", 190 }, 191 }, 192 expectError: true, 193 expectedErrorMessage: "Only one reference can have Controller set to true. Found \"true\" in references for customresourceKind2/name and customresourceKind3/name", 194 }, 195 } 196 197 for _, tc := range testCases { 198 errs := ValidateObjectMeta( 199 &metav1.ObjectMeta{Name: "test", Namespace: "test", OwnerReferences: tc.ownerReferences}, 200 true, 201 func(s string, prefix bool) []string { 202 return nil 203 }, 204 field.NewPath("field")) 205 if len(errs) != 0 && !tc.expectError { 206 t.Errorf("unexpected error: %v in test case %v", errs, tc.description) 207 } 208 if len(errs) == 0 && tc.expectError { 209 t.Errorf("expect error in test case %v", tc.description) 210 } 211 if len(errs) != 0 && !strings.Contains(errs[0].Error(), tc.expectedErrorMessage) { 212 t.Errorf("unexpected error message: %v in test case %v", errs, tc.description) 213 } 214 } 215 } 216 217 func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) { 218 if errs := ValidateObjectMetaUpdate( 219 &metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 220 &metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))}, 221 field.NewPath("field"), 222 ); len(errs) != 1 { 223 t.Fatalf("unexpected errors: %v", errs) 224 } 225 if errs := ValidateObjectMetaUpdate( 226 &metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))}, 227 &metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 228 field.NewPath("field"), 229 ); len(errs) != 1 { 230 t.Fatalf("unexpected errors: %v", errs) 231 } 232 if errs := ValidateObjectMetaUpdate( 233 &metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))}, 234 &metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(11, 0))}, 235 field.NewPath("field"), 236 ); len(errs) != 1 { 237 t.Fatalf("unexpected errors: %v", errs) 238 } 239 } 240 241 func TestValidateFinalizersUpdate(t *testing.T) { 242 testcases := map[string]struct { 243 Old metav1.ObjectMeta 244 New metav1.ObjectMeta 245 ExpectedErr string 246 }{ 247 "invalid adding finalizers": { 248 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}}, 249 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}}, 250 ExpectedErr: "y/b", 251 }, 252 "invalid changing finalizers": { 253 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}}, 254 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/b"}}, 255 ExpectedErr: "x/b", 256 }, 257 "valid removing finalizers": { 258 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}}, 259 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}}, 260 ExpectedErr: "", 261 }, 262 "valid adding finalizers for objects not being deleted": { 263 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a"}}, 264 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a", "y/b"}}, 265 ExpectedErr: "", 266 }, 267 } 268 for name, tc := range testcases { 269 errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field")) 270 if len(errs) == 0 { 271 if len(tc.ExpectedErr) != 0 { 272 t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr) 273 } 274 } else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) { 275 t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a) 276 } 277 } 278 } 279 280 func TestValidateFinalizersPreventConflictingFinalizers(t *testing.T) { 281 testcases := map[string]struct { 282 ObjectMeta metav1.ObjectMeta 283 ExpectedErr string 284 }{ 285 "conflicting finalizers": { 286 ObjectMeta: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents}}, 287 ExpectedErr: "cannot be both set", 288 }, 289 } 290 for name, tc := range testcases { 291 errs := ValidateObjectMeta(&tc.ObjectMeta, false, NameIsDNSSubdomain, field.NewPath("field")) 292 if len(errs) == 0 { 293 if len(tc.ExpectedErr) != 0 { 294 t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr) 295 } 296 } else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) { 297 t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a) 298 } 299 } 300 } 301 302 func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) { 303 now := metav1.NewTime(time.Unix(1000, 0).UTC()) 304 later := metav1.NewTime(time.Unix(2000, 0).UTC()) 305 gracePeriodShort := int64(30) 306 gracePeriodLong := int64(40) 307 308 testcases := map[string]struct { 309 Old metav1.ObjectMeta 310 New metav1.ObjectMeta 311 ExpectedNew metav1.ObjectMeta 312 ExpectedErrs []string 313 }{ 314 "valid without deletion fields": { 315 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 316 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 317 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 318 ExpectedErrs: []string{}, 319 }, 320 "valid with deletion fields": { 321 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort}, 322 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort}, 323 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort}, 324 ExpectedErrs: []string{}, 325 }, 326 327 "invalid set deletionTimestamp": { 328 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 329 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now}, 330 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now}, 331 ExpectedErrs: []string{"field.deletionTimestamp: Invalid value: 1970-01-01 00:16:40 +0000 UTC: field is immutable"}, 332 }, 333 "invalid clear deletionTimestamp": { 334 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now}, 335 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 336 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 337 ExpectedErrs: []string{"field.deletionTimestamp: Invalid value: \"null\": field is immutable"}, 338 }, 339 "invalid change deletionTimestamp": { 340 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now}, 341 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &later}, 342 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &later}, 343 ExpectedErrs: []string{"field.deletionTimestamp: Invalid value: 1970-01-01 00:33:20 +0000 UTC: field is immutable"}, 344 }, 345 346 "invalid set deletionGracePeriodSeconds": { 347 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 348 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort}, 349 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort}, 350 ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 30: field is immutable"}, 351 }, 352 "invalid clear deletionGracePeriodSeconds": { 353 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort}, 354 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 355 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"}, 356 ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: \"null\": field is immutable"}, 357 }, 358 "invalid change deletionGracePeriodSeconds": { 359 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort}, 360 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong}, 361 ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong}, 362 ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 40: field is immutable"}, 363 }, 364 } 365 366 for k, tc := range testcases { 367 errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field")) 368 if len(errs) != len(tc.ExpectedErrs) { 369 t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs) 370 t.Logf("%s: Got: %#v", k, errs) 371 t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs)) 372 continue 373 } 374 for i := range errs { 375 if errs[i].Error() != tc.ExpectedErrs[i] { 376 t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errs[i].Error()) 377 } 378 } 379 if !reflect.DeepEqual(tc.New, tc.ExpectedNew) { 380 t.Errorf("%s: Expected after validation:\n%#v\ngot\n%#v", k, tc.ExpectedNew, tc.New) 381 } 382 } 383 } 384 385 func TestObjectMetaGenerationUpdate(t *testing.T) { 386 testcases := map[string]struct { 387 Old metav1.ObjectMeta 388 New metav1.ObjectMeta 389 ExpectedErrs []string 390 }{ 391 "invalid generation change - decremented": { 392 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5}, 393 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 4}, 394 ExpectedErrs: []string{"field.generation: Invalid value: 4: must not be decremented"}, 395 }, 396 "valid generation change - incremented by one": { 397 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 1}, 398 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 2}, 399 ExpectedErrs: []string{}, 400 }, 401 "valid generation field - not updated": { 402 Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5}, 403 New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5}, 404 ExpectedErrs: []string{}, 405 }, 406 } 407 408 for k, tc := range testcases { 409 errList := []string{} 410 errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field")) 411 if len(errs) != len(tc.ExpectedErrs) { 412 t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs) 413 for _, err := range errs { 414 errList = append(errList, err.Error()) 415 } 416 t.Logf("%s: Got: %#v", k, errList) 417 t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs)) 418 continue 419 } 420 for i := range errList { 421 if errList[i] != tc.ExpectedErrs[i] { 422 t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errList[i]) 423 } 424 } 425 } 426 } 427 428 // Ensure trailing slash is allowed in generate name 429 func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) { 430 errs := ValidateObjectMeta( 431 &metav1.ObjectMeta{Name: "test", GenerateName: "foo-"}, 432 false, 433 NameIsDNSSubdomain, 434 field.NewPath("field")) 435 if len(errs) != 0 { 436 t.Fatalf("unexpected errors: %v", errs) 437 } 438 } 439 440 func TestValidateAnnotations(t *testing.T) { 441 successCases := []map[string]string{ 442 {"simple": "bar"}, 443 {"now-with-dashes": "bar"}, 444 {"1-starts-with-num": "bar"}, 445 {"1234": "bar"}, 446 {"simple/simple": "bar"}, 447 {"now-with-dashes/simple": "bar"}, 448 {"now-with-dashes/now-with-dashes": "bar"}, 449 {"now.with.dots/simple": "bar"}, 450 {"now-with.dashes-and.dots/simple": "bar"}, 451 {"1-num.2-num/3-num": "bar"}, 452 {"1234/5678": "bar"}, 453 {"1.2.3.4/5678": "bar"}, 454 {"UpperCase123": "bar"}, 455 {"a": strings.Repeat("b", TotalAnnotationSizeLimitB-1)}, 456 { 457 "a": strings.Repeat("b", TotalAnnotationSizeLimitB/2-1), 458 "c": strings.Repeat("d", TotalAnnotationSizeLimitB/2-1), 459 }, 460 } 461 for i := range successCases { 462 errs := ValidateAnnotations(successCases[i], field.NewPath("field")) 463 if len(errs) != 0 { 464 t.Errorf("case[%d] expected success, got %#v", i, errs) 465 } 466 } 467 468 nameErrorCases := []struct { 469 annotations map[string]string 470 expect string 471 }{ 472 {map[string]string{"nospecialchars^=@": "bar"}, namePartErrMsg}, 473 {map[string]string{"cantendwithadash-": "bar"}, namePartErrMsg}, 474 {map[string]string{"only/one/slash": "bar"}, nameErrMsg}, 475 {map[string]string{strings.Repeat("a", 254): "bar"}, maxLengthErrMsg}, 476 } 477 for i := range nameErrorCases { 478 errs := ValidateAnnotations(nameErrorCases[i].annotations, field.NewPath("field")) 479 if len(errs) != 1 { 480 t.Errorf("case[%d]: expected failure", i) 481 } else { 482 if !strings.Contains(errs[0].Detail, nameErrorCases[i].expect) { 483 t.Errorf("case[%d]: error details do not include %q: %q", i, nameErrorCases[i].expect, errs[0].Detail) 484 } 485 } 486 } 487 totalSizeErrorCases := []map[string]string{ 488 {"a": strings.Repeat("b", TotalAnnotationSizeLimitB)}, 489 { 490 "a": strings.Repeat("b", TotalAnnotationSizeLimitB/2), 491 "c": strings.Repeat("d", TotalAnnotationSizeLimitB/2), 492 }, 493 } 494 for i := range totalSizeErrorCases { 495 errs := ValidateAnnotations(totalSizeErrorCases[i], field.NewPath("field")) 496 if len(errs) != 1 { 497 t.Errorf("case[%d] expected failure", i) 498 } 499 } 500 }