Make defaulting part of versioning codec

Most normal codec use should perform defaulting. DirectCodecs should not
perform defaulting. Update the defaulting_test to fuzz the list of known
defaulters. Use the new versioning.NewDefaultingCodec() method.
This commit is contained in:
Clayton Coleman
2016-09-25 12:09:52 -04:00
parent 742fb698d4
commit 1694cfb72d
9 changed files with 191 additions and 48 deletions

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015 The Kubernetes Authors.
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.
@@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/diff"
@@ -40,14 +41,73 @@ 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 TestVerifyDefaulting(t *testing.T) {
job := &batchv2alpha1.JobTemplate{}
batchv2alpha1.SetObjectDefaults_JobTemplate(job)
if job.Template.Spec.Template.Spec.DNSPolicy != apiv1.DNSClusterFirst {
t.Errorf("unexpected defaulting: %#v", job)
}
}
// TODO: once we remove defaulting from conversion, convert this test to ensuring
// that all objects that say they have defaulting are verified to mutate the originating
// object.
// TODO: add a reflexive test that verifies that all SetDefaults functions are registered
func TestDefaulting(t *testing.T) {
f := fuzz.New().NilChance(.5).NumElements(1, 1)
f.RandSource(rand.NewSource(1))
// these are the known types with defaulters - you must add to this list if you add a top level defaulter
typesWithDefaulting := map[unversioned.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: "PodAttachOptions"}: {},
{Group: "", Version: "v1", Kind: "PodExecOptions"}: {},
{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: "v1alpha1", Kind: "PetSet"}: {},
{Group: "apps", Version: "v1alpha1", Kind: "PetSetList"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "batch", Version: "v1", Kind: "Job"}: {},
{Group: "batch", Version: "v1", Kind: "JobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "Job"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "JobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJobList"}: {},
{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeProxyConfiguration"}: {},
{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeSchedulerConfiguration"}: {},
{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeletConfiguration"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Job"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "JobList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {},
{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"}: {},
}
f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
f.Funcs(
func(s *runtime.RawExtension, c fuzz.Continue) {},
func(s *unversioned.LabelSelector, c fuzz.Continue) {
@@ -59,16 +119,6 @@ func TestDefaulting(t *testing.T) {
s.LabelSelector = "" // need to fuzz requirement strings specially
s.FieldSelector = "" // need to fuzz requirement strings specially
},
// No longer necessary when we remove defaulting from conversion
func(s *apiv1.Secret, c fuzz.Continue) {
c.FuzzNoCustom(s)
s.StringData = nil // is mapped into Data, which cannot easily be tested
},
func(s *extensionsv1beta1.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
@@ -86,7 +136,23 @@ func TestDefaulting(t *testing.T) {
sort.Sort(testTypes)
for _, gvk := range testTypes {
for i := 0; i < *fuzzIters; i++ {
_, expectedChanged := typesWithDefaulting[gvk]
iter := 0
changedOnce := false
for {
if iter > *fuzzIters {
if !expectedChanged || changedOnce {
break
}
if iter > 200 {
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)
@@ -95,30 +161,38 @@ func TestDefaulting(t *testing.T) {
src.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
original, _ := scheme.DeepCopy(src)
original, err := scheme.DeepCopy(src)
if err != nil {
t.Fatal(err)
}
// get internal
copied, _ := scheme.DeepCopy(src)
scheme.Default(copied.(runtime.Object))
withDefaults, _ := scheme.DeepCopy(src)
scheme.Default(withDefaults.(runtime.Object))
// get expected
// TODO: this relies on the side effect behavior of defaulters applying to the external
// object
if _, err = scheme.UnsafeConvertToVersion(src, runtime.InternalGroupVersioner); err != nil {
t.Errorf("[%v] unable to convert: %v", gvk, err)
continue
}
src.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
existingChanged := !reflect.DeepEqual(original, src)
newChanged := !reflect.DeepEqual(original, copied)
if existingChanged != newChanged {
t.Errorf("[%v] mismatched changes: old=%t new=%t", gvk, existingChanged, newChanged)
}
if !reflect.DeepEqual(src, copied) {
t.Errorf("[%v] changed: %s", gvk, diff.ObjectReflectDiff(src, copied))
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 := api.Scheme
b.ResetTimer()
for i := 0; i < b.N; i++ {
pod := &items[i%len(items)]
scheme.Default(pod)
}
b.StopTimer()
}