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  }