diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index 6c9e6ae3d61..d51f0376df6 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -35,7 +35,6 @@ import ( func fuzzOneOf(c fuzz.Continue, objs ...interface{}) { // Use a new fuzzer which cannot populate nil to ensure one obj will be set. - // FIXME: would be nicer to use FuzzOnePtr() and reflect. f := fuzz.New().NilChance(0).NumElements(1, 1) i := c.RandUint64() % uint64(len(objs)) f.Fuzz(objs[i]) @@ -103,18 +102,15 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { c.Fuzz(&j.Spec) }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { - // TemplateRef is set to nil by omission; this is required for round trip - c.Fuzz(&j.Template) - c.Fuzz(&j.Selector) - j.Replicas = int(c.RandUint64()) + c.FuzzNoCustom(j) // fuzz self without calling this function again + j.TemplateRef = nil // this is required for round trip }, func(j *api.ReplicationControllerStatus, c fuzz.Continue) { // only replicas round trips j.Replicas = int(c.RandUint64()) }, func(j *api.List, c fuzz.Continue) { - c.Fuzz(&j.ListMeta) - c.Fuzz(&j.Items) + c.FuzzNoCustom(j) // fuzz self without calling this function again if j.Items == nil { j.Items = []runtime.Object{} } @@ -132,18 +128,6 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { *j = t } }, - func(intstr *util.IntOrString, c fuzz.Continue) { - // util.IntOrString will panic if its kind is set wrong. - if c.RandBool() { - intstr.Kind = util.IntstrInt - intstr.IntVal = int(c.RandUint64()) - intstr.StrVal = "" - } else { - intstr.Kind = util.IntstrString - intstr.IntVal = 0 - intstr.StrVal = c.RandString() - } - }, func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? @@ -194,34 +178,12 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { *p = types[c.Rand.Intn(len(types))] }, func(ct *api.Container, c fuzz.Continue) { - // This function exists soley to set TerminationMessagePath to a - // non-empty string. TODO: consider making TerminationMessagePath a - // new type to simplify fuzzing. - ct.TerminationMessagePath = api.TerminationMessagePathDefault - // Let fuzzer handle the rest of the fileds. - c.Fuzz(&ct.Name) - c.Fuzz(&ct.Image) - c.Fuzz(&ct.Command) - c.Fuzz(&ct.Ports) - c.Fuzz(&ct.WorkingDir) - c.Fuzz(&ct.Env) - c.Fuzz(&ct.VolumeMounts) - c.Fuzz(&ct.LivenessProbe) - c.Fuzz(&ct.Lifecycle) - c.Fuzz(&ct.ImagePullPolicy) - c.Fuzz(&ct.Privileged) - c.Fuzz(&ct.Capabilities) + c.FuzzNoCustom(ct) // fuzz self without calling this function again + ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty }, func(e *api.Event, c fuzz.Continue) { + c.FuzzNoCustom(e) // fuzz self without calling this function again // Fix event count to 1, otherwise, if a v1beta1 or v1beta2 event has a count set arbitrarily, it's count is ignored - c.Fuzz(&e.TypeMeta) - c.Fuzz(&e.ObjectMeta) - c.Fuzz(&e.InvolvedObject) - c.Fuzz(&e.Reason) - c.Fuzz(&e.Message) - c.Fuzz(&e.Source) - c.Fuzz(&e.FirstTimestamp) - c.Fuzz(&e.LastTimestamp) if e.FirstTimestamp.IsZero() { e.Count = 1 } else { @@ -229,11 +191,8 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { } }, func(s *api.Secret, c fuzz.Continue) { - c.Fuzz(&s.TypeMeta) - c.Fuzz(&s.ObjectMeta) - + c.FuzzNoCustom(s) // fuzz self without calling this function again s.Type = api.SecretTypeOpaque - c.Fuzz(&s.Data) }, func(ep *api.Endpoint, c fuzz.Continue) { // TODO: If our API used a particular type for IP fields we could just catch that here. @@ -241,29 +200,16 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { ep.Port = c.Rand.Intn(65536) }, func(http *api.HTTPGetAction, c fuzz.Continue) { - http.Path = "/" + c.RandString() // can't be blank - c.Fuzz(&http.Port) - c.Fuzz(&http.Host) + c.FuzzNoCustom(http) // fuzz self without calling this function again + http.Path = "/" + http.Path // can't be blank }, func(ss *api.ServiceSpec, c fuzz.Continue) { - // TODO: I wish I could say "fuzz myself but without the custom fuzz-func" - c.Fuzz(&ss.Port) - c.Fuzz(&ss.Protocol) - c.Fuzz(&ss.Selector) - c.Fuzz(&ss.PortalIP) - c.Fuzz(&ss.CreateExternalLoadBalancer) - c.Fuzz(&ss.PublicIPs) - c.Fuzz(&ss.SessionAffinity) - // TODO: would be great if types could voluntarily fuzz themselves. - kinds := []util.IntstrKind{util.IntstrInt, util.IntstrString} - ss.ContainerPort.Kind = kinds[c.Rand.Intn(len(kinds))] + c.FuzzNoCustom(ss) // fuzz self without calling this function again switch ss.ContainerPort.Kind { case util.IntstrInt: - ss.ContainerPort.IntVal = 1 + c.Rand.Intn(65535) // non-zero - ss.ContainerPort.StrVal = "" // needed because we reuse objects without zeroing them + ss.ContainerPort.IntVal = 1 + ss.ContainerPort.IntVal%65535 // non-zero case util.IntstrString: - ss.ContainerPort.StrVal = "x" + c.RandString() // non-empty - ss.ContainerPort.IntVal = 0 // needed because we reuse objects without zeroing them + ss.ContainerPort.StrVal = "x" + ss.ContainerPort.StrVal // non-empty } }, ) diff --git a/pkg/util/util.go b/pkg/util/util.go index 579e9061324..59cb2fe1b5a 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -29,6 +29,7 @@ import ( "time" "github.com/golang/glog" + "github.com/google/gofuzz" ) // For testing, bypass HandleCrash. @@ -162,6 +163,18 @@ func (intstr IntOrString) MarshalJSON() ([]byte, error) { } } +func (intstr *IntOrString) Fuzz(c fuzz.Continue) { + if c.RandBool() { + intstr.Kind = IntstrInt + c.Fuzz(&intstr.IntVal) + intstr.StrVal = "" + } else { + intstr.Kind = IntstrString + intstr.IntVal = 0 + c.Fuzz(&intstr.StrVal) + } +} + // Takes a list of strings and compiles them into a list of regular expressions func CompileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { regexps := []*regexp.Regexp{}