// getContainerStatus creates the api.containerStatus of a container from the podInfo. // TODO(yifan): Get more detailed info such as Image, ImageID, etc. func (p *podInfo) getContainerStatus(container *kubecontainer.Container) api.ContainerStatus { var status api.ContainerStatus status.Name = container.Name status.Image = container.Image containerID, _ := parseContainerID(string(container.ID)) status.ImageID = containerID.imageID switch p.state { case Running: // TODO(yifan): Get StartedAt. status.State = api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.Unix(container.Created, 0), }, } case Embryo, Preparing, Prepared: status.State = api.ContainerState{Waiting: &api.ContainerStateWaiting{}} case AbortedPrepare, Deleting, Exited, Garbage: exitCode, ok := p.exitCodes[status.ImageID] if !ok { glog.Warningf("rkt: Cannot get exit code for container %v", container) } exitCode = -1 status.State = api.ContainerState{ Terminated: &api.ContainerStateTerminated{ ExitCode: exitCode, StartedAt: util.Unix(container.Created, 0), }, } default: glog.Warningf("rkt: Unknown pod state: %q", p.state) } return status }
func TestStoreEventsMultipleEventsInput(t *testing.T) { // Arrange fakeSink := NewFakeSink(false /* avoidColumns */) event1Time := kube_time.Unix(12345, 0) event2Time := kube_time.Unix(12366, 0) event1SourceHostname := "event1HostName" event2SourceHostname := "event2HostName" event1Reason := "event1" event2Reason := "event2" events := []kube_api.Event{ kube_api.Event{ Reason: event1Reason, LastTimestamp: event1Time, Source: kube_api.EventSource{ Host: event1SourceHostname, }, }, kube_api.Event{ Reason: event2Reason, LastTimestamp: event2Time, Source: kube_api.EventSource{ Host: event2SourceHostname, }, }, } // Act err := fakeSink.StoreEvents(events) // Assert assert.NoError(t, err) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls) /* actual */) assert.Equal(t, influxdb.Millisecond /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].timePrecision /* actual */) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls[0].series) /* actual */) assert.Equal(t, eventsSeriesName /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Name /* actual */) assert.Equal(t, 6 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls[0].series[0].Columns) /* actual */) assert.Equal(t, 2 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points) /* actual */) assert.Equal(t, event1Time.Unix() /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[0][0] /* actual */) // Column 0 - time assert.Equal(t, uint64(0xcbf29ce484222325) /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[0][1] /* actual */) // Column 1 - sequence_number assert.Equal(t, "" /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[0][2] /* actual */) // Column 2 - pod_id assert.Equal(t, "" /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[0][3] /* actual */) // Column 3 - pod_id assert.Equal(t, event1SourceHostname /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[0][4] /* actual */) // Column 4 - pod_id assert.Contains(t, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[0][5], event1Reason) // Column 5 - value assert.Equal(t, event2Time.Unix() /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[1][0] /* actual */) // Column 0 - time assert.Equal(t, uint64(0xcbf29ce484222325) /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[1][1] /* actual */) // Column 1 - sequence_number assert.Equal(t, "" /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[1][2] /* actual */) // Column 2 - pod_id assert.Equal(t, "" /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[1][3] /* actual */) // Column 3 - pod_id assert.Equal(t, event2SourceHostname /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[1][4] /* actual */) // Column 4 - pod_id assert.Contains(t, fakeSink.fakeClient.capturedWriteCalls[0].series[0].Points[1][5], event2Reason) // Column 5 - value }
func TestStoreEventsSingleEventInput(t *testing.T) { // Arrange fakeSink := NewFakeSink() eventTime := kube_time.Unix(12345, 0) events := []kube_api.Event{ kube_api.Event{ Reason: "event1", FirstTimestamp: eventTime, LastTimestamp: eventTime, Source: kube_api.EventSource{ Host: "event1HostName", }, }, } // Act err := fakeSink.StoreEvents(events) // Assert assert.NoError(t, err) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeHttpClient.capturedSendRequests) /* actual */) assert.Equal(t, "POST" /* expected */, fakeSink.fakeHttpClient.capturedSendRequests[0].Method /* actual */) assert.Equal(t, "https" /* expected */, fakeSink.fakeHttpClient.capturedSendRequests[0].URL.Scheme /* actual */) assert.Equal(t, "logging.googleapis.com" /* expected */, fakeSink.fakeHttpClient.capturedSendRequests[0].URL.Host /* actual */) assert.Equal(t, "/v1beta3/projects/fakeProjectId/logs/kubernetes.io%2Fevents/entries:write" /* expected */, fakeSink.fakeHttpClient.capturedSendRequests[0].URL.Opaque /* actual */) assert.Equal(t, 2 /* expected */, len(fakeSink.fakeHttpClient.capturedSendRequests[0].Header) /* actual */) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeHttpClient.capturedSendRequests[0].Header["Content-Type"]) /* actual */) assert.Equal(t, "application/json" /* expected */, fakeSink.fakeHttpClient.capturedSendRequests[0].Header["Content-Type"][0] /* actual */) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeHttpClient.capturedSendRequests[0].Header["Authorization"]) /* actual */) assert.Equal(t, "Bearer fakeToken" /* expected */, fakeSink.fakeHttpClient.capturedSendRequests[0].Header["Authorization"][0] /* actual */) reqBodyBytes, err := ioutil.ReadAll(fakeSink.fakeHttpClient.capturedSendRequests[0].Body) assert.NoError(t, err) expectedJson := "{\"entries\":[{\"metadata\":{\"timestamp\":\"1970-01-01T03:25:45Z\",\"severity\":\"NOTICE\",\"projectId\":\"fakeProjectId\",\"serviceName\":\"custom.googleapis.com\"},\"structPayload\":{\"metadata\":{\"creationTimestamp\":null},\"involvedObject\":{},\"reason\":\"event1\",\"source\":{\"host\":\"event1HostName\"},\"firstTimestamp\":\"1970-01-01T03:25:45Z\",\"lastTimestamp\":\"1970-01-01T03:25:45Z\"}}]}" assert.Equal(t, expectedJson /* expected */, string(reqBodyBytes) /* actual */) }
func TestAddOrUpdateEventExisting(t *testing.T) { // Arrange event1Time := util.Unix(2324, 2342) event2Time := util.Now() event1 := api.Event{ Reason: "something happened", Message: "can you believe it?", ObjectMeta: api.ObjectMeta{ ResourceVersion: "rs1", }, InvolvedObject: api.ObjectReference{ Kind: "Scheduler", Name: "anOkName", Namespace: "someNamespace", UID: "C934D3234CD0242", APIVersion: "version", }, Source: api.EventSource{ Component: "kubelet", Host: "kublet.node2", }, Count: 1, FirstTimestamp: event1Time, LastTimestamp: event1Time, } event2 := api.Event{ Reason: "something happened", Message: "can you believe it?", ObjectMeta: api.ObjectMeta{ ResourceVersion: "rs2", }, InvolvedObject: api.ObjectReference{ Kind: "Scheduler", Name: "anOkName", Namespace: "someNamespace", UID: "C934D3234CD0242", APIVersion: "version", }, Source: api.EventSource{ Component: "kubelet", Host: "kublet.node2", }, Count: 3, FirstTimestamp: event1Time, LastTimestamp: event2Time, } // Act addOrUpdateEvent(&event1) result1 := addOrUpdateEvent(&event2) result2 := getEvent(&event1) // Assert compareEventWithHistoryEntry(&event2, &result1, t) compareEventWithHistoryEntry(&event2, &result2, t) }
// FuzzerFor can randomly populate api objects that are destined for version. func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { // Do nothing; this struct has only a Kind field and it must stay blank in memory. }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() j.UID = types.UID(c.RandString()) j.GenerateName = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ObjectReference, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = c.RandString() j.Kind = c.RandString() j.Namespace = c.RandString() j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.FieldPath = c.RandString() }, func(j *api.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.ListOptions, c fuzz.Continue) { // TODO: add some parsing j.LabelSelector, _ = labels.Parse("a=b") j.FieldSelector, _ = fields.ParseSelector("a=b") }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.PodTemplateSpec, c fuzz.Continue) { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual j.ObjectMeta = api.ObjectMeta{} c.Fuzz(&j.ObjectMeta) j.ObjectMeta = api.ObjectMeta{Labels: j.ObjectMeta.Labels} j.Spec = api.PodSpec{} c.Fuzz(&j.Spec) }, func(j *api.Binding, c fuzz.Continue) { c.Fuzz(&j.ObjectMeta) j.Target.Name = c.RandString() }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { 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.FuzzNoCustom(j) // fuzz self without calling this function again // TODO: uncomment when round trip starts from a versioned object if false { //j.Items == nil { j.Items = []runtime.Object{} } }, func(j *runtime.Object, c fuzz.Continue) { // TODO: uncomment when round trip starts from a versioned object if true { //c.RandBool() { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *j = t } }, 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? pb[docker.Port(c.RandString())] = []docker.PortBinding{ {c.RandString(), c.RandString()}, {c.RandString(), c.RandString()}, } }, func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pm[c.RandString()] = docker.PortMapping{ c.RandString(): c.RandString(), } }, func(q *resource.Quantity, c fuzz.Continue) { // Real Quantity fuzz testing is done elsewhere; // this limited subset of functionality survives // round-tripping to v1beta1/2. q.Amount = &inf.Dec{} q.Format = resource.DecimalExponent //q.Amount.SetScale(inf.Scale(-c.Intn(12))) q.Amount.SetUnscaled(c.Int63n(1000)) }, func(p *api.PullPolicy, c fuzz.Continue) { policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent} *p = policies[c.Rand.Intn(len(policies))] }, func(rp *api.RestartPolicy, c fuzz.Continue) { policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure} *rp = policies[c.Rand.Intn(len(policies))] }, func(vs *api.VolumeSource, c fuzz.Continue) { // Exactly one of the fields must be set. v := reflect.ValueOf(vs).Elem() i := int(c.RandUint64() % uint64(v.NumField())) v = v.Field(i).Addr() // Use a new fuzzer which cannot populate nil to ensure one field will be set. fuzz.New().NilChance(0).NumElements(1, 1).Fuzz(v.Interface()) }, func(d *api.DNSPolicy, c fuzz.Continue) { policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault} *d = policies[c.Rand.Intn(len(policies))] }, func(p *api.Protocol, c fuzz.Continue) { protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP} *p = protocols[c.Rand.Intn(len(protocols))] }, func(p *api.ServiceAffinity, c fuzz.Continue) { types := []api.ServiceAffinity{api.ServiceAffinityClientIP, api.ServiceAffinityNone} *p = types[c.Rand.Intn(len(types))] }, func(p *api.ServiceType, c fuzz.Continue) { types := []api.ServiceType{api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer} *p = types[c.Rand.Intn(len(types))] }, func(ct *api.Container, c fuzz.Continue) { c.FuzzNoCustom(ct) // fuzz self without calling this function again ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty }, func(ev *api.EnvVar, c fuzz.Continue) { ev.Name = c.RandString() if c.RandBool() { ev.Value = c.RandString() } else { ev.ValueFrom = &api.EnvVarSource{} ev.ValueFrom.FieldRef = &api.ObjectFieldSelector{} versions := []string{"v1beta1", "v1beta2", "v1beta3"} ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] ev.ValueFrom.FieldRef.FieldPath = c.RandString() } }, func(sc *api.SecurityContext, c fuzz.Continue) { c.FuzzNoCustom(sc) // fuzz self without calling this function again priv := c.RandBool() sc.Privileged = &priv sc.Capabilities = &api.Capabilities{ Add: make([]api.Capability, 0), Drop: make([]api.Capability, 0), } c.Fuzz(&sc.Capabilities.Add) c.Fuzz(&sc.Capabilities.Drop) }, 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 if e.FirstTimestamp.IsZero() { e.Count = 1 } else { c.Fuzz(&e.Count) } }, func(s *api.Secret, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.Type = api.SecretTypeOpaque }, func(pv *api.PersistentVolume, c fuzz.Continue) { c.FuzzNoCustom(pv) // fuzz self without calling this function again types := []api.PersistentVolumePhase{api.VolumeAvailable, api.VolumePending, api.VolumeBound, api.VolumeReleased, api.VolumeFailed} pv.Status.Phase = types[c.Rand.Intn(len(types))] pv.Status.Message = c.RandString() reclamationPolicies := []api.PersistentVolumeReclaimPolicy{api.PersistentVolumeReclaimRecycle, api.PersistentVolumeReclaimRetain} pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))] }, func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) { c.FuzzNoCustom(pvc) // fuzz self without calling this function again types := []api.PersistentVolumeClaimPhase{api.ClaimBound, api.ClaimPending} pvc.Status.Phase = types[c.Rand.Intn(len(types))] }, func(s *api.NamespaceSpec, c fuzz.Continue) { s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes} }, func(s *api.NamespaceStatus, c fuzz.Continue) { s.Phase = api.NamespaceActive }, func(http *api.HTTPGetAction, c fuzz.Continue) { c.FuzzNoCustom(http) // fuzz self without calling this function again http.Path = "/" + http.Path // can't be blank http.Scheme = "x" + http.Scheme // can't be blank }, func(ss *api.ServiceSpec, c fuzz.Continue) { c.FuzzNoCustom(ss) // fuzz self without calling this function again if len(ss.Ports) == 0 { // There must be at least 1 port. ss.Ports = append(ss.Ports, api.ServicePort{}) c.Fuzz(&ss.Ports[0]) } for i := range ss.Ports { switch ss.Ports[i].TargetPort.Kind { case util.IntstrInt: ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero case util.IntstrString: ss.Ports[i].TargetPort.StrVal = "x" + ss.Ports[i].TargetPort.StrVal // non-empty } } }, func(n *api.Node, c fuzz.Continue) { c.FuzzNoCustom(n) n.Spec.ExternalID = "external" }, ) return f }
}, func(j *api.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { // TemplateRef must be nil for round trip c.Fuzz(&j.Template) if j.Template == nil { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual
// fuzzerFor can randomly populate api objects that are destined for version. func fuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { // Do nothing; this struct has only a Kind field and it must stay blank in memory. }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { // TemplateRef must be nil for round trip c.Fuzz(&j.Template) if j.Template == nil { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual j.Template = &api.PodTemplateSpec{} } j.Template.ObjectMeta = api.ObjectMeta{Labels: j.Template.ObjectMeta.Labels} j.Template.Spec.NodeSelector = nil c.Fuzz(&j.Selector) j.Replicas = int(c.RandUint64()) }, 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) if j.Items == nil { j.Items = []runtime.Object{} } }, func(j *runtime.Object, c fuzz.Continue) { if c.RandBool() { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *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? pb[docker.Port(c.RandString())] = []docker.PortBinding{ {c.RandString(), c.RandString()}, {c.RandString(), c.RandString()}, } }, func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pm[c.RandString()] = docker.PortMapping{ c.RandString(): c.RandString(), } }, func(q *resource.Quantity, c fuzz.Continue) { // Real Quantity fuzz testing is done elsewhere; // this limited subset of functionality survives // round-tripping to v1beta1/2. q.Amount = &inf.Dec{} q.Format = resource.DecimalExponent //q.Amount.SetScale(inf.Scale(-c.Intn(12))) q.Amount.SetUnscaled(c.Int63n(1000)) }, ) return f }
func TestDescribeBuildDuration(t *testing.T) { type testBuild struct { build *buildapi.Build output string } creation := kutil.Date(2015, time.April, 9, 6, 0, 0, 0, time.Local) // now a minute ago minuteAgo := kutil.Unix(kutil.Now().Rfc3339Copy().Time.Unix()-60, 0) start := kutil.Date(2015, time.April, 9, 6, 1, 0, 0, time.Local) completion := kutil.Date(2015, time.April, 9, 6, 2, 0, 0, time.Local) duration := completion.Rfc3339Copy().Time.Sub(start.Rfc3339Copy().Time) zeroDuration := time.Duration(0) tests := []testBuild{ { // 0 - build new &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: minuteAgo}, Status: buildapi.BuildStatusNew, Duration: zeroDuration, }, "waiting for 1m0s", }, { // 1 - build pending &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: minuteAgo}, Status: buildapi.BuildStatusPending, Duration: zeroDuration, }, "waiting for 1m0s", }, { // 2 - build running &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, StartTimestamp: &start, Status: buildapi.BuildStatusRunning, Duration: duration, }, "running for 1m0s", }, { // 3 - build completed &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, StartTimestamp: &start, CompletionTimestamp: &completion, Status: buildapi.BuildStatusComplete, Duration: duration, }, "1m0s", }, { // 4 - build failed &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, StartTimestamp: &start, CompletionTimestamp: &completion, Status: buildapi.BuildStatusFailed, Duration: duration, }, "1m0s", }, { // 5 - build error &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, StartTimestamp: &start, CompletionTimestamp: &completion, Status: buildapi.BuildStatusError, Duration: duration, }, "1m0s", }, { // 6 - build cancelled before running, start time wasn't set yet &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, CompletionTimestamp: &completion, Status: buildapi.BuildStatusCancelled, Duration: duration, }, "waited for 2m0s", }, { // 7 - build cancelled while running, start time is set already &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, StartTimestamp: &start, CompletionTimestamp: &completion, Status: buildapi.BuildStatusCancelled, Duration: duration, }, "1m0s", }, { // 8 - build failed before running, start time wasn't set yet &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, CompletionTimestamp: &completion, Status: buildapi.BuildStatusFailed, Duration: duration, }, "waited for 2m0s", }, { // 9 - build error before running, start time wasn't set yet &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, CompletionTimestamp: &completion, Status: buildapi.BuildStatusError, Duration: duration, }, "waited for 2m0s", }, } for i, tc := range tests { if actual, expected := describeBuildDuration(tc.build), tc.output; actual != expected { t.Errorf("(%d) expected duration output %s, got %s", i, expected, actual) } } }
// FuzzerFor can randomly populate api objects that are destined for version. func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { // Do nothing; this struct has only a Kind field and it must stay blank in memory. }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() j.UID = types.UID(c.RandString()) j.GenerateName = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ObjectReference, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = c.RandString() j.Kind = c.RandString() j.Namespace = c.RandString() j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.FieldPath = c.RandString() }, func(j *api.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.PodTemplateSpec, c fuzz.Continue) { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual j.ObjectMeta = api.ObjectMeta{} c.Fuzz(&j.ObjectMeta) j.ObjectMeta = api.ObjectMeta{Labels: j.ObjectMeta.Labels} j.Spec = api.PodSpec{} c.Fuzz(&j.Spec) }, func(j *api.Binding, c fuzz.Continue) { c.Fuzz(&j.ObjectMeta) j.Target.Name = c.RandString() }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { 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.FuzzNoCustom(j) // fuzz self without calling this function again if j.Items == nil { j.Items = []runtime.Object{} } }, func(j *runtime.Object, c fuzz.Continue) { if c.RandBool() { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *j = t } }, 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? pb[docker.Port(c.RandString())] = []docker.PortBinding{ {c.RandString(), c.RandString()}, {c.RandString(), c.RandString()}, } }, func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pm[c.RandString()] = docker.PortMapping{ c.RandString(): c.RandString(), } }, func(q *resource.Quantity, c fuzz.Continue) { // Real Quantity fuzz testing is done elsewhere; // this limited subset of functionality survives // round-tripping to v1beta1/2. q.Amount = &inf.Dec{} q.Format = resource.DecimalExponent //q.Amount.SetScale(inf.Scale(-c.Intn(12))) q.Amount.SetUnscaled(c.Int63n(1000)) }, func(p *api.PullPolicy, c fuzz.Continue) { policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent} *p = policies[c.Rand.Intn(len(policies))] }, func(rp *api.RestartPolicy, c fuzz.Continue) { policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure} *rp = policies[c.Rand.Intn(len(policies))] }, func(vs *api.VolumeSource, c fuzz.Continue) { // Exactly one of the fields should be set. //FIXME: the fuzz can still end up nil. What if fuzz allowed me to say that? fuzzOneOf(c, &vs.HostPath, &vs.EmptyDir, &vs.GCEPersistentDisk, &vs.GitRepo, &vs.Secret, &vs.NFS) }, func(d *api.DNSPolicy, c fuzz.Continue) { policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault} *d = policies[c.Rand.Intn(len(policies))] }, func(p *api.Protocol, c fuzz.Continue) { protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP} *p = protocols[c.Rand.Intn(len(protocols))] }, func(p *api.AffinityType, c fuzz.Continue) { types := []api.AffinityType{api.AffinityTypeClientIP, api.AffinityTypeNone} *p = types[c.Rand.Intn(len(types))] }, func(ct *api.Container, c fuzz.Continue) { 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 if e.FirstTimestamp.IsZero() { e.Count = 1 } else { c.Fuzz(&e.Count) } }, func(s *api.Secret, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.Type = api.SecretTypeOpaque }, func(s *api.NamespaceStatus, c fuzz.Continue) { s.Phase = api.NamespaceActive }, func(ep *api.Endpoint, c fuzz.Continue) { // TODO: If our API used a particular type for IP fields we could just catch that here. ep.IP = fmt.Sprintf("%d.%d.%d.%d", c.Rand.Intn(256), c.Rand.Intn(256), c.Rand.Intn(256), c.Rand.Intn(256)) ep.Port = c.Rand.Intn(65536) }, func(http *api.HTTPGetAction, c fuzz.Continue) { 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) { c.FuzzNoCustom(ss) // fuzz self without calling this function again switch ss.TargetPort.Kind { case util.IntstrInt: ss.TargetPort.IntVal = 1 + ss.TargetPort.IntVal%65535 // non-zero case util.IntstrString: ss.TargetPort.StrVal = "x" + ss.TargetPort.StrVal // non-empty } }, ) return f }
// FuzzerFor can randomly populate api objects that are destined for version. func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { // Do nothing; this struct has only a Kind field and it must stay blank in memory. }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() j.UID = types.UID(c.RandString()) j.GenerateName = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ObjectReference, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = c.RandString() j.Kind = c.RandString() j.Namespace = c.RandString() j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.FieldPath = c.RandString() }, func(j *api.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.PodTemplateSpec, c fuzz.Continue) { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual j.ObjectMeta = api.ObjectMeta{} c.Fuzz(&j.ObjectMeta) j.ObjectMeta = api.ObjectMeta{Labels: j.ObjectMeta.Labels} j.Spec = api.PodSpec{} 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()) }, 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) if j.Items == nil { j.Items = []runtime.Object{} } }, func(j *runtime.Object, c fuzz.Continue) { if c.RandBool() { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *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? pb[docker.Port(c.RandString())] = []docker.PortBinding{ {c.RandString(), c.RandString()}, {c.RandString(), c.RandString()}, } }, func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pm[c.RandString()] = docker.PortMapping{ c.RandString(): c.RandString(), } }, func(q *resource.Quantity, c fuzz.Continue) { // Real Quantity fuzz testing is done elsewhere; // this limited subset of functionality survives // round-tripping to v1beta1/2. q.Amount = &inf.Dec{} q.Format = resource.DecimalExponent //q.Amount.SetScale(inf.Scale(-c.Intn(12))) q.Amount.SetUnscaled(c.Int63n(1000)) }, func(p *api.PullPolicy, c fuzz.Continue) { policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent} *p = policies[c.Rand.Intn(len(policies))] }, func(rp *api.RestartPolicy, c fuzz.Continue) { // Exactly one of the fields should be set. fuzzOneOf(c, &rp.Always, &rp.OnFailure, &rp.Never) }, func(vs *api.VolumeSource, c fuzz.Continue) { // Exactly one of the fields should be set. //FIXME: the fuzz can still end up nil. What if fuzz allowed me to say that? fuzzOneOf(c, &vs.HostPath, &vs.EmptyDir, &vs.GCEPersistentDisk, &vs.GitRepo, &vs.Secret) }, func(d *api.DNSPolicy, c fuzz.Continue) { policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault} *d = policies[c.Rand.Intn(len(policies))] }, func(p *api.Protocol, c fuzz.Continue) { protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP} *p = protocols[c.Rand.Intn(len(protocols))] }, func(p *api.AffinityType, c fuzz.Continue) { types := []api.AffinityType{api.AffinityTypeClientIP, api.AffinityTypeNone} *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) }, func(e *api.Event, c fuzz.Continue) { // 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 { c.Fuzz(&e.Count) } }, func(s *api.Secret, c fuzz.Continue) { c.Fuzz(&s.TypeMeta) c.Fuzz(&s.ObjectMeta) 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. ep.IP = fmt.Sprintf("%d.%d.%d.%d", c.Rand.Intn(256), c.Rand.Intn(256), c.Rand.Intn(256), c.Rand.Intn(256)) ep.Port = c.Rand.Intn(65536) }, ) return f }