knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/resourcesemantics/validation/validation_deprecated_test.go (about) 1 /* 2 Copyright 2019 The Knative 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 "testing" 21 22 admissionv1 "k8s.io/api/admission/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "knative.dev/pkg/apis" 25 "knative.dev/pkg/ptr" 26 27 . "knative.dev/pkg/logging/testing" 28 . "knative.dev/pkg/testing" 29 . "knative.dev/pkg/webhook/testing" 30 ) 31 32 // In strict mode, you are not allowed to set a deprecated field when doing a Create. 33 func TestStrictValidation(t *testing.T) { 34 newCreateReq := func(new []byte) *admissionv1.AdmissionRequest { 35 req := &admissionv1.AdmissionRequest{ 36 Operation: admissionv1.Create, 37 Kind: metav1.GroupVersionKind{ 38 Group: "pkg.knative.dev", 39 Version: "v1alpha1", 40 Kind: "InnerDefaultResource", 41 }, 42 } 43 req.Object.Raw = new 44 return req 45 } 46 47 newUpdateReq := func(old, new []byte) *admissionv1.AdmissionRequest { 48 req := &admissionv1.AdmissionRequest{ 49 Operation: admissionv1.Update, 50 Kind: metav1.GroupVersionKind{ 51 Group: "pkg.knative.dev", 52 Version: "v1alpha1", 53 Kind: "InnerDefaultResource", 54 }, 55 } 56 req.OldObject.Raw = old 57 req.Object.Raw = new 58 return req 59 } 60 61 testCases := map[string]struct { 62 strict bool 63 req *admissionv1.AdmissionRequest 64 wantErrs []string 65 wantWarnings []string 66 }{ 67 "create, strict": { 68 strict: true, 69 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 70 DeprecatedField: "fail setting.", 71 }, nil)), 72 wantErrs: []string{ 73 "must not set", 74 "spec.field", 75 }, 76 }, 77 "create strict, spec.sub.string": { 78 strict: true, 79 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 80 SubFields: &InnerDefaultSubSpec{ 81 DeprecatedString: "an error", 82 }, 83 }, nil)), 84 wantErrs: []string{ 85 "must not set", 86 "spec.subFields.string", 87 }, 88 }, 89 "create strict, spec.sub.stringptr": { 90 strict: true, 91 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 92 SubFields: &InnerDefaultSubSpec{ 93 DeprecatedStringPtr: func() *string { 94 s := "test string" 95 return &s 96 }(), 97 }, 98 }, nil)), 99 wantErrs: []string{ 100 "must not set", 101 "spec.subFields.stringPtr", 102 }, 103 }, 104 "create strict, spec.sub.int": { 105 strict: true, 106 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 107 SubFields: &InnerDefaultSubSpec{ 108 DeprecatedInt: 42, 109 }, 110 }, nil)), 111 wantErrs: []string{ 112 "must not set", 113 "spec.subFields.int", 114 }, 115 }, 116 "create strict, spec.sub.intptr": { 117 strict: true, 118 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 119 SubFields: &InnerDefaultSubSpec{ 120 DeprecatedIntPtr: ptr.Int64(42), 121 }, 122 }, nil)), 123 wantErrs: []string{ 124 "must not set", 125 "spec.subFields.intPtr", 126 }, 127 }, 128 "create strict, spec.sub.map": { 129 strict: true, 130 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 131 SubFields: &InnerDefaultSubSpec{ 132 DeprecatedMap: map[string]string{"hello": "failure"}, 133 }, 134 }, nil)), 135 wantErrs: []string{ 136 "must not set", 137 "spec.subFields.map", 138 }, 139 }, 140 "create strict, spec.sub.slice": { 141 strict: true, 142 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 143 SubFields: &InnerDefaultSubSpec{ 144 DeprecatedSlice: []string{"hello", "failure"}, 145 }, 146 }, nil)), 147 wantErrs: []string{ 148 "must not set", 149 "spec.subFields.slice", 150 }, 151 }, 152 "create strict, spec.sub.struct": { 153 strict: true, 154 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 155 SubFields: &InnerDefaultSubSpec{ 156 DeprecatedStruct: InnerDefaultStruct{ 157 FieldAsString: "fail", 158 }, 159 }, 160 }, nil)), 161 wantErrs: []string{ 162 "must not set", 163 "spec.subFields.struct", 164 }, 165 }, 166 "create strict, spec.sub.structptr": { 167 strict: true, 168 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 169 SubFields: &InnerDefaultSubSpec{ 170 DeprecatedStructPtr: &InnerDefaultStruct{ 171 FieldAsString: "fail", 172 }, 173 }, 174 }, nil)), 175 wantErrs: []string{ 176 "must not set", 177 "spec.subFields.structPtr", 178 }, 179 }, 180 181 "update strict, spec.sub.string": { 182 strict: true, 183 req: newUpdateReq( 184 createInnerDefaultResourceWithoutSpec(t), 185 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 186 SubFields: &InnerDefaultSubSpec{ 187 DeprecatedString: "an error", 188 }, 189 }, nil)), 190 wantErrs: []string{ 191 "must not set", 192 "spec.subFields.string", 193 }, 194 }, 195 "update strict, spec.sub.stringptr": { 196 strict: true, 197 req: newUpdateReq( 198 createInnerDefaultResourceWithoutSpec(t), 199 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 200 SubFields: &InnerDefaultSubSpec{ 201 DeprecatedStringPtr: func() *string { 202 s := "test string" 203 return &s 204 }(), 205 }, 206 }, nil)), 207 wantErrs: []string{ 208 "must not set", 209 "spec.subFields.stringPtr", 210 }, 211 }, 212 "update strict, spec.sub.int": { 213 strict: true, 214 req: newUpdateReq( 215 createInnerDefaultResourceWithoutSpec(t), 216 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 217 SubFields: &InnerDefaultSubSpec{ 218 DeprecatedInt: 42, 219 }, 220 }, nil)), 221 wantErrs: []string{ 222 "must not set", 223 "spec.subFields.int", 224 }, 225 }, 226 "update strict, spec.sub.intptr": { 227 strict: true, 228 req: newUpdateReq( 229 createInnerDefaultResourceWithoutSpec(t), 230 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 231 SubFields: &InnerDefaultSubSpec{ 232 DeprecatedIntPtr: ptr.Int64(42), 233 }, 234 }, nil)), 235 wantErrs: []string{ 236 "must not set", 237 "spec.subFields.intPtr", 238 }, 239 }, 240 "update strict, spec.sub.map": { 241 strict: true, 242 req: newUpdateReq( 243 createInnerDefaultResourceWithoutSpec(t), 244 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 245 SubFields: &InnerDefaultSubSpec{ 246 DeprecatedMap: map[string]string{"hello": "failure"}, 247 }, 248 }, nil)), 249 wantErrs: []string{ 250 "must not set", 251 "spec.subFields.map", 252 }, 253 }, 254 "update strict, spec.sub.slice": { 255 strict: true, 256 req: newUpdateReq( 257 createInnerDefaultResourceWithoutSpec(t), 258 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 259 SubFields: &InnerDefaultSubSpec{ 260 DeprecatedSlice: []string{"hello", "failure"}, 261 }, 262 }, nil)), 263 wantErrs: []string{ 264 "must not set", 265 "spec.subFields.slice", 266 }, 267 }, 268 "update strict, spec.sub.struct": { 269 strict: true, 270 req: newUpdateReq( 271 createInnerDefaultResourceWithoutSpec(t), 272 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 273 SubFields: &InnerDefaultSubSpec{ 274 DeprecatedStruct: InnerDefaultStruct{ 275 FieldAsString: "fail", 276 }, 277 }, 278 }, nil)), 279 wantErrs: []string{ 280 "must not set", 281 "spec.subFields.struct", 282 }, 283 }, 284 "update strict, spec.sub.structptr": { 285 strict: true, 286 req: newUpdateReq( 287 createInnerDefaultResourceWithoutSpec(t), 288 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 289 SubFields: &InnerDefaultSubSpec{ 290 DeprecatedStructPtr: &InnerDefaultStruct{ 291 FieldAsString: "fail", 292 }, 293 }, 294 }, nil)), 295 wantErrs: []string{ 296 "must not set", 297 "spec.subFields.structPtr", 298 }, 299 }, 300 301 "overwrite, strict": { 302 strict: true, 303 req: newUpdateReq( 304 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 305 DeprecatedField: "original setting.", 306 }, nil), 307 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 308 DeprecatedField: "fail setting.", 309 }, nil)), 310 wantErrs: []string{ 311 "must not update", 312 "spec.field", 313 }, 314 }, 315 "overwrite strict, spec.sub.string": { 316 strict: true, 317 req: newUpdateReq( 318 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 319 SubFields: &InnerDefaultSubSpec{ 320 DeprecatedString: "original string", 321 }, 322 }, nil), 323 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 324 SubFields: &InnerDefaultSubSpec{ 325 DeprecatedString: "an error", 326 }, 327 }, nil)), 328 wantErrs: []string{ 329 "must not update", 330 "spec.subFields.string", 331 }, 332 }, 333 "overwrite strict, spec.sub.stringptr": { 334 strict: true, 335 req: newUpdateReq( 336 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 337 SubFields: &InnerDefaultSubSpec{ 338 DeprecatedStringPtr: func() *string { 339 s := "original string" 340 return &s 341 }(), 342 }, 343 }, nil), 344 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 345 SubFields: &InnerDefaultSubSpec{ 346 DeprecatedStringPtr: func() *string { 347 s := "test string" 348 return &s 349 }(), 350 }, 351 }, nil)), 352 wantErrs: []string{ 353 "must not update", 354 "spec.subFields.stringPtr", 355 }, 356 }, 357 "overwrite strict, spec.sub.int": { 358 strict: true, 359 req: newUpdateReq( 360 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 361 SubFields: &InnerDefaultSubSpec{ 362 DeprecatedInt: 10, 363 }, 364 }, nil), 365 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 366 SubFields: &InnerDefaultSubSpec{ 367 DeprecatedInt: 42, 368 }, 369 }, nil)), 370 wantErrs: []string{ 371 "must not update", 372 "spec.subFields.int", 373 }, 374 }, 375 "overwrite strict, spec.sub.intptr": { 376 strict: true, 377 req: newUpdateReq( 378 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 379 SubFields: &InnerDefaultSubSpec{ 380 DeprecatedIntPtr: ptr.Int64(10), 381 }, 382 }, nil), 383 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 384 SubFields: &InnerDefaultSubSpec{ 385 DeprecatedIntPtr: ptr.Int64(42), 386 }, 387 }, nil)), 388 wantErrs: []string{ 389 "must not update", 390 "spec.subFields.intPtr", 391 }, 392 }, 393 "overwrite strict, spec.sub.map": { 394 strict: true, 395 req: newUpdateReq( 396 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 397 SubFields: &InnerDefaultSubSpec{ 398 DeprecatedMap: map[string]string{"goodbye": "existing"}, 399 }, 400 }, nil), 401 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 402 SubFields: &InnerDefaultSubSpec{ 403 DeprecatedMap: map[string]string{"hello": "failure"}, 404 }, 405 }, nil)), 406 wantErrs: []string{ 407 "must not update", 408 "spec.subFields.map", 409 }, 410 }, 411 "overwrite strict, spec.sub.slice": { 412 strict: true, 413 req: newUpdateReq( 414 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 415 SubFields: &InnerDefaultSubSpec{ 416 DeprecatedSlice: []string{"hello", "existing"}, 417 }, 418 }, nil), 419 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 420 SubFields: &InnerDefaultSubSpec{ 421 DeprecatedSlice: []string{"hello", "failure"}, 422 }, 423 }, nil)), 424 wantErrs: []string{ 425 "must not update", 426 "spec.subFields.slice", 427 }, 428 }, 429 "overwrite strict, spec.sub.struct": { 430 strict: true, 431 req: newUpdateReq( 432 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 433 SubFields: &InnerDefaultSubSpec{ 434 DeprecatedStruct: InnerDefaultStruct{ 435 FieldAsString: "original", 436 }, 437 }, 438 }, nil), 439 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 440 SubFields: &InnerDefaultSubSpec{ 441 DeprecatedStruct: InnerDefaultStruct{ 442 FieldAsString: "fail", 443 }, 444 }, 445 }, nil)), 446 wantErrs: []string{ 447 "must not update", 448 "spec.subFields.struct", 449 }, 450 }, 451 "overwrite strict, spec.sub.structptr": { 452 strict: true, 453 req: newUpdateReq( 454 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 455 SubFields: &InnerDefaultSubSpec{ 456 DeprecatedStructPtr: &InnerDefaultStruct{ 457 FieldAsString: "original", 458 }, 459 }, 460 }, nil), 461 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 462 SubFields: &InnerDefaultSubSpec{ 463 DeprecatedStructPtr: &InnerDefaultStruct{ 464 FieldAsString: "fail", 465 }, 466 }, 467 ToBeDeprecatedField: "asdf", 468 }, nil)), 469 wantErrs: []string{ 470 "must not update", 471 "spec.subFields.structPtr", 472 }, 473 wantWarnings: []string{"must not set", "fieldWillWarn"}, 474 }, 475 476 "create, not strict": { 477 strict: false, 478 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 479 DeprecatedField: "fail setting.", 480 }, nil)), 481 }, 482 "update, not strict": { 483 strict: false, 484 req: newUpdateReq( 485 createInnerDefaultResourceWithoutSpec(t), 486 createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 487 DeprecatedField: "fail setting.", 488 }, nil)), 489 }, 490 491 "create, not strict, with warning": { 492 strict: false, 493 req: newCreateReq(createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 494 ToBeDeprecatedField: "emit a warning", 495 }, nil)), 496 wantWarnings: []string{"must not set", "fieldWillWarn"}, 497 }, 498 } 499 500 for n, tc := range testCases { 501 t.Run(n, func(t *testing.T) { 502 ctx := TestContextWithLogger(t) 503 if tc.strict { 504 ctx = apis.DisallowDeprecated(ctx) 505 } 506 507 _, ac := newNonRunningTestResourceAdmissionController(t) 508 resp := ac.Admit(ctx, tc.req) 509 510 if len(tc.wantErrs) > 0 { 511 for _, err := range tc.wantErrs { 512 ExpectFailsWith(t, resp, err) 513 } 514 } else { 515 ExpectAllowed(t, resp) 516 } 517 for _, err := range tc.wantWarnings { 518 ExpectWarnsWith(t, resp, err) 519 } 520 }) 521 } 522 } 523 524 // In strict mode, you are not allowed to set a deprecated filed when doing a Create. 525 func TestStrictValidation_Spec_Create(t *testing.T) { 526 req := &admissionv1.AdmissionRequest{ 527 Operation: admissionv1.Create, 528 Kind: metav1.GroupVersionKind{ 529 Group: "pkg.knative.dev", 530 Version: "v1alpha1", 531 Kind: "InnerDefaultResource", 532 }, 533 } 534 req.Object.Raw = createInnerDefaultResourceWithSpecAndStatus(t, &InnerDefaultSpec{ 535 DeprecatedField: "fail setting.", 536 }, nil) 537 538 ctx := apis.DisallowDeprecated(TestContextWithLogger(t)) 539 540 _, ac := newNonRunningTestResourceAdmissionController(t) 541 resp := ac.Admit(ctx, req) 542 543 ExpectFailsWith(t, resp, "must not set") 544 ExpectFailsWith(t, resp, "spec.field") 545 }