func TestSortStatusTags(t *testing.T) { tests := []struct { name string tags map[string]TagEventList expected []string }{ { name: "all timestamps here", tags: map[string]TagEventList{ "other": { Items: []TagEvent{ { DockerImageReference: "other-ref", Created: kutil.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []TagEvent{ { DockerImageReference: "latest-ref", Created: kutil.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, "third": { Items: []TagEvent{ { DockerImageReference: "third-ref", Created: kutil.Date(2015, 9, 4, 13, 54, 0, 0, time.UTC), Image: "third-image", }, }, }, }, expected: []string{"third", "latest", "other"}, }, } for _, test := range tests { got := SortStatusTags(test.tags) if !reflect.DeepEqual(test.expected, got) { t.Errorf("%s: tags mismatch: expected %v, got %v", test.name, test.expected, got) } } }
func TestOverlappingRCs(t *testing.T) { client := client.NewOrDie(&client.Config{Host: "", Version: testapi.Version()}) for i := 0; i < 5; i++ { manager := NewReplicationManager(client, 10) manager.podStoreSynced = alwaysReady // Create 10 rcs, shuffled them randomly and insert them into the rc manager's store var controllers []*api.ReplicationController for j := 1; j < 10; j++ { controllerSpec := newReplicationController(1) controllerSpec.CreationTimestamp = util.Date(2014, time.December, j, 0, 0, 0, 0, time.Local) controllerSpec.Name = string(util.NewUUID()) controllers = append(controllers, controllerSpec) } shuffledControllers := shuffle(controllers) for j := range shuffledControllers { manager.rcStore.Store.Add(shuffledControllers[j]) } // Add a pod and make sure only the oldest rc is synced pods := newPodList(nil, 1, api.PodPending, controllers[0]) rcKey := getKey(controllers[0], t) manager.addPod(&pods.Items[0]) queueRC, _ := manager.queue.Get() if queueRC != rcKey { t.Fatalf("Expected to find key %v in queue, found %v", rcKey, queueRC) } } }
func TestLimitedLogAndRetryFinish(t *testing.T) { updater := &buildUpdater{} err := errors.New("funky error") now := kutil.Now() retry := controller.Retry{ Count: 0, StartTimestamp: kutil.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-31, now.Second(), now.Nanosecond(), now.Location()), } if limitedLogAndRetry(updater, 30*time.Minute)(&buildapi.Build{Status: buildapi.BuildStatus{Phase: buildapi.BuildPhaseNew}}, err, retry) { t.Error("Expected no more retries after reaching timeout!") } if updater.Build == nil { t.Fatal("BuildUpdater wasn't called!") } if updater.Build.Status.Phase != buildapi.BuildPhaseFailed { t.Errorf("Expected status %s, got %s!", buildapi.BuildPhaseFailed, updater.Build.Status.Phase) } if !strings.Contains(updater.Build.Status.Message, err.Error()) { t.Errorf("Expected message to contain %v, got %s!", err.Error(), updater.Build.Status.Message) } if updater.Build.Status.CompletionTimestamp == nil { t.Error("Expected CompletionTimestamp to be set!") } }
func TestLimitedLogAndRetryProcessing(t *testing.T) { updater := &buildUpdater{} err := errors.New("funky error") now := kutil.Now() retry := controller.Retry{0, kutil.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-10, now.Second(), now.Nanosecond(), now.Location())} if !limitedLogAndRetry(updater, 30*time.Minute)(&buildapi.Build{Status: buildapi.BuildStatus{Phase: buildapi.BuildPhaseNew}}, err, retry) { t.Error("Expected more retries!") } if updater.Build != nil { t.Fatal("BuildUpdater shouldn't be called!") } }
func TestNodeDeletion(t *testing.T) { fakeNow := util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) fakeNodeHandler := &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status has just been updated. LastHeartbeatTime: fakeNow, LastTransitionTime: fakeNow, }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, { ObjectMeta: api.ObjectMeta{ Name: "node1", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status has just been updated. LastHeartbeatTime: fakeNow, LastTransitionTime: fakeNow, }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0"), *newPod("pod1", "node1")}}), } nodeController := NewNodeController(nil, fakeNodeHandler, 5*time.Minute, NewPodEvictor(util.NewFakeRateLimiter()), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false) nodeController.now = func() util.Time { return fakeNow } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } fakeNodeHandler.Delete("node1") if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } podEvicted := false for _, action := range fakeNodeHandler.Actions() { if action.GetVerb() == "delete" && action.GetResource() == "pods" { podEvicted = true } } if !podEvicted { t.Error("expected pods to be evicted from the deleted node") } }
func TestMonitorNodeStatusUpdateStatus(t *testing.T) { fakeNow := util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) table := []struct { fakeNodeHandler *FakeNodeHandler timeToPass time.Duration newNodeStatus api.NodeStatus expectedEvictPods bool expectedRequestCount int expectedNodes []*api.Node }{ // Node created long time ago, without status: // Expect Unknown status posted from node controller. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 2, // List+Update expectedNodes: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, Reason: fmt.Sprintf("Kubelet never posted node status."), LastHeartbeatTime: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), LastTransitionTime: fakeNow, }, }, }, }, }, }, // Node created recently, without status. // Expect no action from node controller (within startup grace period). { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: fakeNow, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 1, // List expectedNodes: nil, }, // Node created long time ago, with status updated by kubelet exceeds grace period. // Expect Unknown status posted from node controller. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status hasn't been updated for 1hr. LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 3, // (List+)List+Update timeToPass: time.Hour, newNodeStatus: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status hasn't been updated for 1hr. LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, expectedNodes: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, Reason: fmt.Sprintf("Kubelet stopped posting node status."), LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Time{util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)}, }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, }, // Node created long time ago, with status updated recently. // Expect no action from node controller (within monitor grace period). { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status has just been updated. LastHeartbeatTime: fakeNow, LastTransitionTime: fakeNow, }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 1, // List expectedNodes: nil, }, } for _, item := range table { nodeController := NewNodeController(nil, item.fakeNodeHandler, 5*time.Minute, NewPodEvictor(util.NewFakeRateLimiter()), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false) nodeController.now = func() util.Time { return fakeNow } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } if item.timeToPass > 0 { nodeController.now = func() util.Time { return util.Time{Time: fakeNow.Add(item.timeToPass)} } item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } } if item.expectedRequestCount != item.fakeNodeHandler.RequestCount { t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount) } if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) { t.Errorf("expected nodes %+v, got %+v", item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]) } } }
func TestMonitorNodeStatusEvictPods(t *testing.T) { fakeNow := util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) evictionTimeout := 10 * time.Minute table := []struct { fakeNodeHandler *FakeNodeHandler timeToPass time.Duration newNodeStatus api.NodeStatus expectedEvictPods bool description string }{ // Node created recently, with no status (happens only at cluster startup). { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: fakeNow, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, timeToPass: 0, newNodeStatus: api.NodeStatus{}, expectedEvictPods: false, description: "Node created recently, with no status.", }, // Node created long time ago, and kubelet posted NotReady for a short period of time. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionFalse, LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, timeToPass: evictionTimeout, newNodeStatus: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionFalse, // Node status has just been updated, and is NotReady for 10min. LastHeartbeatTime: util.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, expectedEvictPods: false, description: "Node created long time ago, and kubelet posted NotReady for a short period of time.", }, // Node created long time ago, and kubelet posted NotReady for a long period of time. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionFalse, LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, timeToPass: time.Hour, newNodeStatus: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionFalse, // Node status has just been updated, and is NotReady for 1hr. LastHeartbeatTime: util.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, expectedEvictPods: true, description: "Node created long time ago, and kubelet posted NotReady for a long period of time.", }, // Node created long time ago, node controller posted Unknown for a short period of time. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, timeToPass: evictionTimeout - testNodeMonitorGracePeriod, newNodeStatus: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, // Node status was updated by nodecontroller 10min ago LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, expectedEvictPods: false, description: "Node created long time ago, node controller posted Unknown for a short period of time.", }, // Node created long time ago, node controller posted Unknown for a long period of time. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, timeToPass: 60 * time.Minute, newNodeStatus: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, // Node status was updated by nodecontroller 1hr ago LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, }, expectedEvictPods: true, description: "Node created long time ago, node controller posted Unknown for a long period of time.", }, } for _, item := range table { podEvictor := NewPodEvictor(util.NewFakeRateLimiter()) nodeController := NewNodeController(nil, item.fakeNodeHandler, evictionTimeout, podEvictor, testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false) nodeController.now = func() util.Time { return fakeNow } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } if item.timeToPass > 0 { nodeController.now = func() util.Time { return util.Time{Time: fakeNow.Add(item.timeToPass)} } item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } podEvictor.TryEvict(func(nodeName string) { nodeController.deletePods(nodeName) }) podEvicted := false for _, action := range item.fakeNodeHandler.Actions() { if action.GetVerb() == "delete" && action.GetResource() == "pods" { podEvicted = true } } if item.expectedEvictPods != podEvicted { t.Errorf("expected pod eviction: %+v, got %+v for %+v", item.expectedEvictPods, podEvicted, item.description) } } }
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.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) } } }
func mockStreams() []*imageapi.ImageStream { return []*imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{Name: "less-than-three-tags"}, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "other": { Items: []imageapi.TagEvent{ { DockerImageReference: "other-ref", Created: kutil.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: "latest-ref", Created: kutil.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, }, }, }, { ObjectMeta: kapi.ObjectMeta{Name: "three-tags"}, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "other": { Items: []imageapi.TagEvent{ { DockerImageReference: "other-ref", Created: kutil.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: "latest-ref", Created: kutil.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, "third": { Items: []imageapi.TagEvent{ { DockerImageReference: "third-ref", Created: kutil.Date(2015, 9, 4, 13, 54, 0, 0, time.UTC), Image: "third-image", }, }, }, }, }, }, { ObjectMeta: kapi.ObjectMeta{Name: "more-than-three-tags"}, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "other": { Items: []imageapi.TagEvent{ { DockerImageReference: "other-ref", Created: kutil.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: "latest-ref", Created: kutil.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, "third": { Items: []imageapi.TagEvent{ { DockerImageReference: "third-ref", Created: kutil.Date(2015, 9, 4, 13, 54, 0, 0, time.UTC), Image: "third-image", }, }, }, "another": { Items: []imageapi.TagEvent{ { DockerImageReference: "another-ref", Created: kutil.Date(2015, 9, 4, 13, 55, 0, 0, time.UTC), Image: "another-image", }, }, }, }, }, }, } }
func TestImageWithMetadata(t *testing.T) { tests := map[string]struct { image Image expectedImage Image expectError bool }{ "no manifest data": { image: Image{}, expectedImage: Image{}, }, "error unmarshalling manifest data": { image: Image{ DockerImageManifest: "{ no {{{ json here!!!", }, expectedImage: Image{}, expectError: true, }, "no history": { image: Image{ DockerImageManifest: `{"name": "library/ubuntu", "tag": "latest"}`, }, expectedImage: Image{}, }, "error unmarshalling v1 compat": { image: Image{ DockerImageManifest: `{"name": "library/ubuntu", "tag": "latest", "history": ["v1Compatibility": "{ not valid {{ json" }`, }, expectError: true, }, "happy path": { image: validImageWithManifestData(), expectedImage: Image{ ObjectMeta: kapi.ObjectMeta{ Name: "id", }, DockerImageManifest: "", DockerImageMetadata: DockerImage{ ID: "2d24f826cb16146e2016ff349a8a33ed5830f3b938d45c0f82943f4ab8c097e7", Parent: "117ee323aaa9d1b136ea55e4421f4ce413dfc6c0cc6b2186dea6c88d93e1ad7c", Comment: "", Created: util.Date(2015, 2, 21, 2, 11, 6, 735146646, time.UTC), Container: "c9a3eda5951d28aa8dbe5933be94c523790721e4f80886d0a8e7a710132a38ec", ContainerConfig: DockerConfig{ Hostname: "43bd710ec89a", Domainname: "", User: "", Memory: 0, MemorySwap: 0, CPUShares: 0, CPUSet: "", AttachStdin: false, AttachStdout: false, AttachStderr: false, PortSpecs: nil, ExposedPorts: nil, Tty: false, OpenStdin: false, StdinOnce: false, Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, Cmd: []string{"/bin/sh", "-c", "#(nop) CMD [/bin/bash]"}, Image: "117ee323aaa9d1b136ea55e4421f4ce413dfc6c0cc6b2186dea6c88d93e1ad7c", Volumes: nil, WorkingDir: "", Entrypoint: nil, NetworkDisabled: false, SecurityOpts: nil, OnBuild: []string{}, }, DockerVersion: "1.4.1", Author: "", Config: &DockerConfig{ Hostname: "43bd710ec89a", Domainname: "", User: "", Memory: 0, MemorySwap: 0, CPUShares: 0, CPUSet: "", AttachStdin: false, AttachStdout: false, AttachStderr: false, PortSpecs: nil, ExposedPorts: nil, Tty: false, OpenStdin: false, StdinOnce: false, Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, Cmd: []string{"/bin/bash"}, Image: "117ee323aaa9d1b136ea55e4421f4ce413dfc6c0cc6b2186dea6c88d93e1ad7c", Volumes: nil, WorkingDir: "", Entrypoint: nil, NetworkDisabled: false, OnBuild: []string{}, }, Architecture: "amd64", Size: 0, }, }, }, } for name, test := range tests { imageWithMetadata, err := ImageWithMetadata(test.image) gotError := err != nil if e, a := test.expectError, gotError; e != a { t.Fatalf("%s: expectError=%t, gotError=%t: %s", name, e, a, err) } if test.expectError { continue } if e, a := test.expectedImage, *imageWithMetadata; !kapi.Semantic.DeepEqual(e, a) { stringE := fmt.Sprintf("%#v", e) stringA := fmt.Sprintf("%#v", a) t.Errorf("%s: image: %s", name, util.StringDiff(stringE, stringA)) } } }
func TestGetImageStreamTag(t *testing.T) { tests := map[string]struct { image *api.Image repo *api.ImageStream expectError bool errorTargetKind string errorTargetID string }{ "happy path": { image: &api.Image{ObjectMeta: kapi.ObjectMeta{Name: "10"}, DockerImageReference: "foo/bar/baz"}, repo: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "default", Name: "test", }, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { Annotations: map[string]string{ "color": "blue", "size": "large", }, }, }, }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": { Items: []api.TagEvent{ { Created: util.Date(2015, 3, 24, 9, 38, 0, 0, time.UTC), DockerImageReference: "test", Image: "10", }, }, }, }, }, }, }, "image = ''": { repo: &api.ImageStream{Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": {Items: []api.TagEvent{{DockerImageReference: "test", Image: ""}}}, }, }}, expectError: true, errorTargetKind: "imageStreamTag", errorTargetID: "test:latest", }, "missing image": { repo: &api.ImageStream{Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": {Items: []api.TagEvent{{DockerImageReference: "test", Image: "10"}}}, }, }}, expectError: true, errorTargetKind: "image", errorTargetID: "10", }, "missing repo": { expectError: true, errorTargetKind: "imageStream", errorTargetID: "test", }, "missing tag": { image: &api.Image{ObjectMeta: kapi.ObjectMeta{Name: "10"}, DockerImageReference: "foo/bar/baz"}, repo: &api.ImageStream{Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "other": {Items: []api.TagEvent{{DockerImageReference: "test", Image: "10"}}}, }, }}, expectError: true, errorTargetKind: "imageStreamTag", errorTargetID: "test:latest", }, } for name, testCase := range tests { fakeEtcdClient, _, storage := setup(t) if testCase.image != nil { fakeEtcdClient.Data["/images/"+testCase.image.Name] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(latest.Codec, testCase.image), ModifiedIndex: 1, }, }, } } else { fakeEtcdClient.Data["/images/10"] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: tools.EtcdErrorNotFound, } } if testCase.repo != nil { fakeEtcdClient.Data["/imagestreams/default/test"] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(latest.Codec, testCase.repo), ModifiedIndex: 1, }, }, } } else { fakeEtcdClient.Data["/imagestreams/default/test"] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: tools.EtcdErrorNotFound, } } obj, err := storage.Get(kapi.NewDefaultContext(), "test:latest") gotErr := err != nil if e, a := testCase.expectError, gotErr; e != a { t.Fatalf("%s: Expected err=%v: got %v: %v", name, e, a, err) } if testCase.expectError { if !errors.IsNotFound(err) { t.Fatalf("%s: unexpected error type: %v", name, err) } status := err.(statusError).Status() if status.Details.Kind != testCase.errorTargetKind || status.Details.Name != testCase.errorTargetID { t.Errorf("%s: unexpected status: %#v", name, status) } } else { actual := obj.(*api.ImageStreamTag) if e, a := "default", actual.Namespace; e != a { t.Errorf("%s: namespace: expected %v, got %v", name, e, a) } if e, a := "test:latest", actual.Name; e != a { t.Errorf("%s: name: expected %v, got %v", name, e, a) } if e, a := map[string]string{"size": "large", "color": "blue"}, actual.Image.Annotations; !reflect.DeepEqual(e, a) { t.Errorf("%s: annotations: expected %v, got %v", name, e, a) } if e, a := util.Date(2015, 3, 24, 9, 38, 0, 0, time.UTC), actual.CreationTimestamp; !a.Equal(e) { t.Errorf("%s: timestamp: expected %v, got %v", name, e, a) } } } }
func TestFormatImageStreamTags(t *testing.T) { repo := imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "spec1": { From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "foo", Name: "bar:latest", }, }, "spec2": { From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "mysql", Name: "latest@sha256:e52c6534db85036dabac5e71ff14e720db94def2d90f986f3548425ea27b3719", }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ imageapi.DefaultImageTag: { Items: []imageapi.TagEvent{ { Created: util.Date(2015, 3, 24, 9, 38, 0, 0, time.UTC), DockerImageReference: "registry:5000/foo/bar@sha256:4bd26aef1ce78b4f05ede83496276f11e3343441574ca1ce89dffd146c708c16", Image: "sha256:4bd26aef1ce78b4f05ede83496276f11e3343441574ca1ce89dffd146c708c16", }, { Created: util.Date(2015, 3, 23, 7, 15, 0, 0, time.UTC), DockerImageReference: "registry:5000/foo/bar@sha256:062b80555a5dd7f5d58e78b266785a399277ff8c3e402ce5fa5d8571788e6bad", Image: "sha256:062b80555a5dd7f5d58e78b266785a399277ff8c3e402ce5fa5d8571788e6bad", }, }, }, "spec1": { Items: []imageapi.TagEvent{ { Created: util.Date(2015, 3, 24, 9, 38, 0, 0, time.UTC), DockerImageReference: "registry:5000/foo/bar@sha256:4bd26aef1ce78b4f05ede83496276f11e3343441574ca1ce89dffd146c708c16", Image: "sha256:4bd26aef1ce78b4f05ede83496276f11e3343441574ca1ce89dffd146c708c16", }, }, }, "spec2": { Items: []imageapi.TagEvent{ { Created: util.Date(2015, 3, 24, 9, 38, 0, 0, time.UTC), DockerImageReference: "mysql:latest", Image: "sha256:e52c6534db85036dabac5e71ff14e720db94def2d90f986f3548425ea27b3719", }, }, }, }, }, } out := new(tabwriter.Writer) b := make([]byte, 1024) buf := bytes.NewBuffer(b) out.Init(buf, 0, 8, 1, '\t', 0) formatImageStreamTags(out, &repo) out.Flush() actual := string(buf.String()) t.Logf("\n%s", actual) }