...

Source file src/k8s.io/kubernetes/pkg/apis/apiserverinternal/validation/validation_test.go

Documentation: k8s.io/kubernetes/pkg/apis/apiserverinternal/validation

     1  /*
     2  Copyright 2020 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  	"strings"
    21  	"testing"
    22  
    23  	"k8s.io/apimachinery/pkg/util/validation/field"
    24  	"k8s.io/kubernetes/pkg/apis/apiserverinternal"
    25  	"k8s.io/utils/pointer"
    26  )
    27  
    28  func TestValidateServerStorageVersion(t *testing.T) {
    29  	cases := []struct {
    30  		ssv         apiserverinternal.ServerStorageVersion
    31  		expectedErr string
    32  	}{{
    33  		ssv: apiserverinternal.ServerStorageVersion{
    34  			APIServerID:       "-fea",
    35  			EncodingVersion:   "v1alpha1",
    36  			DecodableVersions: []string{"v1alpha1", "v1"},
    37  			ServedVersions:    []string{"v1alpha1", "v1"},
    38  		},
    39  		expectedErr: "apiServerID: Invalid value",
    40  	}, {
    41  		ssv: apiserverinternal.ServerStorageVersion{
    42  			APIServerID:       "fea",
    43  			EncodingVersion:   "v1alpha1",
    44  			DecodableVersions: []string{"v1beta1", "v1"},
    45  			ServedVersions:    []string{"v1beta1", "v1"},
    46  		},
    47  		expectedErr: "decodableVersions must include encodingVersion",
    48  	}, {
    49  		ssv: apiserverinternal.ServerStorageVersion{
    50  			APIServerID:       "fea",
    51  			EncodingVersion:   "v1alpha1",
    52  			DecodableVersions: []string{"v1alpha1", "v1", "-fea"},
    53  			ServedVersions:    []string{"v1alpha1", "v1", "-fea"},
    54  		},
    55  		expectedErr: "decodableVersions[2]: Invalid value",
    56  	}, {
    57  		ssv: apiserverinternal.ServerStorageVersion{
    58  			APIServerID:       "fea",
    59  			EncodingVersion:   "v1alpha1",
    60  			DecodableVersions: []string{"v1alpha1", "v1"},
    61  			ServedVersions:    []string{"v1alpha1", "v1"},
    62  		},
    63  		expectedErr: "",
    64  	}, {
    65  		ssv: apiserverinternal.ServerStorageVersion{
    66  			APIServerID:       "fea",
    67  			EncodingVersion:   "v1alpha1",
    68  			DecodableVersions: []string{"v1alpha1", "v1"},
    69  			ServedVersions:    []string{"v1alpha1", "v1"},
    70  		},
    71  		expectedErr: "",
    72  	}, {
    73  		ssv: apiserverinternal.ServerStorageVersion{
    74  			APIServerID:       "fea",
    75  			EncodingVersion:   "mygroup.com/v2",
    76  			DecodableVersions: []string{"v1alpha1", "v1", "mygroup.com/v2"},
    77  			ServedVersions:    []string{"v1alpha1", "v1", "mygroup.com/v2"},
    78  		},
    79  		expectedErr: "",
    80  	}, {
    81  		ssv: apiserverinternal.ServerStorageVersion{
    82  			APIServerID:       "fea",
    83  			EncodingVersion:   "v1alpha1",
    84  			DecodableVersions: []string{"v1alpha1", "v1"},
    85  			ServedVersions:    []string{"/v3"},
    86  		},
    87  		expectedErr: `[].servedVersions[0]: Invalid value: "/v3": group part: must be non-empty`,
    88  	}, {
    89  		ssv: apiserverinternal.ServerStorageVersion{
    90  			APIServerID:       "fea",
    91  			EncodingVersion:   "mygroup.com/v2",
    92  			DecodableVersions: []string{"mygroup.com/v2", "/v3"},
    93  			ServedVersions:    []string{"mygroup.com/v2", "/v3"},
    94  		},
    95  		expectedErr: `[].decodableVersions[1]: Invalid value: "/v3": group part: must be non-empty`,
    96  	}, {
    97  		ssv: apiserverinternal.ServerStorageVersion{
    98  			APIServerID:       "fea",
    99  			EncodingVersion:   "mygroup.com/v2",
   100  			DecodableVersions: []string{"mygroup.com/v2", "/v3"},
   101  			ServedVersions:    []string{"mygroup.com/"},
   102  		},
   103  		expectedErr: `[].servedVersions[0]: Invalid value: "mygroup.com/": version part: must be non-empty`,
   104  	}, {
   105  		ssv: apiserverinternal.ServerStorageVersion{
   106  			APIServerID:       "fea",
   107  			EncodingVersion:   "mygroup.com/v2",
   108  			DecodableVersions: []string{"mygroup.com/v2", "mygroup.com/"},
   109  			ServedVersions:    []string{"mygroup.com/v2", "mygroup.com/"},
   110  		},
   111  		expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/": version part: must be non-empty`,
   112  	}, {
   113  		ssv: apiserverinternal.ServerStorageVersion{
   114  			APIServerID:       "fea",
   115  			EncodingVersion:   "/v3",
   116  			DecodableVersions: []string{"mygroup.com/v2", "/v3"},
   117  			ServedVersions:    []string{"mygroup.com/v2", "/v3"},
   118  		},
   119  		expectedErr: `[].encodingVersion: Invalid value: "/v3": group part: must be non-empty`,
   120  	}, {
   121  		ssv: apiserverinternal.ServerStorageVersion{
   122  			APIServerID:       "fea",
   123  			EncodingVersion:   "v1",
   124  			DecodableVersions: []string{"v1", "mygroup_com/v2"},
   125  			ServedVersions:    []string{"v1", "mygroup_com/v2"},
   126  		},
   127  		expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup_com/v2": group part: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`,
   128  	}, {
   129  		ssv: apiserverinternal.ServerStorageVersion{
   130  			APIServerID:       "fea",
   131  			EncodingVersion:   "v1",
   132  			DecodableVersions: []string{"v1", "mygroup.com/v2"},
   133  			ServedVersions:    []string{"v1", "mygroup_com/v2"},
   134  		},
   135  		expectedErr: `[].servedVersions[1]: Invalid value: "mygroup_com/v2": group part: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`,
   136  	}, {
   137  		ssv: apiserverinternal.ServerStorageVersion{
   138  			APIServerID:       "fea",
   139  			EncodingVersion:   "v1",
   140  			DecodableVersions: []string{"v1", "mygroup.com/v2_"},
   141  			ServedVersions:    []string{"v1", "mygroup.com/v2_"},
   142  		},
   143  		expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/v2_": version part: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',  or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')`,
   144  	}, {
   145  		ssv: apiserverinternal.ServerStorageVersion{
   146  			APIServerID:       "fea",
   147  			EncodingVersion:   "v1",
   148  			DecodableVersions: []string{"v1", "mygroup.com/v2"},
   149  			ServedVersions:    []string{"v1", "mygroup.com/v2_"},
   150  		},
   151  		expectedErr: `[].servedVersions[1]: Invalid value: "mygroup.com/v2_": version part: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',  or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')`,
   152  	}, {
   153  		ssv: apiserverinternal.ServerStorageVersion{
   154  			APIServerID:       "fea",
   155  			EncodingVersion:   "v1",
   156  			DecodableVersions: []string{"v1", "mygroup.com/v2/myresource"},
   157  			ServedVersions:    []string{"v1", "mygroup.com/v2/myresource"},
   158  		},
   159  		expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/v2/myresource": an apiVersion is a DNS-1035 label, which must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',  or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyVersion')`,
   160  	}, {
   161  		ssv: apiserverinternal.ServerStorageVersion{
   162  			APIServerID:       "fea",
   163  			EncodingVersion:   "v1",
   164  			DecodableVersions: []string{"v1", "mygroup.com/v2"},
   165  			ServedVersions:    []string{"v1", "mygroup.com/v2/myresource"},
   166  		},
   167  		expectedErr: `[].servedVersions[1]: Invalid value: "mygroup.com/v2/myresource": an apiVersion is a DNS-1035 label, which must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',  or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyVersion')`,
   168  	}, {
   169  		ssv: apiserverinternal.ServerStorageVersion{
   170  			APIServerID:       "fea",
   171  			EncodingVersion:   "v1alpha1",
   172  			DecodableVersions: []string{"v1alpha1", "v1"},
   173  			ServedVersions:    []string{"v2"},
   174  		},
   175  		expectedErr: `[].servedVersions[0]: Invalid value: "v2": individual served version : v2 must be included in decodableVersions : [v1alpha1 v1]`,
   176  	}}
   177  
   178  	for _, tc := range cases {
   179  		err := validateServerStorageVersion(tc.ssv, field.NewPath("")).ToAggregate()
   180  		if err == nil && len(tc.expectedErr) == 0 {
   181  			continue
   182  		}
   183  		if err != nil && len(tc.expectedErr) == 0 {
   184  			t.Errorf("unexpected error %v", err)
   185  			continue
   186  		}
   187  		if err == nil && len(tc.expectedErr) != 0 {
   188  			t.Errorf("unexpected empty error")
   189  			continue
   190  		}
   191  		if !strings.Contains(err.Error(), tc.expectedErr) {
   192  			t.Errorf("expected error to contain %s, got %s", tc.expectedErr, err)
   193  		}
   194  	}
   195  }
   196  
   197  func TestValidateStorageVersionStatus(t *testing.T) {
   198  	cases := []struct {
   199  		svs         apiserverinternal.StorageVersionStatus
   200  		expectedErr string
   201  	}{{
   202  		svs: apiserverinternal.StorageVersionStatus{
   203  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   204  				APIServerID:       "1",
   205  				EncodingVersion:   "v1alpha1",
   206  				DecodableVersions: []string{"v1alpha1", "v1"},
   207  			}, {
   208  				APIServerID:       "2",
   209  				EncodingVersion:   "v1alpha1",
   210  				DecodableVersions: []string{"v1alpha1", "v1"},
   211  			}},
   212  			CommonEncodingVersion: pointer.String("v1alpha1"),
   213  		},
   214  		expectedErr: "",
   215  	}, {
   216  		svs: apiserverinternal.StorageVersionStatus{
   217  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   218  				APIServerID:       "1",
   219  				EncodingVersion:   "v1alpha1",
   220  				DecodableVersions: []string{"v1alpha1", "v1"},
   221  			}, {
   222  				APIServerID:       "1",
   223  				EncodingVersion:   "v1beta1",
   224  				DecodableVersions: []string{"v1alpha1", "v1"},
   225  			}},
   226  			CommonEncodingVersion: pointer.String("v1alpha1"),
   227  		},
   228  		expectedErr: "storageVersions[1].apiServerID: Duplicate value: \"1\"",
   229  	}}
   230  
   231  	for _, tc := range cases {
   232  		err := validateStorageVersionStatus(tc.svs, field.NewPath("")).ToAggregate()
   233  		if err == nil && len(tc.expectedErr) == 0 {
   234  			continue
   235  		}
   236  		if err != nil && len(tc.expectedErr) == 0 {
   237  			t.Errorf("unexpected error %v", err)
   238  			continue
   239  		}
   240  		if err == nil && len(tc.expectedErr) != 0 {
   241  			t.Errorf("unexpected empty error")
   242  			continue
   243  		}
   244  		if !strings.Contains(err.Error(), tc.expectedErr) {
   245  			t.Errorf("expected error to contain %s, got %s", tc.expectedErr, err)
   246  		}
   247  	}
   248  }
   249  
   250  func TestValidateCommonVersion(t *testing.T) {
   251  	cases := []struct {
   252  		status      apiserverinternal.StorageVersionStatus
   253  		expectedErr string
   254  	}{{
   255  		status: apiserverinternal.StorageVersionStatus{
   256  			StorageVersions:       []apiserverinternal.ServerStorageVersion{},
   257  			CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
   258  		},
   259  		expectedErr: "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet",
   260  	}, {
   261  		status: apiserverinternal.StorageVersionStatus{
   262  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   263  				APIServerID:     "1",
   264  				EncodingVersion: "v1alpha1",
   265  			}, {
   266  				APIServerID:     "2",
   267  				EncodingVersion: "v1",
   268  			}},
   269  			CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
   270  		},
   271  		expectedErr: "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet",
   272  	}, {
   273  		status: apiserverinternal.StorageVersionStatus{
   274  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   275  				APIServerID:     "1",
   276  				EncodingVersion: "v1alpha1",
   277  			}, {
   278  				APIServerID:     "2",
   279  				EncodingVersion: "v1alpha1",
   280  			}},
   281  			CommonEncodingVersion: nil,
   282  		},
   283  		expectedErr: "Invalid value: \"null\": the common encoding version is v1alpha1",
   284  	}, {
   285  		status: apiserverinternal.StorageVersionStatus{
   286  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   287  				APIServerID:     "1",
   288  				EncodingVersion: "v1alpha1",
   289  			}, {
   290  				APIServerID:     "2",
   291  				EncodingVersion: "v1alpha1",
   292  			}},
   293  			CommonEncodingVersion: func() *string { a := "v1"; return &a }(),
   294  		},
   295  		expectedErr: "Invalid value: \"v1\": the actual common encoding version is v1alpha1",
   296  	}, {
   297  		status: apiserverinternal.StorageVersionStatus{
   298  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   299  				APIServerID:     "1",
   300  				EncodingVersion: "v1alpha1",
   301  			}, {
   302  				APIServerID:     "2",
   303  				EncodingVersion: "v1alpha1",
   304  			}},
   305  			CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
   306  		},
   307  		expectedErr: "",
   308  	}, {
   309  		status: apiserverinternal.StorageVersionStatus{
   310  			StorageVersions: []apiserverinternal.ServerStorageVersion{{
   311  				APIServerID:     "1",
   312  				EncodingVersion: "v1alpha1",
   313  			}},
   314  			CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
   315  		},
   316  		expectedErr: "",
   317  	}}
   318  	for _, tc := range cases {
   319  		err := validateCommonVersion(tc.status, field.NewPath(""))
   320  		if err == nil && len(tc.expectedErr) == 0 {
   321  			continue
   322  		}
   323  		if err != nil && len(tc.expectedErr) == 0 {
   324  			t.Errorf("unexpected error %v", err)
   325  			continue
   326  		}
   327  		if err == nil && len(tc.expectedErr) != 0 {
   328  			t.Errorf("unexpected empty error")
   329  			continue
   330  		}
   331  		if !strings.Contains(err.Error(), tc.expectedErr) {
   332  			t.Errorf("expected error to contain %s, got %s", tc.expectedErr, err)
   333  		}
   334  	}
   335  }
   336  
   337  func TestValidateStorageVersionCondition(t *testing.T) {
   338  	cases := []struct {
   339  		conditions  []apiserverinternal.StorageVersionCondition
   340  		expectedErr string
   341  	}{{
   342  		conditions: []apiserverinternal.StorageVersionCondition{{
   343  			Type:    "-fea",
   344  			Status:  "True",
   345  			Reason:  "unknown",
   346  			Message: "unknown",
   347  		}},
   348  		expectedErr: "type: Invalid value",
   349  	}, {
   350  		conditions: []apiserverinternal.StorageVersionCondition{{
   351  			Type:    "fea",
   352  			Status:  "-True",
   353  			Reason:  "unknown",
   354  			Message: "unknown",
   355  		}},
   356  		expectedErr: "status: Invalid value",
   357  	}, {
   358  		conditions: []apiserverinternal.StorageVersionCondition{{
   359  			Type:    "fea",
   360  			Status:  "True",
   361  			Message: "unknown",
   362  		}},
   363  		expectedErr: "Required value: reason cannot be empty",
   364  	}, {
   365  		conditions: []apiserverinternal.StorageVersionCondition{{
   366  			Type:   "fea",
   367  			Status: "True",
   368  			Reason: "unknown",
   369  		}},
   370  		expectedErr: "Required value: message cannot be empty",
   371  	}, {
   372  		conditions: []apiserverinternal.StorageVersionCondition{{
   373  			Type:    "fea",
   374  			Status:  "True",
   375  			Reason:  "unknown",
   376  			Message: "unknown",
   377  		}, {
   378  			Type:    "fea",
   379  			Status:  "True",
   380  			Reason:  "unknown",
   381  			Message: "unknown",
   382  		}},
   383  		expectedErr: `"fea": the type of the condition is not unique, it also appears in conditions[0]`,
   384  	}, {
   385  		conditions: []apiserverinternal.StorageVersionCondition{{
   386  			Type:    "fea",
   387  			Status:  "True",
   388  			Reason:  "unknown",
   389  			Message: "unknown",
   390  		}},
   391  		expectedErr: "",
   392  	}}
   393  	for _, tc := range cases {
   394  		err := validateStorageVersionCondition(tc.conditions, field.NewPath("")).ToAggregate()
   395  		if err == nil && len(tc.expectedErr) == 0 {
   396  			continue
   397  		}
   398  		if err != nil && len(tc.expectedErr) == 0 {
   399  			t.Errorf("unexpected error %v", err)
   400  			continue
   401  		}
   402  		if err == nil && len(tc.expectedErr) != 0 {
   403  			t.Errorf("unexpected empty error")
   404  			continue
   405  		}
   406  		if !strings.Contains(err.Error(), tc.expectedErr) {
   407  			t.Errorf("expected error to contain %s, got %s", tc.expectedErr, err)
   408  		}
   409  	}
   410  }
   411  
   412  func TestValidateStorageVersionName(t *testing.T) {
   413  	cases := []struct {
   414  		name        string
   415  		expectedErr string
   416  	}{{
   417  		name:        "",
   418  		expectedErr: `name must be in the form of <group>.<resource>`,
   419  	}, {
   420  		name:        "pods",
   421  		expectedErr: `name must be in the form of <group>.<resource>`,
   422  	}, {
   423  		name:        "core.pods",
   424  		expectedErr: "",
   425  	}, {
   426  		name:        "authentication.k8s.io.tokenreviews",
   427  		expectedErr: "",
   428  	}, {
   429  		name:        strings.Repeat("x", 253) + ".tokenreviews",
   430  		expectedErr: "",
   431  	}, {
   432  		name:        strings.Repeat("x", 254) + ".tokenreviews",
   433  		expectedErr: `the group segment must be no more than 253 characters`,
   434  	}, {
   435  		name:        "authentication.k8s.io." + strings.Repeat("x", 63),
   436  		expectedErr: "",
   437  	}, {
   438  		name:        "authentication.k8s.io." + strings.Repeat("x", 64),
   439  		expectedErr: `the resource segment must be no more than 63 characters`,
   440  	}}
   441  	for _, tc := range cases {
   442  		errs := ValidateStorageVersionName(tc.name, false)
   443  		if errs == nil && len(tc.expectedErr) == 0 {
   444  			continue
   445  		}
   446  		if errs != nil && len(tc.expectedErr) == 0 {
   447  			t.Errorf("unexpected error %v", errs)
   448  			continue
   449  		}
   450  		if errs == nil && len(tc.expectedErr) != 0 {
   451  			t.Errorf("unexpected empty error")
   452  			continue
   453  		}
   454  		found := false
   455  		for _, msg := range errs {
   456  			if msg == tc.expectedErr {
   457  				found = true
   458  			}
   459  		}
   460  		if !found {
   461  			t.Errorf("expected error to contain %s, got %v", tc.expectedErr, errs)
   462  		}
   463  	}
   464  }
   465  

View as plain text