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 = ¶llelism 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 }