github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/fuzzer.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors All rights reserved.
     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  	"fmt"
    21  	"math/rand"
    22  	"reflect"
    23  	"strconv"
    24  	"testing"
    25  
    26  	docker "github.com/fsouza/go-dockerclient"
    27  	"k8s.io/kubernetes/pkg/api"
    28  	"k8s.io/kubernetes/pkg/api/registered"
    29  	"k8s.io/kubernetes/pkg/api/resource"
    30  	"k8s.io/kubernetes/pkg/api/unversioned"
    31  	"k8s.io/kubernetes/pkg/apis/extensions"
    32  	"k8s.io/kubernetes/pkg/fields"
    33  	"k8s.io/kubernetes/pkg/labels"
    34  	"k8s.io/kubernetes/pkg/runtime"
    35  	"k8s.io/kubernetes/pkg/types"
    36  	"k8s.io/kubernetes/pkg/util/intstr"
    37  
    38  	"github.com/google/gofuzz"
    39  )
    40  
    41  // FuzzerFor can randomly populate api objects that are destined for version.
    42  func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
    43  	f := fuzz.New().NilChance(.5).NumElements(1, 1)
    44  	if src != nil {
    45  		f.RandSource(src)
    46  	}
    47  	f.Funcs(
    48  		func(j *runtime.PluginBase, c fuzz.Continue) {
    49  			// Do nothing; this struct has only a Kind field and it must stay blank in memory.
    50  		},
    51  		func(j *runtime.TypeMeta, c fuzz.Continue) {
    52  			// We have to customize the randomization of TypeMetas because their
    53  			// APIVersion and Kind must remain blank in memory.
    54  			j.APIVersion = ""
    55  			j.Kind = ""
    56  		},
    57  		func(j *unversioned.TypeMeta, c fuzz.Continue) {
    58  			// We have to customize the randomization of TypeMetas because their
    59  			// APIVersion and Kind must remain blank in memory.
    60  			j.APIVersion = ""
    61  			j.Kind = ""
    62  		},
    63  		func(j *api.ObjectMeta, c fuzz.Continue) {
    64  			j.Name = c.RandString()
    65  			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
    66  			j.SelfLink = c.RandString()
    67  			j.UID = types.UID(c.RandString())
    68  			j.GenerateName = c.RandString()
    69  
    70  			var sec, nsec int64
    71  			c.Fuzz(&sec)
    72  			c.Fuzz(&nsec)
    73  			j.CreationTimestamp = unversioned.Unix(sec, nsec).Rfc3339Copy()
    74  		},
    75  		func(j *api.ObjectReference, c fuzz.Continue) {
    76  			// We have to customize the randomization of TypeMetas because their
    77  			// APIVersion and Kind must remain blank in memory.
    78  			j.APIVersion = c.RandString()
    79  			j.Kind = c.RandString()
    80  			j.Namespace = c.RandString()
    81  			j.Name = c.RandString()
    82  			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
    83  			j.FieldPath = c.RandString()
    84  		},
    85  		func(j *unversioned.ListMeta, c fuzz.Continue) {
    86  			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
    87  			j.SelfLink = c.RandString()
    88  		},
    89  		func(j *api.ListOptions, c fuzz.Continue) {
    90  			// TODO: add some parsing
    91  			j.LabelSelector, _ = labels.Parse("a=b")
    92  			j.FieldSelector, _ = fields.ParseSelector("a=b")
    93  		},
    94  		func(j *unversioned.ListOptions, c fuzz.Continue) {
    95  			// TODO: add some parsing
    96  			label, _ := labels.Parse("a=b")
    97  			j.LabelSelector = unversioned.LabelSelector{label}
    98  			field, _ := fields.ParseSelector("a=b")
    99  			j.FieldSelector = unversioned.FieldSelector{field}
   100  		},
   101  		func(s *api.PodSpec, c fuzz.Continue) {
   102  			c.FuzzNoCustom(s)
   103  			// has a default value
   104  			ttl := int64(30)
   105  			if c.RandBool() {
   106  				ttl = int64(c.Uint32())
   107  			}
   108  			s.TerminationGracePeriodSeconds = &ttl
   109  
   110  			c.Fuzz(s.SecurityContext)
   111  
   112  			if s.SecurityContext == nil {
   113  				s.SecurityContext = new(api.PodSecurityContext)
   114  			}
   115  		},
   116  		func(j *api.PodPhase, c fuzz.Continue) {
   117  			statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
   118  			*j = statuses[c.Rand.Intn(len(statuses))]
   119  		},
   120  		func(j *api.Binding, c fuzz.Continue) {
   121  			c.Fuzz(&j.ObjectMeta)
   122  			j.Target.Name = c.RandString()
   123  		},
   124  		func(j *api.ReplicationControllerSpec, c fuzz.Continue) {
   125  			c.FuzzNoCustom(j) // fuzz self without calling this function again
   126  			//j.TemplateRef = nil // this is required for round trip
   127  		},
   128  		func(j *extensions.DeploymentStrategy, c fuzz.Continue) {
   129  			c.FuzzNoCustom(j) // fuzz self without calling this function again
   130  			// Ensure that strategyType is one of valid values.
   131  			strategyTypes := []extensions.DeploymentStrategyType{extensions.RecreateDeploymentStrategyType, extensions.RollingUpdateDeploymentStrategyType}
   132  			j.Type = strategyTypes[c.Rand.Intn(len(strategyTypes))]
   133  			if j.Type != extensions.RollingUpdateDeploymentStrategyType {
   134  				j.RollingUpdate = nil
   135  			} else {
   136  				rollingUpdate := extensions.RollingUpdateDeployment{}
   137  				if c.RandBool() {
   138  					rollingUpdate.MaxUnavailable = intstr.FromInt(int(c.RandUint64()))
   139  					rollingUpdate.MaxSurge = intstr.FromInt(int(c.RandUint64()))
   140  				} else {
   141  					rollingUpdate.MaxSurge = intstr.FromString(fmt.Sprintf("%d%%", c.RandUint64()))
   142  				}
   143  				j.RollingUpdate = &rollingUpdate
   144  			}
   145  		},
   146  		func(j *extensions.JobSpec, c fuzz.Continue) {
   147  			c.FuzzNoCustom(j) // fuzz self without calling this function again
   148  			completions := c.Rand.Int()
   149  			parallelism := c.Rand.Int()
   150  			j.Completions = &completions
   151  			j.Parallelism = &parallelism
   152  		},
   153  		func(j *api.List, c fuzz.Continue) {
   154  			c.FuzzNoCustom(j) // fuzz self without calling this function again
   155  			// TODO: uncomment when round trip starts from a versioned object
   156  			if false { //j.Items == nil {
   157  				j.Items = []runtime.Object{}
   158  			}
   159  		},
   160  		func(j *runtime.Object, c fuzz.Continue) {
   161  			// TODO: uncomment when round trip starts from a versioned object
   162  			if true { //c.RandBool() {
   163  				*j = &runtime.Unknown{
   164  					TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"},
   165  					RawJSON:  []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`),
   166  				}
   167  			} else {
   168  				types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}}
   169  				t := types[c.Rand.Intn(len(types))]
   170  				c.Fuzz(t)
   171  				*j = t
   172  			}
   173  		},
   174  		func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) {
   175  			// This is necessary because keys with nil values get omitted.
   176  			// TODO: Is this a bug?
   177  			pb[docker.Port(c.RandString())] = []docker.PortBinding{
   178  				{c.RandString(), c.RandString()},
   179  				{c.RandString(), c.RandString()},
   180  			}
   181  		},
   182  		func(pm map[string]docker.PortMapping, c fuzz.Continue) {
   183  			// This is necessary because keys with nil values get omitted.
   184  			// TODO: Is this a bug?
   185  			pm[c.RandString()] = docker.PortMapping{
   186  				c.RandString(): c.RandString(),
   187  			}
   188  		},
   189  		func(q *api.ResourceRequirements, c fuzz.Continue) {
   190  			randomQuantity := func() resource.Quantity {
   191  				return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
   192  			}
   193  			q.Limits = make(api.ResourceList)
   194  			q.Requests = make(api.ResourceList)
   195  			cpuLimit := randomQuantity()
   196  			q.Limits[api.ResourceCPU] = *cpuLimit.Copy()
   197  			q.Requests[api.ResourceCPU] = *cpuLimit.Copy()
   198  			memoryLimit := randomQuantity()
   199  			q.Limits[api.ResourceMemory] = *memoryLimit.Copy()
   200  			q.Requests[api.ResourceMemory] = *memoryLimit.Copy()
   201  			storageLimit := randomQuantity()
   202  			q.Limits[api.ResourceStorage] = *storageLimit.Copy()
   203  			q.Requests[api.ResourceStorage] = *storageLimit.Copy()
   204  		},
   205  		func(q *api.LimitRangeItem, c fuzz.Continue) {
   206  			randomQuantity := func() resource.Quantity {
   207  				return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
   208  			}
   209  			cpuLimit := randomQuantity()
   210  
   211  			q.Type = api.LimitTypeContainer
   212  			q.Default = make(api.ResourceList)
   213  			q.Default[api.ResourceCPU] = *(cpuLimit.Copy())
   214  
   215  			q.DefaultRequest = make(api.ResourceList)
   216  			q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy())
   217  
   218  			q.Max = make(api.ResourceList)
   219  			q.Max[api.ResourceCPU] = *(cpuLimit.Copy())
   220  
   221  			q.Min = make(api.ResourceList)
   222  			q.Min[api.ResourceCPU] = *(cpuLimit.Copy())
   223  
   224  			q.MaxLimitRequestRatio = make(api.ResourceList)
   225  			q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10")
   226  		},
   227  		func(p *api.PullPolicy, c fuzz.Continue) {
   228  			policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent}
   229  			*p = policies[c.Rand.Intn(len(policies))]
   230  		},
   231  		func(rp *api.RestartPolicy, c fuzz.Continue) {
   232  			policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure}
   233  			*rp = policies[c.Rand.Intn(len(policies))]
   234  		},
   235  		func(vs *api.VolumeSource, c fuzz.Continue) {
   236  			// Exactly one of the fields must be set.
   237  			v := reflect.ValueOf(vs).Elem()
   238  			i := int(c.RandUint64() % uint64(v.NumField()))
   239  			v = v.Field(i).Addr()
   240  			// Use a new fuzzer which cannot populate nil to ensure one field will be set.
   241  			f := fuzz.New().NilChance(0).NumElements(1, 1)
   242  			f.Funcs(
   243  				// Only api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be
   244  				// defaulted to a version otherwise roundtrip will fail
   245  				// For the remaining volume plugins the default fuzzer is enough.
   246  				func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) {
   247  					m.Path = c.RandString()
   248  					versions := []string{"v1"}
   249  					m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))]
   250  					m.FieldRef.FieldPath = c.RandString()
   251  				},
   252  			).Fuzz(v.Interface())
   253  		},
   254  		func(d *api.DNSPolicy, c fuzz.Continue) {
   255  			policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault}
   256  			*d = policies[c.Rand.Intn(len(policies))]
   257  		},
   258  		func(p *api.Protocol, c fuzz.Continue) {
   259  			protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP}
   260  			*p = protocols[c.Rand.Intn(len(protocols))]
   261  		},
   262  		func(p *api.ServiceAffinity, c fuzz.Continue) {
   263  			types := []api.ServiceAffinity{api.ServiceAffinityClientIP, api.ServiceAffinityNone}
   264  			*p = types[c.Rand.Intn(len(types))]
   265  		},
   266  		func(p *api.ServiceType, c fuzz.Continue) {
   267  			types := []api.ServiceType{api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer}
   268  			*p = types[c.Rand.Intn(len(types))]
   269  		},
   270  		func(ct *api.Container, c fuzz.Continue) {
   271  			c.FuzzNoCustom(ct)                                          // fuzz self without calling this function again
   272  			ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty
   273  		},
   274  		func(p *api.Probe, c fuzz.Continue) {
   275  			c.FuzzNoCustom(p)
   276  			// These fields have default values.
   277  			intFieldsWithDefaults := [...]string{"TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"}
   278  			v := reflect.ValueOf(p).Elem()
   279  			for _, field := range intFieldsWithDefaults {
   280  				f := v.FieldByName(field)
   281  				if f.Int() == 0 {
   282  					f.SetInt(1)
   283  				}
   284  			}
   285  		},
   286  		func(ev *api.EnvVar, c fuzz.Continue) {
   287  			ev.Name = c.RandString()
   288  			if c.RandBool() {
   289  				ev.Value = c.RandString()
   290  			} else {
   291  				ev.ValueFrom = &api.EnvVarSource{}
   292  				ev.ValueFrom.FieldRef = &api.ObjectFieldSelector{}
   293  
   294  				versions := registered.RegisteredGroupVersions
   295  
   296  				ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))].String()
   297  				ev.ValueFrom.FieldRef.FieldPath = c.RandString()
   298  			}
   299  		},
   300  		func(sc *api.SecurityContext, c fuzz.Continue) {
   301  			c.FuzzNoCustom(sc) // fuzz self without calling this function again
   302  			if c.RandBool() {
   303  				priv := c.RandBool()
   304  				sc.Privileged = &priv
   305  			}
   306  
   307  			if c.RandBool() {
   308  				sc.Capabilities = &api.Capabilities{
   309  					Add:  make([]api.Capability, 0),
   310  					Drop: make([]api.Capability, 0),
   311  				}
   312  				c.Fuzz(&sc.Capabilities.Add)
   313  				c.Fuzz(&sc.Capabilities.Drop)
   314  			}
   315  		},
   316  		func(s *api.Secret, c fuzz.Continue) {
   317  			c.FuzzNoCustom(s) // fuzz self without calling this function again
   318  			s.Type = api.SecretTypeOpaque
   319  		},
   320  		func(pv *api.PersistentVolume, c fuzz.Continue) {
   321  			c.FuzzNoCustom(pv) // fuzz self without calling this function again
   322  			types := []api.PersistentVolumePhase{api.VolumeAvailable, api.VolumePending, api.VolumeBound, api.VolumeReleased, api.VolumeFailed}
   323  			pv.Status.Phase = types[c.Rand.Intn(len(types))]
   324  			pv.Status.Message = c.RandString()
   325  			reclamationPolicies := []api.PersistentVolumeReclaimPolicy{api.PersistentVolumeReclaimRecycle, api.PersistentVolumeReclaimRetain}
   326  			pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))]
   327  		},
   328  		func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) {
   329  			c.FuzzNoCustom(pvc) // fuzz self without calling this function again
   330  			types := []api.PersistentVolumeClaimPhase{api.ClaimBound, api.ClaimPending}
   331  			pvc.Status.Phase = types[c.Rand.Intn(len(types))]
   332  		},
   333  		func(s *api.NamespaceSpec, c fuzz.Continue) {
   334  			s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
   335  		},
   336  		func(s *api.NamespaceStatus, c fuzz.Continue) {
   337  			s.Phase = api.NamespaceActive
   338  		},
   339  		func(http *api.HTTPGetAction, c fuzz.Continue) {
   340  			c.FuzzNoCustom(http)            // fuzz self without calling this function again
   341  			http.Path = "/" + http.Path     // can't be blank
   342  			http.Scheme = "x" + http.Scheme // can't be blank
   343  		},
   344  		func(ss *api.ServiceSpec, c fuzz.Continue) {
   345  			c.FuzzNoCustom(ss) // fuzz self without calling this function again
   346  			if len(ss.Ports) == 0 {
   347  				// There must be at least 1 port.
   348  				ss.Ports = append(ss.Ports, api.ServicePort{})
   349  				c.Fuzz(&ss.Ports[0])
   350  			}
   351  			for i := range ss.Ports {
   352  				switch ss.Ports[i].TargetPort.Type {
   353  				case intstr.Int:
   354  					ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero
   355  				case intstr.String:
   356  					ss.Ports[i].TargetPort.StrVal = "x" + ss.Ports[i].TargetPort.StrVal // non-empty
   357  				}
   358  			}
   359  		},
   360  		func(n *api.Node, c fuzz.Continue) {
   361  			c.FuzzNoCustom(n)
   362  			n.Spec.ExternalID = "external"
   363  		},
   364  		func(s *extensions.APIVersion, c fuzz.Continue) {
   365  			// We can't use c.RandString() here because it may generate empty
   366  			// string, which will cause tests failure.
   367  			s.APIGroup = "something"
   368  		},
   369  		func(s *extensions.HorizontalPodAutoscalerSpec, c fuzz.Continue) {
   370  			c.FuzzNoCustom(s) // fuzz self without calling this function again
   371  			minReplicas := c.Rand.Int()
   372  			s.MinReplicas = &minReplicas
   373  			s.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int(c.RandUint64())}
   374  		},
   375  	)
   376  	return f
   377  }