| /* |
| Copyright 2016 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package testing |
| |
| import ( |
| "math/rand" |
| "reflect" |
| "sort" |
| "testing" |
| |
| fuzz "github.com/google/gofuzz" |
| |
| apiv1 "k8s.io/api/core/v1" |
| extensionsv1beta1 "k8s.io/api/extensions/v1beta1" |
| roundtrip "k8s.io/apimachinery/pkg/api/apitesting/roundtrip" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/runtime/schema" |
| "k8s.io/apimachinery/pkg/util/diff" |
| "k8s.io/kubernetes/pkg/api/legacyscheme" |
| ) |
| |
| type orderedGroupVersionKinds []schema.GroupVersionKind |
| |
| func (o orderedGroupVersionKinds) Len() int { return len(o) } |
| func (o orderedGroupVersionKinds) Swap(i, j int) { o[i], o[j] = o[j], o[i] } |
| func (o orderedGroupVersionKinds) Less(i, j int) bool { |
| return o[i].String() < o[j].String() |
| } |
| |
| // TODO: add a reflexive test that verifies that all SetDefaults functions are registered |
| func TestDefaulting(t *testing.T) { |
| // these are the known types with defaulters - you must add to this list if you add a top level defaulter |
| typesWithDefaulting := map[schema.GroupVersionKind]struct{}{ |
| {Group: "", Version: "v1", Kind: "ConfigMap"}: {}, |
| {Group: "", Version: "v1", Kind: "ConfigMapList"}: {}, |
| {Group: "", Version: "v1", Kind: "Endpoints"}: {}, |
| {Group: "", Version: "v1", Kind: "EndpointsList"}: {}, |
| {Group: "", Version: "v1", Kind: "Namespace"}: {}, |
| {Group: "", Version: "v1", Kind: "NamespaceList"}: {}, |
| {Group: "", Version: "v1", Kind: "Node"}: {}, |
| {Group: "", Version: "v1", Kind: "NodeList"}: {}, |
| {Group: "", Version: "v1", Kind: "PersistentVolume"}: {}, |
| {Group: "", Version: "v1", Kind: "PersistentVolumeList"}: {}, |
| {Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}: {}, |
| {Group: "", Version: "v1", Kind: "PersistentVolumeClaimList"}: {}, |
| {Group: "", Version: "v1", Kind: "Pod"}: {}, |
| {Group: "", Version: "v1", Kind: "PodList"}: {}, |
| {Group: "", Version: "v1", Kind: "PodTemplate"}: {}, |
| {Group: "", Version: "v1", Kind: "PodTemplateList"}: {}, |
| {Group: "", Version: "v1", Kind: "ReplicationController"}: {}, |
| {Group: "", Version: "v1", Kind: "ReplicationControllerList"}: {}, |
| {Group: "", Version: "v1", Kind: "Secret"}: {}, |
| {Group: "", Version: "v1", Kind: "SecretList"}: {}, |
| {Group: "", Version: "v1", Kind: "Service"}: {}, |
| {Group: "", Version: "v1", Kind: "ServiceList"}: {}, |
| {Group: "apps", Version: "v1beta1", Kind: "StatefulSet"}: {}, |
| {Group: "apps", Version: "v1beta1", Kind: "StatefulSetList"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "StatefulSet"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "StatefulSetList"}: {}, |
| {Group: "apps", Version: "v1", Kind: "StatefulSet"}: {}, |
| {Group: "apps", Version: "v1", Kind: "StatefulSetList"}: {}, |
| {Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}: {}, |
| {Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {}, |
| {Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscaler"}: {}, |
| {Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscalerList"}: {}, |
| {Group: "batch", Version: "v1", Kind: "Job"}: {}, |
| {Group: "batch", Version: "v1", Kind: "JobList"}: {}, |
| {Group: "batch", Version: "v1beta1", Kind: "CronJob"}: {}, |
| {Group: "batch", Version: "v1beta1", Kind: "CronJobList"}: {}, |
| {Group: "batch", Version: "v1beta1", Kind: "JobTemplate"}: {}, |
| {Group: "batch", Version: "v2alpha1", Kind: "CronJob"}: {}, |
| {Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}: {}, |
| {Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {}, |
| {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}: {}, |
| {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}: {}, |
| {Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "MasterConfiguration"}: {}, |
| // This object contains only int fields which currently breaks the defaulting test because |
| // it's pretty stupid. Once we add non integer fields, we should uncomment this. |
| // {Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "NodeConfiguration"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "DaemonSet"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "DaemonSetList"}: {}, |
| {Group: "apps", Version: "v1", Kind: "DaemonSet"}: {}, |
| {Group: "apps", Version: "v1", Kind: "DaemonSetList"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {}, |
| {Group: "apps", Version: "v1beta1", Kind: "Deployment"}: {}, |
| {Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "Deployment"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "DeploymentList"}: {}, |
| {Group: "apps", Version: "v1", Kind: "Deployment"}: {}, |
| {Group: "apps", Version: "v1", Kind: "DeploymentList"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicy"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicyList"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "ReplicaSet"}: {}, |
| {Group: "apps", Version: "v1beta2", Kind: "ReplicaSetList"}: {}, |
| {Group: "apps", Version: "v1", Kind: "ReplicaSet"}: {}, |
| {Group: "apps", Version: "v1", Kind: "ReplicaSetList"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}: {}, |
| {Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicyList"}: {}, |
| {Group: "policy", Version: "v1beta1", Kind: "PodSecurityPolicy"}: {}, |
| {Group: "policy", Version: "v1beta1", Kind: "PodSecurityPolicyList"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBindingList"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}: {}, |
| {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBindingList"}: {}, |
| {Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPreset"}: {}, |
| {Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}: {}, |
| {Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfiguration"}: {}, |
| {Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfigurationList"}: {}, |
| {Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfiguration"}: {}, |
| {Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfigurationList"}: {}, |
| {Group: "auditregistration.k8s.io", Version: "v1alpha1", Kind: "AuditSink"}: {}, |
| {Group: "auditregistration.k8s.io", Version: "v1alpha1", Kind: "AuditSinkList"}: {}, |
| {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}: {}, |
| {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}: {}, |
| {Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}: {}, |
| {Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClassList"}: {}, |
| {Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}: {}, |
| {Group: "storage.k8s.io", Version: "v1", Kind: "StorageClassList"}: {}, |
| {Group: "authentication.k8s.io", Version: "v1", Kind: "TokenRequest"}: {}, |
| } |
| |
| f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1)) |
| f.Funcs( |
| func(s *runtime.RawExtension, c fuzz.Continue) {}, |
| func(s *metav1.LabelSelector, c fuzz.Continue) { |
| c.FuzzNoCustom(s) |
| s.MatchExpressions = nil // need to fuzz this specially |
| }, |
| func(s *metav1.ListOptions, c fuzz.Continue) { |
| c.FuzzNoCustom(s) |
| s.LabelSelector = "" // need to fuzz requirement strings specially |
| s.FieldSelector = "" // need to fuzz requirement strings specially |
| }, |
| func(s *extensionsv1beta1.ScaleStatus, c fuzz.Continue) { |
| c.FuzzNoCustom(s) |
| s.TargetSelector = "" // need to fuzz requirement strings specially |
| }, |
| ) |
| |
| scheme := legacyscheme.Scheme |
| var testTypes orderedGroupVersionKinds |
| for gvk := range scheme.AllKnownTypes() { |
| if gvk.Version == runtime.APIVersionInternal { |
| continue |
| } |
| testTypes = append(testTypes, gvk) |
| } |
| sort.Sort(testTypes) |
| |
| for _, gvk := range testTypes { |
| _, expectedChanged := typesWithDefaulting[gvk] |
| iter := 0 |
| changedOnce := false |
| for { |
| if iter > *roundtrip.FuzzIters { |
| if !expectedChanged || changedOnce { |
| break |
| } |
| if iter > 300 { |
| t.Errorf("expected %s to trigger defaulting due to fuzzing", gvk) |
| break |
| } |
| // if we expected defaulting, continue looping until the fuzzer gives us one |
| // at worst, we will timeout |
| } |
| iter++ |
| |
| src, err := scheme.New(gvk) |
| if err != nil { |
| t.Fatal(err) |
| } |
| f.Fuzz(src) |
| |
| src.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{}) |
| |
| original := src.DeepCopyObject() |
| |
| // get internal |
| withDefaults := src.DeepCopyObject() |
| scheme.Default(withDefaults.(runtime.Object)) |
| |
| if !reflect.DeepEqual(original, withDefaults) { |
| changedOnce = true |
| if !expectedChanged { |
| 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, diff.ObjectReflectDiff(original, withDefaults)) |
| } |
| } |
| } |
| } |
| } |
| |
| func BenchmarkPodDefaulting(b *testing.B) { |
| f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1)) |
| items := make([]apiv1.Pod, 100) |
| for i := range items { |
| f.Fuzz(&items[i]) |
| } |
| |
| scheme := legacyscheme.Scheme |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| pod := &items[i%len(items)] |
| |
| scheme.Default(pod) |
| } |
| b.StopTimer() |
| } |