k8s.io/kubernetes@v1.29.3/pkg/api/testing/defaulting_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 testing
    18  
    19  import (
    20  	"math/rand"
    21  	"reflect"
    22  	"sort"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	fuzz "github.com/google/gofuzz"
    27  
    28  	apiv1 "k8s.io/api/core/v1"
    29  	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
    30  	"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/runtime/schema"
    34  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    35  )
    36  
    37  type orderedGroupVersionKinds []schema.GroupVersionKind
    38  
    39  func (o orderedGroupVersionKinds) Len() int      { return len(o) }
    40  func (o orderedGroupVersionKinds) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
    41  func (o orderedGroupVersionKinds) Less(i, j int) bool {
    42  	return o[i].String() < o[j].String()
    43  }
    44  
    45  // TODO: add a reflexive test that verifies that all SetDefaults functions are registered
    46  func TestDefaulting(t *testing.T) {
    47  	// these are the known types with defaulters - you must add to this list if you add a top level defaulter
    48  	typesWithDefaulting := map[schema.GroupVersionKind]struct{}{
    49  		{Group: "", Version: "v1", Kind: "ConfigMap"}:                                                              {},
    50  		{Group: "", Version: "v1", Kind: "ConfigMapList"}:                                                          {},
    51  		{Group: "", Version: "v1", Kind: "Endpoints"}:                                                              {},
    52  		{Group: "", Version: "v1", Kind: "EndpointsList"}:                                                          {},
    53  		{Group: "", Version: "v1", Kind: "EphemeralContainers"}:                                                    {},
    54  		{Group: "", Version: "v1", Kind: "Namespace"}:                                                              {},
    55  		{Group: "", Version: "v1", Kind: "NamespaceList"}:                                                          {},
    56  		{Group: "", Version: "v1", Kind: "Node"}:                                                                   {},
    57  		{Group: "", Version: "v1", Kind: "NodeList"}:                                                               {},
    58  		{Group: "", Version: "v1", Kind: "PersistentVolume"}:                                                       {},
    59  		{Group: "", Version: "v1", Kind: "PersistentVolumeList"}:                                                   {},
    60  		{Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}:                                                  {},
    61  		{Group: "", Version: "v1", Kind: "PersistentVolumeClaimList"}:                                              {},
    62  		{Group: "", Version: "v1", Kind: "Pod"}:                                                                    {},
    63  		{Group: "", Version: "v1", Kind: "PodList"}:                                                                {},
    64  		{Group: "", Version: "v1", Kind: "PodTemplate"}:                                                            {},
    65  		{Group: "", Version: "v1", Kind: "PodTemplateList"}:                                                        {},
    66  		{Group: "", Version: "v1", Kind: "ReplicationController"}:                                                  {},
    67  		{Group: "", Version: "v1", Kind: "ReplicationControllerList"}:                                              {},
    68  		{Group: "", Version: "v1", Kind: "Secret"}:                                                                 {},
    69  		{Group: "", Version: "v1", Kind: "SecretList"}:                                                             {},
    70  		{Group: "", Version: "v1", Kind: "Service"}:                                                                {},
    71  		{Group: "", Version: "v1", Kind: "ServiceList"}:                                                            {},
    72  		{Group: "apps", Version: "v1beta1", Kind: "StatefulSet"}:                                                   {},
    73  		{Group: "apps", Version: "v1beta1", Kind: "StatefulSetList"}:                                               {},
    74  		{Group: "apps", Version: "v1beta2", Kind: "StatefulSet"}:                                                   {},
    75  		{Group: "apps", Version: "v1beta2", Kind: "StatefulSetList"}:                                               {},
    76  		{Group: "apps", Version: "v1", Kind: "StatefulSet"}:                                                        {},
    77  		{Group: "apps", Version: "v1", Kind: "StatefulSetList"}:                                                    {},
    78  		{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}:                                     {},
    79  		{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}:                                 {},
    80  		{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscaler"}:                                     {},
    81  		{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscalerList"}:                                 {},
    82  		{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscaler"}:                                {},
    83  		{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscalerList"}:                            {},
    84  		{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscaler"}:                                {},
    85  		{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscalerList"}:                            {},
    86  		{Group: "batch", Version: "v1", Kind: "CronJob"}:                                                           {},
    87  		{Group: "batch", Version: "v1", Kind: "CronJobList"}:                                                       {},
    88  		{Group: "batch", Version: "v1", Kind: "Job"}:                                                               {},
    89  		{Group: "batch", Version: "v1", Kind: "JobList"}:                                                           {},
    90  		{Group: "batch", Version: "v1beta1", Kind: "CronJob"}:                                                      {},
    91  		{Group: "batch", Version: "v1beta1", Kind: "CronJobList"}:                                                  {},
    92  		{Group: "batch", Version: "v1beta1", Kind: "JobTemplate"}:                                                  {},
    93  		{Group: "batch", Version: "v2alpha1", Kind: "CronJob"}:                                                     {},
    94  		{Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}:                                                 {},
    95  		{Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}:                                                 {},
    96  		{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}:                      {},
    97  		{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}:                  {},
    98  		{Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSlice"}:                                          {},
    99  		{Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSliceList"}:                                      {},
   100  		{Group: "discovery.k8s.io", Version: "v1beta1", Kind: "EndpointSlice"}:                                     {},
   101  		{Group: "discovery.k8s.io", Version: "v1beta1", Kind: "EndpointSliceList"}:                                 {},
   102  		{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}:                                               {},
   103  		{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}:                                           {},
   104  		{Group: "apps", Version: "v1beta2", Kind: "DaemonSet"}:                                                     {},
   105  		{Group: "apps", Version: "v1beta2", Kind: "DaemonSetList"}:                                                 {},
   106  		{Group: "apps", Version: "v1", Kind: "DaemonSet"}:                                                          {},
   107  		{Group: "apps", Version: "v1", Kind: "DaemonSetList"}:                                                      {},
   108  		{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}:                                              {},
   109  		{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}:                                          {},
   110  		{Group: "apps", Version: "v1beta1", Kind: "Deployment"}:                                                    {},
   111  		{Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}:                                                {},
   112  		{Group: "apps", Version: "v1beta2", Kind: "Deployment"}:                                                    {},
   113  		{Group: "apps", Version: "v1beta2", Kind: "DeploymentList"}:                                                {},
   114  		{Group: "apps", Version: "v1", Kind: "Deployment"}:                                                         {},
   115  		{Group: "apps", Version: "v1", Kind: "DeploymentList"}:                                                     {},
   116  		{Group: "extensions", Version: "v1beta1", Kind: "Ingress"}:                                                 {},
   117  		{Group: "extensions", Version: "v1beta1", Kind: "IngressList"}:                                             {},
   118  		{Group: "apps", Version: "v1beta2", Kind: "ReplicaSet"}:                                                    {},
   119  		{Group: "apps", Version: "v1beta2", Kind: "ReplicaSetList"}:                                                {},
   120  		{Group: "apps", Version: "v1", Kind: "ReplicaSet"}:                                                         {},
   121  		{Group: "apps", Version: "v1", Kind: "ReplicaSetList"}:                                                     {},
   122  		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}:                                              {},
   123  		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}:                                          {},
   124  		{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}:                                           {},
   125  		{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicyList"}:                                       {},
   126  		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}:                      {},
   127  		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}:                  {},
   128  		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}:                             {},
   129  		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}:                         {},
   130  		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}:                       {},
   131  		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}:                   {},
   132  		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}:                              {},
   133  		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}:                          {},
   134  		{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}:                            {},
   135  		{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBindingList"}:                        {},
   136  		{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}:                                   {},
   137  		{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBindingList"}:                               {},
   138  		{Group: "resource.k8s.io", Version: "v1alpha2", Kind: "ResourceClaim"}:                                     {},
   139  		{Group: "resource.k8s.io", Version: "v1alpha2", Kind: "ResourceClaimList"}:                                 {},
   140  		{Group: "resource.k8s.io", Version: "v1alpha2", Kind: "ResourceClaimTemplate"}:                             {},
   141  		{Group: "resource.k8s.io", Version: "v1alpha2", Kind: "ResourceClaimTemplateList"}:                         {},
   142  		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicy"}:            {},
   143  		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyList"}:        {},
   144  		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyBinding"}:     {},
   145  		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyBindingList"}: {},
   146  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfiguration"}:        {},
   147  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfigurationList"}:    {},
   148  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfiguration"}:          {},
   149  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfigurationList"}:      {},
   150  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicy"}:             {},
   151  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyList"}:         {},
   152  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyBinding"}:      {},
   153  		{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyBindingList"}:  {},
   154  		{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfiguration"}:             {},
   155  		{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfigurationList"}:         {},
   156  		{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration"}:               {},
   157  		{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfigurationList"}:           {},
   158  		{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}:                                         {},
   159  		{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}:                                     {},
   160  		{Group: "networking.k8s.io", Version: "v1beta1", Kind: "Ingress"}:                                          {},
   161  		{Group: "networking.k8s.io", Version: "v1beta1", Kind: "IngressList"}:                                      {},
   162  		{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClass"}:                                          {},
   163  		{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClassList"}:                                      {},
   164  		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}:                                        {},
   165  		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClassList"}:                                    {},
   166  		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "CSIDriver"}:                                           {},
   167  		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "CSIDriverList"}:                                       {},
   168  		{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}:                                             {},
   169  		{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClassList"}:                                         {},
   170  		{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"}:                                         {},
   171  		{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachmentList"}:                                     {},
   172  		{Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriver"}:                                                {},
   173  		{Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriverList"}:                                            {},
   174  		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "VolumeAttachment"}:                                    {},
   175  		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "VolumeAttachmentList"}:                                {},
   176  		{Group: "authentication.k8s.io", Version: "v1", Kind: "TokenRequest"}:                                      {},
   177  		{Group: "scheduling.k8s.io", Version: "v1alpha1", Kind: "PriorityClass"}:                                   {},
   178  		{Group: "scheduling.k8s.io", Version: "v1beta1", Kind: "PriorityClass"}:                                    {},
   179  		{Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClass"}:                                         {},
   180  		{Group: "scheduling.k8s.io", Version: "v1alpha1", Kind: "PriorityClassList"}:                               {},
   181  		{Group: "scheduling.k8s.io", Version: "v1beta1", Kind: "PriorityClassList"}:                                {},
   182  		{Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClassList"}:                                     {},
   183  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1alpha1", Kind: "PriorityLevelConfiguration"}:           {},
   184  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1alpha1", Kind: "PriorityLevelConfigurationList"}:       {},
   185  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1", Kind: "PriorityLevelConfiguration"}:            {},
   186  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1", Kind: "PriorityLevelConfigurationList"}:        {},
   187  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "PriorityLevelConfiguration"}:            {},
   188  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "PriorityLevelConfigurationList"}:        {},
   189  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta3", Kind: "PriorityLevelConfiguration"}:            {},
   190  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta3", Kind: "PriorityLevelConfigurationList"}:        {},
   191  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1", Kind: "PriorityLevelConfiguration"}:                 {},
   192  		{Group: "flowcontrol.apiserver.k8s.io", Version: "v1", Kind: "PriorityLevelConfigurationList"}:             {},
   193  	}
   194  
   195  	f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
   196  	f.Funcs(
   197  		func(s *runtime.RawExtension, c fuzz.Continue) {},
   198  		func(s *metav1.LabelSelector, c fuzz.Continue) {
   199  			c.FuzzNoCustom(s)
   200  			s.MatchExpressions = nil // need to fuzz this specially
   201  		},
   202  		func(s *metav1.ListOptions, c fuzz.Continue) {
   203  			c.FuzzNoCustom(s)
   204  			s.LabelSelector = "" // need to fuzz requirement strings specially
   205  			s.FieldSelector = "" // need to fuzz requirement strings specially
   206  		},
   207  		func(s *extensionsv1beta1.ScaleStatus, c fuzz.Continue) {
   208  			c.FuzzNoCustom(s)
   209  			s.TargetSelector = "" // need to fuzz requirement strings specially
   210  		},
   211  	)
   212  
   213  	scheme := legacyscheme.Scheme
   214  	var testTypes orderedGroupVersionKinds
   215  	for gvk := range scheme.AllKnownTypes() {
   216  		if gvk.Version == runtime.APIVersionInternal {
   217  			continue
   218  		}
   219  		testTypes = append(testTypes, gvk)
   220  	}
   221  	sort.Sort(testTypes)
   222  
   223  	for _, gvk := range testTypes {
   224  		_, expectedChanged := typesWithDefaulting[gvk]
   225  		iter := 0
   226  		changedOnce := false
   227  		for {
   228  			if iter > *roundtrip.FuzzIters {
   229  				if !expectedChanged || changedOnce {
   230  					break
   231  				}
   232  				if iter > 300 {
   233  					t.Errorf("expected %s to trigger defaulting due to fuzzing", gvk)
   234  					break
   235  				}
   236  				// if we expected defaulting, continue looping until the fuzzer gives us one
   237  				// at worst, we will timeout
   238  			}
   239  			iter++
   240  
   241  			src, err := scheme.New(gvk)
   242  			if err != nil {
   243  				t.Fatal(err)
   244  			}
   245  			f.Fuzz(src)
   246  
   247  			src.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
   248  
   249  			original := src.DeepCopyObject()
   250  
   251  			// get internal
   252  			withDefaults := src.DeepCopyObject()
   253  			scheme.Default(withDefaults.(runtime.Object))
   254  
   255  			if !reflect.DeepEqual(original, withDefaults) {
   256  				changedOnce = true
   257  				if !expectedChanged {
   258  					t.Errorf("{Group: \"%s\", Version: \"%s\", Kind: \"%s\"} did not expect defaults to be set - update expected or check defaulter registering: %s", gvk.Group, gvk.Version, gvk.Kind, cmp.Diff(original, withDefaults))
   259  				}
   260  			}
   261  		}
   262  	}
   263  }
   264  
   265  func BenchmarkPodDefaulting(b *testing.B) {
   266  	f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
   267  	items := make([]apiv1.Pod, 100)
   268  	for i := range items {
   269  		f.Fuzz(&items[i])
   270  	}
   271  
   272  	scheme := legacyscheme.Scheme
   273  	b.ResetTimer()
   274  	for i := 0; i < b.N; i++ {
   275  		pod := &items[i%len(items)]
   276  
   277  		scheme.Default(pod)
   278  	}
   279  	b.StopTimer()
   280  }