func TestReconcilePodStatus(t *testing.T) { client := testclient.NewSimpleFake(testPod) syncer := newTestManager(client) syncer.SetPodStatus(testPod, getRandomPodStatus()) // Call syncBatch directly to test reconcile syncer.syncBatch() // The apiStatusVersions should be set now originalStatus := testPod.Status podStatus, ok := syncer.GetPodStatus(testPod.UID) if !ok { t.Fatal("Should find pod status for pod: %+v", testPod) } testPod.Status = podStatus // If the pod status is the same, a reconciliation is not needed, // syncBatch should do nothing syncer.podManager.UpdatePod(testPod) if syncer.needsReconcile(testPod.UID, podStatus) { t.Errorf("Pod status is the same, a reconciliation is not needed") } client.ClearActions() syncer.syncBatch() verifyActions(t, client, []testclient.Action{}) // If the pod status is the same, only the timestamp is in Rfc3339 format (lower precision without nanosecond), // a reconciliation is not needed, syncBatch should do nothing. // The StartTime should have been set in SetPodStatus(). // TODO(random-liu): Remove this later when api becomes consistent for timestamp. normalizedStartTime := testPod.Status.StartTime.Rfc3339Copy() testPod.Status.StartTime = &normalizedStartTime syncer.podManager.UpdatePod(testPod) if syncer.needsReconcile(testPod.UID, podStatus) { t.Errorf("Pod status only differs for timestamp format, a reconciliation is not needed") } client.ClearActions() syncer.syncBatch() verifyActions(t, client, []testclient.Action{}) // If the pod status is different, a reconciliation is needed, syncBatch should trigger an update testPod.Status = getRandomPodStatus() syncer.podManager.UpdatePod(testPod) if !syncer.needsReconcile(testPod.UID, podStatus) { t.Errorf("Pod status is different, a reconciliation is needed") } client.ClearActions() syncer.syncBatch() verifyActions(t, client, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}}, }) // Just in case that testPod is shared among different test functions, set it back. testPod.Status = originalStatus }
func TestStaticPodStatus(t *testing.T) { staticPod := *testPod staticPod.Annotations = map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"} mirrorPod := *testPod mirrorPod.UID = "mirror-12345678" mirrorPod.Annotations = map[string]string{ kubetypes.ConfigSourceAnnotationKey: "api", kubetypes.ConfigMirrorAnnotationKey: "mirror", } client := testclient.NewSimpleFake(&mirrorPod) m := newTestManager(client) m.podManager.AddPod(&staticPod) m.podManager.AddPod(&mirrorPod) // Verify setup. assert.True(t, kubepod.IsStaticPod(&staticPod), "SetUp error: staticPod") assert.True(t, kubepod.IsMirrorPod(&mirrorPod), "SetUp error: mirrorPod") assert.Equal(t, m.podManager.TranslatePodUID(mirrorPod.UID), staticPod.UID) status := getRandomPodStatus() now := unversioned.Now() status.StartTime = &now m.SetPodStatus(&staticPod, status) retrievedStatus := expectPodStatus(t, m, &staticPod) assert.True(t, isStatusEqual(&status, &retrievedStatus), "Expected: %+v, Got: %+v", status, retrievedStatus) retrievedStatus, _ = m.GetPodStatus(mirrorPod.UID) assert.True(t, isStatusEqual(&status, &retrievedStatus), "Expected: %+v, Got: %+v", status, retrievedStatus) // Should translate mirrorPod / staticPod UID. m.testSyncBatch() verifyActions(t, m.kubeClient, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}}, }) updateAction := client.Actions()[1].(testclient.UpdateActionImpl) updatedPod := updateAction.Object.(*api.Pod) assert.Equal(t, mirrorPod.UID, updatedPod.UID, "Expected mirrorPod (%q), but got %q", mirrorPod.UID, updatedPod.UID) assert.True(t, isStatusEqual(&status, &updatedPod.Status), "Expected: %+v, Got: %+v", status, updatedPod.Status) client.ClearActions() // No changes. m.testSyncBatch() verifyActions(t, m.kubeClient, []testclient.Action{}) // Mirror pod identity changes. m.podManager.DeletePod(&mirrorPod) mirrorPod.UID = "new-mirror-pod" mirrorPod.Status = api.PodStatus{} m.podManager.AddPod(&mirrorPod) // Expect update to new mirrorPod. m.testSyncBatch() verifyActions(t, m.kubeClient, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}}, }) updateAction = client.Actions()[1].(testclient.UpdateActionImpl) updatedPod = updateAction.Object.(*api.Pod) assert.Equal(t, mirrorPod.UID, updatedPod.UID, "Expected mirrorPod (%q), but got %q", mirrorPod.UID, updatedPod.UID) assert.True(t, isStatusEqual(&status, &updatedPod.Status), "Expected: %+v, Got: %+v", status, updatedPod.Status) }
func TestStaticPodStatus(t *testing.T) { staticPod := *testPod staticPod.Annotations = map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"} mirrorPod := *testPod mirrorPod.UID = "mirror-12345678" mirrorPod.Annotations = map[string]string{ kubetypes.ConfigSourceAnnotationKey: "api", kubetypes.ConfigMirrorAnnotationKey: "mirror", } client := testclient.NewSimpleFake(&mirrorPod) m := newTestManager(client) m.podManager.AddPod(&staticPod) m.podManager.AddPod(&mirrorPod) // Verify setup. assert.True(t, kubepod.IsStaticPod(&staticPod), "SetUp error: staticPod") assert.True(t, kubepod.IsMirrorPod(&mirrorPod), "SetUp error: mirrorPod") assert.Equal(t, m.podManager.TranslatePodUID(mirrorPod.UID), staticPod.UID) status := getRandomPodStatus() now := unversioned.Now() status.StartTime = &now m.SetPodStatus(&staticPod, status) retrievedStatus, _ := m.GetPodStatus(staticPod.UID) assert.True(t, isStatusEqual(&status, &retrievedStatus), "Expected: %+v, Got: %+v", status, retrievedStatus) retrievedStatus, _ = m.GetPodStatus(mirrorPod.UID) assert.True(t, isStatusEqual(&status, &retrievedStatus), "Expected: %+v, Got: %+v", status, retrievedStatus) // Should translate mirrorPod / staticPod UID. m.syncBatch() verifyActions(t, m.kubeClient, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}}, }) updateAction := client.Actions()[1].(testclient.UpdateActionImpl) updatedPod := updateAction.Object.(*api.Pod) assert.Equal(t, mirrorPod.UID, updatedPod.UID, "Expected mirrorPod (%q), but got %q", mirrorPod.UID, updatedPod.UID) assert.True(t, isStatusEqual(&status, &updatedPod.Status), "Expected: %+v, Got: %+v", status, updatedPod.Status) client.ClearActions() otherPod := &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "other-87654321", Name: "other", Namespace: "new", }, } m.podManager.AddPod(otherPod) m.SetPodStatus(otherPod, getRandomPodStatus()) m.syncBatch() verifyActions(t, m.kubeClient, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, }) _, found := m.GetPodStatus(otherPod.UID) assert.False(t, found, "otherPod status should have been deleted") }
func TestStaleUpdates(t *testing.T) { pod := *testPod client := testclient.NewSimpleFake(&pod) m := newTestManager(client) status := api.PodStatus{Message: "initial status"} m.SetPodStatus(&pod, status) status.Message = "first version bump" m.SetPodStatus(&pod, status) status.Message = "second version bump" m.SetPodStatus(&pod, status) verifyUpdates(t, m, 3) t.Logf("First sync pushes latest status.") m.testSyncBatch() verifyActions(t, m.kubeClient, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}}, }) client.ClearActions() for i := 0; i < 2; i++ { t.Logf("Next 2 syncs should be ignored (%d).", i) m.testSyncBatch() verifyActions(t, m.kubeClient, []testclient.Action{}) } t.Log("Unchanged status should not send an update.") m.SetPodStatus(&pod, status) verifyUpdates(t, m, 0) t.Log("... unless it's stale.") m.apiStatusVersions[pod.UID] = m.apiStatusVersions[pod.UID] - 1 m.SetPodStatus(&pod, status) m.testSyncBatch() verifyActions(t, m.kubeClient, []testclient.Action{ testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}}, testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}}, }) // Nothing stuck in the pipe. verifyUpdates(t, m, 0) }
func TestSyncBatchNoDeadlock(t *testing.T) { client := &testclient.Fake{} m := newTestManager(client) // Setup fake client. var ret api.Pod var err error client.AddReactor("*", "pods", func(action testclient.Action) (bool, runtime.Object, error) { switch action := action.(type) { case testclient.GetAction: assert.Equal(t, testPod.Name, action.GetName(), "Unexpeted GetAction: %+v", action) case testclient.UpdateAction: assert.Equal(t, testPod.Name, action.GetObject().(*api.Pod).Name, "Unexpeted UpdateAction: %+v", action) default: assert.Fail(t, "Unexpected Action: %+v", action) } return true, &ret, err }) pod := new(api.Pod) *pod = *testPod pod.Status.ContainerStatuses = []api.ContainerStatus{{State: api.ContainerState{Running: &api.ContainerStateRunning{}}}} getAction := testclient.GetActionImpl{ActionImpl: testclient.ActionImpl{Verb: "get", Resource: "pods"}} updateAction := testclient.UpdateActionImpl{ActionImpl: testclient.ActionImpl{Verb: "update", Resource: "pods", Subresource: "status"}} // Pod not found. ret = *pod err = errors.NewNotFound(api.Resource("pods"), pod.Name) m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []testclient.Action{getAction}) client.ClearActions() // Pod was recreated. ret.UID = "other_pod" err = nil m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []testclient.Action{getAction}) client.ClearActions() // Pod not deleted (success case). ret = *pod m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []testclient.Action{getAction, updateAction}) client.ClearActions() // Pod is terminated, but still running. pod.DeletionTimestamp = new(unversioned.Time) m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []testclient.Action{getAction, updateAction}) client.ClearActions() // Pod is terminated successfully. pod.Status.ContainerStatuses[0].State.Running = nil pod.Status.ContainerStatuses[0].State.Terminated = &api.ContainerStateTerminated{} m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []testclient.Action{getAction, updateAction}) client.ClearActions() // Error case. err = fmt.Errorf("intentional test error") m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []testclient.Action{getAction}) client.ClearActions() }