// makeContainerStatus creates the api.containerStatus of a container from the podInfo. func makeContainerStatus(container *kubecontainer.Container, podInfo *podInfo) api.ContainerStatus { var status api.ContainerStatus status.Name = container.Name status.Image = container.Image status.ContainerID = string(container.ID) // TODO(yifan): Add image ID info. switch podInfo.state { case Running: // TODO(yifan): Get StartedAt. status.State = api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: unversioned.Unix(container.Created, 0), }, } case Embryo, Preparing, Prepared: status.State = api.ContainerState{Waiting: &api.ContainerStateWaiting{}} case AbortedPrepare, Deleting, Exited, Garbage: exitCode, ok := podInfo.exitCodes[status.Name] 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: unversioned.Unix(container.Created, 0), }, } default: glog.Warningf("rkt: Unknown pod state: %q", podInfo.state) } return status }
func generateFakeEvents() []kube_api.Event { event1Time := kube_api_unv.Unix(12345, 0) event2Time := kube_api_unv.Unix(12366, 0) event1SourceHostname := "event1HostName" event2SourceHostname := "event2HostName" event1Reason := "event1" event2Reason := "event2" events := []kube_api.Event{ { Reason: event1Reason, LastTimestamp: event1Time, Source: kube_api.EventSource{ Host: event1SourceHostname, }, }, { Reason: event2Reason, LastTimestamp: event2Time, Source: kube_api.EventSource{ Host: event2SourceHostname, }, }, } return events }
func TestSyncPastDeadlineJobFinished(t *testing.T) { clientset := clientset.NewForConfigOrDie(&client.Config{Host: "", ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) manager := NewJobController(clientset, controller.NoResyncPeriodFunc) fakePodControl := controller.FakePodControl{} manager.podControl = &fakePodControl manager.podStoreSynced = alwaysReady var actual *extensions.Job manager.updateHandler = func(job *extensions.Job) error { actual = job return nil } job := newJob(1, 1) activeDeadlineSeconds := int64(10) job.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds start := unversioned.Unix(unversioned.Now().Time.Unix()-15, 0) job.Status.StartTime = &start job.Status.Conditions = append(job.Status.Conditions, newCondition(extensions.JobFailed, "DeadlineExceeded", "Job was active longer than specified deadline")) manager.jobStore.Store.Add(job) err := manager.syncJob(getKey(job, t)) if err != nil { t.Errorf("Unexpected error when syncing jobs %v", err) } if len(fakePodControl.Templates) != 0 { t.Errorf("Unexpected number of creates. Expected %d, saw %d\n", 0, len(fakePodControl.Templates)) } if len(fakePodControl.DeletePodName) != 0 { t.Errorf("Unexpected number of deletes. Expected %d, saw %d\n", 0, len(fakePodControl.DeletePodName)) } if actual != nil { t.Error("Unexpected job modification") } }
func TestStoreEventsSingleEventInput(t *testing.T) { fakeSink := NewFakeSink() eventTime := kube_api_unv.Unix(12345, 0) eventSourceHostname := "event1HostName" eventReason := "event1" events := []kube_api.Event{ { Reason: eventReason, LastTimestamp: eventTime, Source: kube_api.EventSource{ Host: eventSourceHostname, }, }, } //expect msg string timeStr, err := eventTime.MarshalJSON() assert.NoError(t, err) msgString := fmt.Sprintf(`{"EventMessage":"","EventReason":"%s","EventTimestamp":%s,"EventCount":0,"EventInvolvedObject":{},"EventSource":{"host":"%s"}}`, eventReason, string(timeStr), eventSourceHostname) err = fakeSink.StoreEvents(events) assert.NoError(t, err) assert.Equal(t, 1, len(fakeSink.fakeProducer.msgs)) assert.Equal(t, msgString, fakeSink.fakeProducer.msgs[0].message) }
func TestStoreEventsSingleEventInput(t *testing.T) { fakeSink := NewFakeOpenTSDBSink(true, true) eventTime := kube_api_unv.Unix(12345, 0) eventSourceHostname := fakeNodeIp eventReason := "created" involvedObject := kube_api.ObjectReference{ Kind: "Pod", Name: fakePodName, UID: types.UID(fakePodUid), Namespace: "default", } events := []kube_api.Event{ { Reason: eventReason, LastTimestamp: eventTime, Source: kube_api.EventSource{ Host: fakeNodeIp, }, InvolvedObject: involvedObject, }, } err := fakeSink.StoreEvents(events) assert.NoError(t, err) assert.Equal(t, 1, len(fakeSink.fakeClient.receivedDataPoints)) assert.Equal(t, eventMetricName, fakeSink.fakeClient.receivedDataPoints[0].Metric) assert.Equal(t, 4, len(fakeSink.fakeClient.receivedDataPoints[0].Tags)) assert.Equal(t, eventTime.Time.Unix(), fakeSink.fakeClient.receivedDataPoints[0].Timestamp) assert.Equal(t, fakePodUid, fakeSink.fakeClient.receivedDataPoints[0].Tags["pod_id"]) assert.Equal(t, eventSourceHostname, fakeSink.fakeClient.receivedDataPoints[0].Tags[sink_api.LabelHostname.Key]) assert.Contains(t, fakeSink.fakeClient.receivedDataPoints[0].Value, eventReason) }
func TestStoreEventsSingleEventInput(t *testing.T) { // Arrange fakeSink := NewFakeSink() eventTime := kube_api_unv.Unix(12345, 0) events := []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 TestStoreEventsSingleEventInput(t *testing.T) { // Arrange fakeSink := NewFakeSink() eventTime := kube_api_unv.Unix(12345, 0) eventSourceHostname := "event1HostName" eventReason := "event1" events := []kube_api.Event{ { Reason: eventReason, LastTimestamp: eventTime, Source: kube_api.EventSource{ Host: eventSourceHostname, }, }, } // Act err := fakeSink.StoreEvents(events) // Assert assert.NoError(t, err) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls) /* actual */) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls[0].points) /* actual */) assert.Equal(t, eventMeasurementName /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].points[0].Measurement /* actual */) assert.Equal(t, 2 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls[0].points[0].Tags) /* actual */) assert.Equal(t, 1 /* expected */, len(fakeSink.fakeClient.capturedWriteCalls[0].points[0].Fields) /* actual */) assert.Equal(t, eventTime.UTC() /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].points[0].Time /* actual */) assert.Equal(t, "" /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].points[0].Tags["pod_id"] /* actual */) assert.Equal(t, eventSourceHostname /* expected */, fakeSink.fakeClient.capturedWriteCalls[0].points[0].Tags["hostname"] /* actual */) assert.Contains(t, fakeSink.fakeClient.capturedWriteCalls[0].points[0].Fields["value"], eventReason) }
func TestAddOrUpdateEventExisting(t *testing.T) { // Arrange eventCache := NewEventCache() event1Time := unversioned.Unix(2324, 2342) event2Time := unversioned.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 eventCache.addOrUpdateEvent(&event1) result1 := eventCache.addOrUpdateEvent(&event2) result2 := eventCache.getEvent(&event1) // Assert compareEventWithHistoryEntry(&event2, &result1, t) compareEventWithHistoryEntry(&event2, &result2, t) }
func TestStoreEventsMultipleEventsInput(t *testing.T) { fakeSink := NewFakeOpenTSDBSink(true, true) event1Time := kube_api_unv.Unix(12345, 0) event2Time := kube_api_unv.Unix(12366, 0) event1SourceHostname := "event1HostName" event2SourceHostname := "event2HostName" event1Reason := "event1" event2Reason := "event2" events := []kube_api.Event{ { Reason: event1Reason, LastTimestamp: event1Time, Source: kube_api.EventSource{ Host: event1SourceHostname, }, }, { Reason: event2Reason, LastTimestamp: event2Time, Source: kube_api.EventSource{ Host: event2SourceHostname, }, }, } err := fakeSink.StoreEvents(events) assert.NoError(t, err) assert.Equal(t, 2, len(fakeSink.fakeClient.receivedDataPoints)) assert.Equal(t, eventMetricName, fakeSink.fakeClient.receivedDataPoints[0].Metric) assert.Equal(t, 2, len(fakeSink.fakeClient.receivedDataPoints[0].Tags)) assert.Equal(t, event1Time.Time.Unix(), fakeSink.fakeClient.receivedDataPoints[0].Timestamp) assert.Equal(t, "", fakeSink.fakeClient.receivedDataPoints[0].Tags["pod_id"]) assert.Equal(t, event1SourceHostname, fakeSink.fakeClient.receivedDataPoints[0].Tags[sink_api.LabelHostname.Key]) assert.Contains(t, fakeSink.fakeClient.receivedDataPoints[0].Value, event1Reason) assert.Equal(t, eventMetricName, fakeSink.fakeClient.receivedDataPoints[1].Metric) assert.Equal(t, 2, len(fakeSink.fakeClient.receivedDataPoints[1].Tags)) assert.Equal(t, event2Time.Time.Unix(), fakeSink.fakeClient.receivedDataPoints[1].Timestamp) assert.Equal(t, "", fakeSink.fakeClient.receivedDataPoints[1].Tags["pod_id"]) assert.Equal(t, event2SourceHostname, fakeSink.fakeClient.receivedDataPoints[1].Tags[sink_api.LabelHostname.Key]) assert.Contains(t, fakeSink.fakeClient.receivedDataPoints[1].Value, event2Reason) }
func fuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( 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 *runtime.Object, c fuzz.Continue) { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "unknown.group/unknown", Kind: "Something", }, ContentType: "application/json", Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), } }, func(j *unversioned.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 *kapi.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 = unversioned.Unix(sec, nsec).Rfc3339Copy() }, func(j *kapi.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() }, ) return f }
func TestNewSecretListCreation(t *testing.T) { cases := []struct { k8sRs *api.SecretList expected *SecretList namespace *common.NamespaceQuery }{ { k8SecretList, &SecretList{ Secrets: []Secret{ { ObjectMeta: common.ObjectMeta{ Name: "user1", Namespace: "foo", CreationTimestamp: unversioned.Unix(111, 222), }, TypeMeta: common.NewTypeMeta(common.ResourceKindSecret), }, { ObjectMeta: common.ObjectMeta{ Name: "user2", Namespace: "foo", CreationTimestamp: unversioned.Unix(111, 222), }, TypeMeta: common.NewTypeMeta(common.ResourceKindSecret), }, }, ListMeta: common.ListMeta{2}, }, common.NewNamespaceQuery([]string{"foo"}), }, } for _, c := range cases { actual := NewSecretList(c.k8sRs.Items, dataselect.NoDataSelect) if !reflect.DeepEqual(actual, c.expected) { t.Errorf("NewSecretList() ==\n %#v\nExpected: %#v", actual, c.expected) } } }
func TestSortingPrinter(t *testing.T) { intPtr := func(val int32) *int32 { return &val } a := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "a", }, } b := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "b", }, } c := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "c", }, } tests := []struct { obj runtime.Object sort runtime.Object field string name string }{ { name: "in-order-already", obj: &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Name: "a", }, }, { ObjectMeta: api.ObjectMeta{ Name: "b", }, }, { ObjectMeta: api.ObjectMeta{ Name: "c", }, }, }, }, sort: &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Name: "a", }, }, { ObjectMeta: api.ObjectMeta{ Name: "b", }, }, { ObjectMeta: api.ObjectMeta{ Name: "c", }, }, }, }, field: "{.metadata.name}", }, { name: "reverse-order", obj: &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Name: "b", }, }, { ObjectMeta: api.ObjectMeta{ Name: "c", }, }, { ObjectMeta: api.ObjectMeta{ Name: "a", }, }, }, }, sort: &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Name: "a", }, }, { ObjectMeta: api.ObjectMeta{ Name: "b", }, }, { ObjectMeta: api.ObjectMeta{ Name: "c", }, }, }, }, field: "{.metadata.name}", }, { name: "random-order-timestamp", obj: &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ CreationTimestamp: unversioned.Unix(300, 0), }, }, { ObjectMeta: api.ObjectMeta{ CreationTimestamp: unversioned.Unix(100, 0), }, }, { ObjectMeta: api.ObjectMeta{ CreationTimestamp: unversioned.Unix(200, 0), }, }, }, }, sort: &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ CreationTimestamp: unversioned.Unix(100, 0), }, }, { ObjectMeta: api.ObjectMeta{ CreationTimestamp: unversioned.Unix(200, 0), }, }, { ObjectMeta: api.ObjectMeta{ CreationTimestamp: unversioned.Unix(300, 0), }, }, }, }, field: "{.metadata.creationTimestamp}", }, { name: "random-order-numbers", obj: &api.ReplicationControllerList{ Items: []api.ReplicationController{ { Spec: api.ReplicationControllerSpec{ Replicas: intPtr(5), }, }, { Spec: api.ReplicationControllerSpec{ Replicas: intPtr(1), }, }, { Spec: api.ReplicationControllerSpec{ Replicas: intPtr(9), }, }, }, }, sort: &api.ReplicationControllerList{ Items: []api.ReplicationController{ { Spec: api.ReplicationControllerSpec{ Replicas: intPtr(1), }, }, { Spec: api.ReplicationControllerSpec{ Replicas: intPtr(5), }, }, { Spec: api.ReplicationControllerSpec{ Replicas: intPtr(9), }, }, }, }, field: "{.spec.replicas}", }, { name: "v1.List in order", obj: &api.List{ Items: []runtime.RawExtension{ {Raw: encodeOrDie(a)}, {Raw: encodeOrDie(b)}, {Raw: encodeOrDie(c)}, }, }, sort: &api.List{ Items: []runtime.RawExtension{ {Raw: encodeOrDie(a)}, {Raw: encodeOrDie(b)}, {Raw: encodeOrDie(c)}, }, }, field: "{.metadata.name}", }, { name: "v1.List in reverse", obj: &api.List{ Items: []runtime.RawExtension{ {Raw: encodeOrDie(c)}, {Raw: encodeOrDie(b)}, {Raw: encodeOrDie(a)}, }, }, sort: &api.List{ Items: []runtime.RawExtension{ {Raw: encodeOrDie(a)}, {Raw: encodeOrDie(b)}, {Raw: encodeOrDie(c)}, }, }, field: "{.metadata.name}", }, } for _, test := range tests { sort := &SortingPrinter{SortField: test.field, Decoder: internal.Codecs.UniversalDecoder()} if err := sort.sortObj(test.obj); err != nil { t.Errorf("unexpected error: %v (%s)", err, test.name) continue } if !reflect.DeepEqual(test.obj, test.sort) { t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v", test.name, test.sort, test.obj) } } }
// FuzzerFor can randomly populate api objects that are destined for version. func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *int, c fuzz.Continue) { *j = int(c.Int31()) }, func(j **int, c fuzz.Continue) { if c.RandBool() { i := int(c.Int31()) *j = &i } else { *j = nil } }, func(q *resource.Quantity, c fuzz.Continue) { *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) }, 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 *unversioned.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 = unversioned.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 *unversioned.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.ListOptions, c fuzz.Continue) { label, _ := labels.Parse("a=b") j.LabelSelector = label field, _ := fields.ParseSelector("a=b") j.FieldSelector = field }, func(j *api.PodExecOptions, c fuzz.Continue) { j.Stdout = true j.Stderr = true }, func(j *api.PodAttachOptions, c fuzz.Continue) { j.Stdout = true j.Stderr = true }, func(s *api.PodSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // has a default value ttl := int64(30) if c.RandBool() { ttl = int64(c.Uint32()) } s.TerminationGracePeriodSeconds = &ttl c.Fuzz(s.SecurityContext) if s.SecurityContext == nil { s.SecurityContext = new(api.PodSecurityContext) } }, 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.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 *extensions.DeploymentStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // Ensure that strategyType is one of valid values. strategyTypes := []extensions.DeploymentStrategyType{extensions.RecreateDeploymentStrategyType, extensions.RollingUpdateDeploymentStrategyType} j.Type = strategyTypes[c.Rand.Intn(len(strategyTypes))] if j.Type != extensions.RollingUpdateDeploymentStrategyType { j.RollingUpdate = nil } else { rollingUpdate := extensions.RollingUpdateDeployment{} if c.RandBool() { rollingUpdate.MaxUnavailable = intstr.FromInt(int(c.RandUint64())) rollingUpdate.MaxSurge = intstr.FromInt(int(c.RandUint64())) } else { rollingUpdate.MaxSurge = intstr.FromString(fmt.Sprintf("%d%%", c.RandUint64())) } j.RollingUpdate = &rollingUpdate } }, func(j *batch.JobSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again completions := int32(c.Rand.Int31()) parallelism := int32(c.Rand.Int31()) j.Completions = &completions j.Parallelism = ¶llelism if c.Rand.Int31()%2 == 0 { j.ManualSelector = newBool(true) } else { j.ManualSelector = nil } }, 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{ // We do not set TypeMeta here because it is not carried through a round trip Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), ContentType: runtime.ContentTypeJSON, } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *j = t } }, func(q *api.ResourceRequirements, c fuzz.Continue) { randomQuantity := func() resource.Quantity { var q resource.Quantity c.Fuzz(&q) return q } q.Limits = make(api.ResourceList) q.Requests = make(api.ResourceList) cpuLimit := randomQuantity() q.Limits[api.ResourceCPU] = *cpuLimit.Copy() q.Requests[api.ResourceCPU] = *cpuLimit.Copy() memoryLimit := randomQuantity() q.Limits[api.ResourceMemory] = *memoryLimit.Copy() q.Requests[api.ResourceMemory] = *memoryLimit.Copy() storageLimit := randomQuantity() q.Limits[api.ResourceStorage] = *storageLimit.Copy() q.Requests[api.ResourceStorage] = *storageLimit.Copy() }, func(q *api.LimitRangeItem, c fuzz.Continue) { var cpuLimit resource.Quantity c.Fuzz(&cpuLimit) q.Type = api.LimitTypeContainer q.Default = make(api.ResourceList) q.Default[api.ResourceCPU] = *(cpuLimit.Copy()) q.DefaultRequest = make(api.ResourceList) q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy()) q.Max = make(api.ResourceList) q.Max[api.ResourceCPU] = *(cpuLimit.Copy()) q.Min = make(api.ResourceList) q.Min[api.ResourceCPU] = *(cpuLimit.Copy()) q.MaxLimitRequestRatio = make(api.ResourceList) q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10") }, 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))] }, // Only api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be // defaulted to a version otherwise roundtrip will fail // For the remaining volume plugins the default fuzzer is enough. func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) { m.Path = c.RandString() versions := []string{"v1"} m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] m.FieldRef.FieldPath = c.RandString() }, 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())) t := v.Field(i).Addr() for v.Field(i).IsNil() { c.Fuzz(t.Interface()) } }, func(i *api.ISCSIVolumeSource, c fuzz.Continue) { i.ISCSIInterface = c.RandString() if i.ISCSIInterface == "" { i.ISCSIInterface = "default" } }, 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(p *api.Probe, c fuzz.Continue) { c.FuzzNoCustom(p) // These fields have default values. intFieldsWithDefaults := [...]string{"TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"} v := reflect.ValueOf(p).Elem() for _, field := range intFieldsWithDefaults { f := v.FieldByName(field) if f.Int() == 0 { f.SetInt(1) } } }, 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{} var versions []unversioned.GroupVersion for _, testGroup := range testapi.Groups { versions = append(versions, *testGroup.GroupVersion()) } ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))].String() ev.ValueFrom.FieldRef.FieldPath = c.RandString() } }, func(sc *api.SecurityContext, c fuzz.Continue) { c.FuzzNoCustom(sc) // fuzz self without calling this function again if c.RandBool() { priv := c.RandBool() sc.Privileged = &priv } if c.RandBool() { 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(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.Type { case intstr.Int: ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero case intstr.String: 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" }, func(s *api.NodeStatus, c fuzz.Continue) { c.FuzzNoCustom(s) s.Allocatable = s.Capacity }, func(s *extensions.HorizontalPodAutoscalerSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again minReplicas := int32(c.Rand.Int31()) s.MinReplicas = &minReplicas s.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int32(c.RandUint64())} }, func(s *extensions.SubresourceReference, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.Subresource = "scale" }, func(psp *extensions.PodSecurityPolicySpec, c fuzz.Continue) { c.FuzzNoCustom(psp) // fuzz self without calling this function again runAsUserRules := []extensions.RunAsUserStrategy{extensions.RunAsUserStrategyMustRunAsNonRoot, extensions.RunAsUserStrategyMustRunAs, extensions.RunAsUserStrategyRunAsAny} psp.RunAsUser.Rule = runAsUserRules[c.Rand.Intn(len(runAsUserRules))] seLinuxRules := []extensions.SELinuxStrategy{extensions.SELinuxStrategyRunAsAny, extensions.SELinuxStrategyMustRunAs} psp.SELinux.Rule = seLinuxRules[c.Rand.Intn(len(seLinuxRules))] }, func(s *extensions.Scale, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again // TODO: Implement a fuzzer to generate valid keys, values and operators for // selector requirements. if s.Status.Selector != nil { s.Status.Selector = &unversioned.LabelSelector{ MatchLabels: map[string]string{ "testlabelkey": "testlabelval", }, MatchExpressions: []unversioned.LabelSelectorRequirement{ { Key: "testkey", Operator: unversioned.LabelSelectorOpIn, Values: []string{"val1", "val2", "val3"}, }, }, } } }, ) return f }
func TestGetJobListFromChannels(t *testing.T) { var jobCompletions int32 = 21 cases := []struct { k8sRs batch.JobList k8sRsError error pods *api.PodList expected *JobList expectedError error }{ { batch.JobList{}, nil, &api.PodList{}, &JobList{ ListMeta: common.ListMeta{}, CumulativeMetrics: make([]metric.Metric, 0), Jobs: []Job{}}, nil, }, { batch.JobList{}, errors.New("MyCustomError"), &api.PodList{}, nil, errors.New("MyCustomError"), }, { batch.JobList{}, &k8serrors.StatusError{}, &api.PodList{}, nil, &k8serrors.StatusError{}, }, { batch.JobList{}, &k8serrors.StatusError{ErrStatus: unversioned.Status{}}, &api.PodList{}, nil, &k8serrors.StatusError{ErrStatus: unversioned.Status{}}, }, { batch.JobList{}, &k8serrors.StatusError{ErrStatus: unversioned.Status{Reason: "foo-bar"}}, &api.PodList{}, nil, &k8serrors.StatusError{ErrStatus: unversioned.Status{Reason: "foo-bar"}}, }, { batch.JobList{}, &k8serrors.StatusError{ErrStatus: unversioned.Status{Reason: "NotFound"}}, &api.PodList{}, &JobList{ Jobs: make([]Job, 0), }, nil, }, { batch.JobList{ Items: []batch.Job{{ ObjectMeta: api.ObjectMeta{ Name: "rs-name", Namespace: "rs-namespace", Labels: map[string]string{"key": "value"}, CreationTimestamp: unversioned.Unix(111, 222), }, Spec: batch.JobSpec{ Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Completions: &jobCompletions, }, Status: batch.JobStatus{ Active: 7, }, }, { ObjectMeta: api.ObjectMeta{ Name: "rs-name", Namespace: "rs-namespace", Labels: map[string]string{"key": "value"}, CreationTimestamp: unversioned.Unix(111, 222), }, Spec: batch.JobSpec{ Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, }, Status: batch.JobStatus{ Active: 7, }, }, }, }, nil, &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Namespace: "rs-namespace", Labels: map[string]string{"foo": "bar"}, }, Status: api.PodStatus{Phase: api.PodFailed}, }, { ObjectMeta: api.ObjectMeta{ Namespace: "rs-namespace", Labels: map[string]string{"foo": "baz"}, }, Status: api.PodStatus{Phase: api.PodFailed}, }, }, }, &JobList{ ListMeta: common.ListMeta{TotalItems: 2}, CumulativeMetrics: make([]metric.Metric, 0), Jobs: []Job{{ ObjectMeta: common.ObjectMeta{ Name: "rs-name", Namespace: "rs-namespace", Labels: map[string]string{"key": "value"}, CreationTimestamp: unversioned.Unix(111, 222), }, TypeMeta: common.TypeMeta{Kind: common.ResourceKindJob}, Pods: common.PodInfo{ Current: 7, Desired: 21, Failed: 1, Warnings: []common.Event{}, }, }, { ObjectMeta: common.ObjectMeta{ Name: "rs-name", Namespace: "rs-namespace", Labels: map[string]string{"key": "value"}, CreationTimestamp: unversioned.Unix(111, 222), }, TypeMeta: common.TypeMeta{Kind: common.ResourceKindJob}, Pods: common.PodInfo{ Current: 7, Desired: 0, Failed: 1, Warnings: []common.Event{}, }, }}, }, nil, }, } for _, c := range cases { channels := &common.ResourceChannels{ JobList: common.JobListChannel{ List: make(chan *batch.JobList, 1), Error: make(chan error, 1), }, NodeList: common.NodeListChannel{ List: make(chan *api.NodeList, 1), Error: make(chan error, 1), }, ServiceList: common.ServiceListChannel{ List: make(chan *api.ServiceList, 1), Error: make(chan error, 1), }, PodList: common.PodListChannel{ List: make(chan *api.PodList, 1), Error: make(chan error, 1), }, EventList: common.EventListChannel{ List: make(chan *api.EventList, 1), Error: make(chan error, 1), }, } channels.JobList.Error <- c.k8sRsError channels.JobList.List <- &c.k8sRs channels.NodeList.List <- &api.NodeList{} channels.NodeList.Error <- nil channels.ServiceList.List <- &api.ServiceList{} channels.ServiceList.Error <- nil channels.PodList.List <- c.pods channels.PodList.Error <- nil channels.EventList.List <- &api.EventList{} channels.EventList.Error <- nil actual, err := GetJobListFromChannels(channels, dataselect.NoDataSelect, nil) if !reflect.DeepEqual(actual, c.expected) { t.Errorf("GetJobListFromChannels() ==\n %#v\nExpected: %#v", actual, c.expected) } if !reflect.DeepEqual(err, c.expectedError) { t.Errorf("GetJobListFromChannels() ==\n %#v\nExpected: %#v", err, c.expectedError) } } }
func TestGetPetSetListFromChannels(t *testing.T) { cases := []struct { k8sRs apps.PetSetList k8sRsError error pods *api.PodList expected *PetSetList expectedError error }{ { apps.PetSetList{}, nil, &api.PodList{}, &PetSetList{[]PetSet{}}, nil, }, { apps.PetSetList{}, errors.New("MyCustomError"), &api.PodList{}, nil, errors.New("MyCustomError"), }, { apps.PetSetList{}, &k8serrors.StatusError{}, &api.PodList{}, nil, &k8serrors.StatusError{}, }, { apps.PetSetList{}, &k8serrors.StatusError{ErrStatus: unversioned.Status{}}, &api.PodList{}, nil, &k8serrors.StatusError{ErrStatus: unversioned.Status{}}, }, { apps.PetSetList{}, &k8serrors.StatusError{ErrStatus: unversioned.Status{Reason: "foo-bar"}}, &api.PodList{}, nil, &k8serrors.StatusError{ErrStatus: unversioned.Status{Reason: "foo-bar"}}, }, { apps.PetSetList{}, &k8serrors.StatusError{ErrStatus: unversioned.Status{Reason: "NotFound"}}, &api.PodList{}, &PetSetList{ PetSets: make([]PetSet, 0), }, nil, }, { apps.PetSetList{ Items: []apps.PetSet{{ ObjectMeta: api.ObjectMeta{ Name: "rs-name", Namespace: "rs-namespace", Labels: map[string]string{"key": "value"}, CreationTimestamp: unversioned.Unix(111, 222), }, Spec: apps.PetSetSpec{ Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Replicas: 21, }, Status: apps.PetSetStatus{ Replicas: 7, }, }}, }, nil, &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Namespace: "rs-namespace", Labels: map[string]string{"foo": "bar"}, }, Status: api.PodStatus{Phase: api.PodFailed}, }, { ObjectMeta: api.ObjectMeta{ Namespace: "rs-namespace", Labels: map[string]string{"foo": "baz"}, }, Status: api.PodStatus{Phase: api.PodFailed}, }, }, }, &PetSetList{ []PetSet{{ ObjectMeta: common.ObjectMeta{ Name: "rs-name", Namespace: "rs-namespace", Labels: map[string]string{"key": "value"}, CreationTimestamp: unversioned.Unix(111, 222), }, TypeMeta: common.TypeMeta{Kind: common.ResourceKindPetSet}, Pods: common.PodInfo{ Current: 7, Desired: 21, Failed: 1, Warnings: []common.Event{}, }, }}, }, nil, }, } for _, c := range cases { channels := &common.ResourceChannels{ PetSetList: common.PetSetListChannel{ List: make(chan *apps.PetSetList, 1), Error: make(chan error, 1), }, NodeList: common.NodeListChannel{ List: make(chan *api.NodeList, 1), Error: make(chan error, 1), }, ServiceList: common.ServiceListChannel{ List: make(chan *api.ServiceList, 1), Error: make(chan error, 1), }, PodList: common.PodListChannel{ List: make(chan *api.PodList, 1), Error: make(chan error, 1), }, EventList: common.EventListChannel{ List: make(chan *api.EventList, 1), Error: make(chan error, 1), }, } channels.PetSetList.Error <- c.k8sRsError channels.PetSetList.List <- &c.k8sRs channels.NodeList.List <- &api.NodeList{} channels.NodeList.Error <- nil channels.ServiceList.List <- &api.ServiceList{} channels.ServiceList.Error <- nil channels.PodList.List <- c.pods channels.PodList.Error <- nil channels.EventList.List <- &api.EventList{} channels.EventList.Error <- nil actual, err := GetPetSetListFromChannels(channels) if !reflect.DeepEqual(actual, c.expected) { t.Errorf("GetPetSetListChannels() ==\n %#v\nExpected: %#v", actual, c.expected) } if !reflect.DeepEqual(err, c.expectedError) { t.Errorf("GetPetSetListChannels() ==\n %#v\nExpected: %#v", err, c.expectedError) } } }
// compare two unversioned.Time values. func timeLess(i, j reflect.Value) (bool, bool) { if i.Type() != reflect.TypeOf(unversioned.Unix(0, 0)) { return false, false } return true, i.MethodByName("Before").Call([]reflect.Value{j})[0].Bool() }
func TestSyncJobPastDeadline(t *testing.T) { testCases := map[string]struct { // job setup parallelism int completions int activeDeadlineSeconds int64 startTime int64 // pod setup activePods int succeededPods int failedPods int // expectations expectedDeletions int expectedActive int expectedSucceeded int expectedFailed int }{ "activeDeadlineSeconds less than single pod execution": { 1, 1, 10, 15, 1, 0, 0, 1, 0, 0, 1, }, "activeDeadlineSeconds bigger than single pod execution": { 1, 2, 10, 15, 1, 1, 0, 1, 0, 1, 1, }, "activeDeadlineSeconds times-out before any pod starts": { 1, 1, 10, 10, 0, 0, 0, 0, 0, 0, 0, }, } for name, tc := range testCases { // job manager setup clientset := clientset.NewForConfigOrDie(&client.Config{Host: "", ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) manager := NewJobController(clientset, controller.NoResyncPeriodFunc) fakePodControl := controller.FakePodControl{} manager.podControl = &fakePodControl manager.podStoreSynced = alwaysReady var actual *extensions.Job manager.updateHandler = func(job *extensions.Job) error { actual = job return nil } // job & pods setup job := newJob(tc.parallelism, tc.completions) job.Spec.ActiveDeadlineSeconds = &tc.activeDeadlineSeconds start := unversioned.Unix(unversioned.Now().Time.Unix()-tc.startTime, 0) job.Status.StartTime = &start manager.jobStore.Store.Add(job) for _, pod := range newPodList(tc.activePods, api.PodRunning, job) { manager.podStore.Store.Add(&pod) } for _, pod := range newPodList(tc.succeededPods, api.PodSucceeded, job) { manager.podStore.Store.Add(&pod) } for _, pod := range newPodList(tc.failedPods, api.PodFailed, job) { manager.podStore.Store.Add(&pod) } // run err := manager.syncJob(getKey(job, t)) if err != nil { t.Errorf("%s: unexpected error when syncing jobs %v", err) } // validate created/deleted pods if len(fakePodControl.Templates) != 0 { t.Errorf("%s: unexpected number of creates. Expected 0, saw %d\n", name, len(fakePodControl.Templates)) } if len(fakePodControl.DeletePodName) != tc.expectedDeletions { t.Errorf("%s: unexpected number of deletes. Expected %d, saw %d\n", name, tc.expectedDeletions, len(fakePodControl.DeletePodName)) } // validate status if actual.Status.Active != tc.expectedActive { t.Errorf("%s: unexpected number of active pods. Expected %d, saw %d\n", name, tc.expectedActive, actual.Status.Active) } if actual.Status.Succeeded != tc.expectedSucceeded { t.Errorf("%s: unexpected number of succeeded pods. Expected %d, saw %d\n", name, tc.expectedSucceeded, actual.Status.Succeeded) } if actual.Status.Failed != tc.expectedFailed { t.Errorf("%s: unexpected number of failed pods. Expected %d, saw %d\n", name, tc.expectedFailed, actual.Status.Failed) } if actual.Status.StartTime == nil { t.Errorf("%s: .status.startTime was not set", name) } // validate conditions if !getCondition(actual, extensions.JobFailed) { t.Errorf("%s: expected fail condition. Got %#v", name, actual.Status.Conditions) } } }
func TestDescribeBuildDuration(t *testing.T) { type testBuild struct { build *buildapi.Build output string } creation := unversioned.Date(2015, time.April, 9, 6, 0, 0, 0, time.Local) // now a minute ago minuteAgo := unversioned.Unix(unversioned.Now().Rfc3339Copy().Time.Unix()-60, 0) start := unversioned.Date(2015, time.April, 9, 6, 1, 0, 0, time.Local) completion := unversioned.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.BuildStatus{ Phase: buildapi.BuildPhaseNew, Duration: zeroDuration, }, }, "waiting for 1m0s", }, { // 1 - build pending &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: minuteAgo}, Status: buildapi.BuildStatus{ Phase: buildapi.BuildPhasePending, Duration: zeroDuration, }, }, "waiting for 1m0s", }, { // 2 - build running &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ StartTimestamp: &start, Phase: buildapi.BuildPhaseRunning, Duration: duration, }, }, "running for 1m0s", }, { // 3 - build completed &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ StartTimestamp: &start, CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseComplete, Duration: duration, }, }, "1m0s", }, { // 4 - build failed &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ StartTimestamp: &start, CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseFailed, Duration: duration, }, }, "1m0s", }, { // 5 - build error &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ StartTimestamp: &start, CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseError, Duration: duration, }, }, "1m0s", }, { // 6 - build cancelled before running, start time wasn't set yet &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseCancelled, Duration: duration, }, }, "waited for 2m0s", }, { // 7 - build cancelled while running, start time is set already &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ StartTimestamp: &start, CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseCancelled, Duration: duration, }, }, "1m0s", }, { // 8 - build failed before running, start time wasn't set yet &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseFailed, Duration: duration, }, }, "waited for 2m0s", }, { // 9 - build error before running, start time wasn't set yet &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{CreationTimestamp: creation}, Status: buildapi.BuildStatus{ CompletionTimestamp: &completion, Phase: buildapi.BuildPhaseError, 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 schema.GroupVersion, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *int, c fuzz.Continue) { *j = int(c.Int31()) }, func(j **int, c fuzz.Continue) { if c.RandBool() { i := int(c.Int31()) *j = &i } else { *j = nil } }, func(q *resource.Quantity, c fuzz.Continue) { *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) }, 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 *unversioned.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 = unversioned.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 *unversioned.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.ListOptions, c fuzz.Continue) { label, _ := labels.Parse("a=b") j.LabelSelector = label field, _ := fields.ParseSelector("a=b") j.FieldSelector = field }, func(j *api.PodExecOptions, c fuzz.Continue) { j.Stdout = true j.Stderr = true }, func(j *api.PodAttachOptions, c fuzz.Continue) { j.Stdout = true j.Stderr = true }, func(s *api.PodSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // has a default value ttl := int64(30) if c.RandBool() { ttl = int64(c.Uint32()) } s.TerminationGracePeriodSeconds = &ttl c.Fuzz(s.SecurityContext) if s.SecurityContext == nil { s.SecurityContext = new(api.PodSecurityContext) } }, 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.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 *extensions.DeploymentStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // Ensure that strategyType is one of valid values. strategyTypes := []extensions.DeploymentStrategyType{extensions.RecreateDeploymentStrategyType, extensions.RollingUpdateDeploymentStrategyType} j.Type = strategyTypes[c.Rand.Intn(len(strategyTypes))] if j.Type != extensions.RollingUpdateDeploymentStrategyType { j.RollingUpdate = nil } else { rollingUpdate := extensions.RollingUpdateDeployment{} if c.RandBool() { rollingUpdate.MaxUnavailable = intstr.FromInt(int(c.RandUint64())) rollingUpdate.MaxSurge = intstr.FromInt(int(c.RandUint64())) } else { rollingUpdate.MaxSurge = intstr.FromString(fmt.Sprintf("%d%%", c.RandUint64())) } j.RollingUpdate = &rollingUpdate } }, func(j *batch.JobSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again completions := int32(c.Rand.Int31()) parallelism := int32(c.Rand.Int31()) j.Completions = &completions j.Parallelism = ¶llelism if c.Rand.Int31()%2 == 0 { j.ManualSelector = newBool(true) } else { j.ManualSelector = nil } }, func(sj *batch.CronJobSpec, c fuzz.Continue) { c.FuzzNoCustom(sj) suspend := c.RandBool() sj.Suspend = &suspend sds := int64(c.RandUint64()) sj.StartingDeadlineSeconds = &sds sj.Schedule = c.RandString() }, func(cp *batch.ConcurrencyPolicy, c fuzz.Continue) { policies := []batch.ConcurrencyPolicy{batch.AllowConcurrent, batch.ForbidConcurrent, batch.ReplaceConcurrent} *cp = policies[c.Rand.Intn(len(policies))] }, 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{ // We do not set TypeMeta here because it is not carried through a round trip Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), ContentType: runtime.ContentTypeJSON, } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *j = t } }, func(q *api.ResourceRequirements, c fuzz.Continue) { randomQuantity := func() resource.Quantity { var q resource.Quantity c.Fuzz(&q) // precalc the string for benchmarking purposes _ = q.String() return q } q.Limits = make(api.ResourceList) q.Requests = make(api.ResourceList) cpuLimit := randomQuantity() q.Limits[api.ResourceCPU] = *cpuLimit.Copy() q.Requests[api.ResourceCPU] = *cpuLimit.Copy() memoryLimit := randomQuantity() q.Limits[api.ResourceMemory] = *memoryLimit.Copy() q.Requests[api.ResourceMemory] = *memoryLimit.Copy() storageLimit := randomQuantity() q.Limits[api.ResourceStorage] = *storageLimit.Copy() q.Requests[api.ResourceStorage] = *storageLimit.Copy() }, func(q *api.LimitRangeItem, c fuzz.Continue) { var cpuLimit resource.Quantity c.Fuzz(&cpuLimit) q.Type = api.LimitTypeContainer q.Default = make(api.ResourceList) q.Default[api.ResourceCPU] = *(cpuLimit.Copy()) q.DefaultRequest = make(api.ResourceList) q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy()) q.Max = make(api.ResourceList) q.Max[api.ResourceCPU] = *(cpuLimit.Copy()) q.Min = make(api.ResourceList) q.Min[api.ResourceCPU] = *(cpuLimit.Copy()) q.MaxLimitRequestRatio = make(api.ResourceList) q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10") }, 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))] }, // api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be // defaulted to a version otherwise roundtrip will fail func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) { m.Path = c.RandString() versions := []string{"v1"} m.FieldRef = &api.ObjectFieldSelector{} m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] m.FieldRef.FieldPath = c.RandString() c.Fuzz(m.Mode) if m.Mode != nil { *m.Mode &= 0777 } }, func(s *api.SecretVolumeSource, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again // DefaultMode should always be set, it has a default // value and it is expected to be between 0 and 0777 var mode int32 c.Fuzz(&mode) mode &= 0777 s.DefaultMode = &mode }, func(cm *api.ConfigMapVolumeSource, c fuzz.Continue) { c.FuzzNoCustom(cm) // fuzz self without calling this function again // DefaultMode should always be set, it has a default // value and it is expected to be between 0 and 0777 var mode int32 c.Fuzz(&mode) mode &= 0777 cm.DefaultMode = &mode }, func(d *api.DownwardAPIVolumeSource, c fuzz.Continue) { c.FuzzNoCustom(d) // fuzz self without calling this function again // DefaultMode should always be set, it has a default // value and it is expected to be between 0 and 0777 var mode int32 c.Fuzz(&mode) mode &= 0777 d.DefaultMode = &mode }, func(k *api.KeyToPath, c fuzz.Continue) { c.FuzzNoCustom(k) // fuzz self without calling this function again k.Key = c.RandString() k.Path = c.RandString() // Mode is not mandatory, but if it is set, it should be // a value between 0 and 0777 if k.Mode != nil { *k.Mode &= 0777 } }, 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())) t := v.Field(i).Addr() for v.Field(i).IsNil() { c.Fuzz(t.Interface()) } }, func(i *api.ISCSIVolumeSource, c fuzz.Continue) { i.ISCSIInterface = c.RandString() if i.ISCSIInterface == "" { i.ISCSIInterface = "default" } }, 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(p *api.Probe, c fuzz.Continue) { c.FuzzNoCustom(p) // These fields have default values. intFieldsWithDefaults := [...]string{"TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"} v := reflect.ValueOf(p).Elem() for _, field := range intFieldsWithDefaults { f := v.FieldByName(field) if f.Int() == 0 { f.SetInt(1) } } }, 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{} var versions []schema.GroupVersion for _, testGroup := range testapi.Groups { versions = append(versions, *testGroup.GroupVersion()) } ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))].String() ev.ValueFrom.FieldRef.FieldPath = c.RandString() } }, func(sc *api.SecurityContext, c fuzz.Continue) { c.FuzzNoCustom(sc) // fuzz self without calling this function again if c.RandBool() { priv := c.RandBool() sc.Privileged = &priv } if c.RandBool() { 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(s *api.Secret, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.Type = api.SecretTypeOpaque }, func(r *api.RBDVolumeSource, c fuzz.Continue) { r.RBDPool = c.RandString() if r.RBDPool == "" { r.RBDPool = "rbd" } r.RadosUser = c.RandString() if r.RadosUser == "" { r.RadosUser = "******" } r.Keyring = c.RandString() if r.Keyring == "" { r.Keyring = "/etc/ceph/keyring" } }, 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, api.ClaimLost} pvc.Status.Phase = types[c.Rand.Intn(len(types))] }, func(obj *api.AzureDiskVolumeSource, c fuzz.Continue) { if obj.CachingMode == nil { obj.CachingMode = new(api.AzureDataDiskCachingMode) *obj.CachingMode = api.AzureDataDiskCachingNone } if obj.FSType == nil { obj.FSType = new(string) *obj.FSType = "ext4" } if obj.ReadOnly == nil { obj.ReadOnly = new(bool) *obj.ReadOnly = false } }, 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.Type { case intstr.Int: ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero case intstr.String: 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" }, func(s *api.NodeStatus, c fuzz.Continue) { c.FuzzNoCustom(s) s.Allocatable = s.Capacity }, func(s *autoscaling.HorizontalPodAutoscalerSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again minReplicas := int32(c.Rand.Int31()) s.MinReplicas = &minReplicas targetCpu := int32(c.RandUint64()) s.TargetCPUUtilizationPercentage = &targetCpu }, func(psp *extensions.PodSecurityPolicySpec, c fuzz.Continue) { c.FuzzNoCustom(psp) // fuzz self without calling this function again runAsUserRules := []extensions.RunAsUserStrategy{extensions.RunAsUserStrategyMustRunAsNonRoot, extensions.RunAsUserStrategyMustRunAs, extensions.RunAsUserStrategyRunAsAny} psp.RunAsUser.Rule = runAsUserRules[c.Rand.Intn(len(runAsUserRules))] seLinuxRules := []extensions.SELinuxStrategy{extensions.SELinuxStrategyRunAsAny, extensions.SELinuxStrategyMustRunAs} psp.SELinux.Rule = seLinuxRules[c.Rand.Intn(len(seLinuxRules))] }, func(s *extensions.Scale, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again // TODO: Implement a fuzzer to generate valid keys, values and operators for // selector requirements. if s.Status.Selector != nil { s.Status.Selector = &unversioned.LabelSelector{ MatchLabels: map[string]string{ "testlabelkey": "testlabelval", }, MatchExpressions: []unversioned.LabelSelectorRequirement{ { Key: "testkey", Operator: unversioned.LabelSelectorOpIn, Values: []string{"val1", "val2", "val3"}, }, }, } } }, func(r *rbac.RoleRef, c fuzz.Continue) { c.FuzzNoCustom(r) // fuzz self without calling this function again // match defaulter if len(r.APIGroup) == 0 { r.APIGroup = rbac.GroupName } }, func(r *runtime.RawExtension, c fuzz.Continue) { // Pick an arbitrary type and fuzz it types := []runtime.Object{&api.Pod{}, &extensions.Deployment{}, &api.Service{}} obj := types[c.Rand.Intn(len(types))] c.Fuzz(obj) // Find a codec for converting the object to raw bytes. This is necessary for the // api version and kind to be correctly set be serialization. var codec runtime.Codec switch obj.(type) { case *api.Pod: codec = testapi.Default.Codec() case *extensions.Deployment: codec = testapi.Extensions.Codec() case *api.Service: codec = testapi.Default.Codec() default: t.Errorf("Failed to find codec for object type: %T", obj) return } // Convert the object to raw bytes bytes, err := runtime.Encode(codec, obj) if err != nil { t.Errorf("Failed to encode object: %v", err) return } // Set the bytes field on the RawExtension r.Raw = bytes }, func(obj *kubeadm.MasterConfiguration, c fuzz.Continue) { c.FuzzNoCustom(obj) obj.KubernetesVersion = "v10" obj.API.BindPort = 20 obj.Discovery.BindPort = 20 obj.Networking.ServiceSubnet = "foo" obj.Networking.DNSDomain = "foo" }, func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) { c.FuzzNoCustom(obj) obj.APIPort = 20 obj.DiscoveryPort = 20 }, func(s *policy.PodDisruptionBudgetStatus, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.PodDisruptionsAllowed = int32(c.Rand.Intn(2)) }, ) 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 *unversioned.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 = unversioned.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 *unversioned.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(s *api.PodSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // has a default value ttl := int64(30) if c.RandBool() { ttl = int64(c.Uint32()) } s.TerminationGracePeriodSeconds = &ttl if s.SecurityContext == nil { s.SecurityContext = &api.PodSecurityContext{} } }, 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 *experimental.DeploymentStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // Ensure that strategyType is one of valid values. strategyTypes := []experimental.DeploymentStrategyType{experimental.RecreateDeploymentStrategyType, experimental.RollingUpdateDeploymentStrategyType} j.Type = strategyTypes[c.Rand.Intn(len(strategyTypes))] if j.Type != experimental.RollingUpdateDeploymentStrategyType { j.RollingUpdate = nil } else { rollingUpdate := experimental.RollingUpdateDeployment{} if c.RandBool() { rollingUpdate.MaxUnavailable = util.NewIntOrStringFromInt(int(c.RandUint64())) rollingUpdate.MaxSurge = util.NewIntOrStringFromInt(int(c.RandUint64())) } else { rollingUpdate.MaxSurge = util.NewIntOrStringFromString(fmt.Sprintf("%d%%", c.RandUint64())) } j.RollingUpdate = &rollingUpdate } }, func(j *experimental.JobSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again completions := c.Rand.Int() parallelism := c.Rand.Int() j.Completions = &completions j.Parallelism = ¶llelism }, 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(q *api.ResourceRequirements, c fuzz.Continue) { randomQuantity := func() resource.Quantity { return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) } q.Limits = make(api.ResourceList) q.Requests = make(api.ResourceList) cpuLimit := randomQuantity() q.Limits[api.ResourceCPU] = *cpuLimit.Copy() q.Requests[api.ResourceCPU] = *cpuLimit.Copy() memoryLimit := randomQuantity() q.Limits[api.ResourceMemory] = *memoryLimit.Copy() q.Requests[api.ResourceMemory] = *memoryLimit.Copy() storageLimit := randomQuantity() q.Limits[api.ResourceStorage] = *storageLimit.Copy() q.Requests[api.ResourceStorage] = *storageLimit.Copy() }, func(q *api.LimitRangeItem, c fuzz.Continue) { randomQuantity := func() resource.Quantity { return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) } cpuLimit := randomQuantity() q.Type = api.LimitTypeContainer q.Default = make(api.ResourceList) q.Default[api.ResourceCPU] = *(cpuLimit.Copy()) q.DefaultRequest = make(api.ResourceList) q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy()) q.Max = make(api.ResourceList) q.Max[api.ResourceCPU] = *(cpuLimit.Copy()) q.Min = make(api.ResourceList) q.Min[api.ResourceCPU] = *(cpuLimit.Copy()) q.MaxLimitRequestRatio = make(api.ResourceList) q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10") }, 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. f := fuzz.New().NilChance(0).NumElements(1, 1) f.Funcs( // Only api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be // defaulted to a version otherwise roundtrip will fail // For the remaining volume plugins the default fuzzer is enough. func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) { m.Path = c.RandString() versions := []string{"v1"} m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] m.FieldRef.FieldPath = c.RandString() }, ).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 := registered.RegisteredVersions 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" }, func(s *experimental.APIVersion, c fuzz.Continue) { // We can't use c.RandString() here because it may generate empty // string, which will cause tests failure. s.APIGroup = "something" }, ) return f }
"testing" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" ) var k8SecretList = &api.SecretList{ Items: []api.Secret{ { ObjectMeta: api.ObjectMeta{ Name: "user1", Namespace: "foo", CreationTimestamp: unversioned.Unix(111, 222), }, }, { ObjectMeta: api.ObjectMeta{ Name: "user2", Namespace: "foo", CreationTimestamp: unversioned.Unix(111, 222), }, }, }, } func TestNewSecretListCreation(t *testing.T) { cases := []struct { k8sRs *api.SecretList