diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index ad59de4df1c..6f7b76783ab 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -169,6 +169,8 @@ type Pod struct { // The name and namespace of the pod, which is readable by human. Name string Namespace string + // Creation timestamps of the Pod in nanoseconds. + CreatedAt uint64 // List of containers that belongs to this pod. It may contain only // running containers, or mixed with dead ones (when GetPods(true)). Containers []*Container diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager.go b/pkg/kubelet/kuberuntime/kuberuntime_manager.go index 634c1bdcd0e..1add54c1a6b 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager.go @@ -21,6 +21,7 @@ import ( "fmt" "os" "path/filepath" + "sort" "time" cadvisorapi "github.com/google/cadvisor/info/v1" @@ -364,6 +365,7 @@ func (m *kubeGenericRuntimeManager) GetPods(all bool) ([]*kubecontainer.Pod, err continue } p.Sandboxes = append(p.Sandboxes, converted) + p.CreatedAt = uint64(s.GetCreatedAt()) } containers, err := m.getKubeletContainers(all) @@ -403,6 +405,15 @@ func (m *kubeGenericRuntimeManager) GetPods(all bool) ([]*kubecontainer.Pod, err result = append(result, pod) } + // There are scenarios where multiple pods are running in parallel having + // the same name, because one of them have not been fully terminated yet. + // To avoid unexpected behavior on container name based search (for example + // by calling *Kubelet.findContainer() without specifying a pod ID), we now + // return the list of pods ordered by their creation time. + sort.SliceStable(result, func(i, j int) bool { + return result[i].CreatedAt > result[j].CreatedAt + }) + return result, nil } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go b/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go index 8e2daef2810..5343d80dd41 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go @@ -17,6 +17,7 @@ limitations under the License. package kuberuntime import ( + "fmt" "path/filepath" "reflect" "sort" @@ -473,6 +474,7 @@ func TestGetPods(t *testing.T) { ID: types.UID("12345678"), Name: "foo", Namespace: "new", + CreatedAt: uint64(fakeSandbox.CreatedAt), Containers: []*kubecontainer.Container{containers[0], containers[1]}, Sandboxes: []*kubecontainer.Container{sandbox}, }, @@ -486,6 +488,35 @@ func TestGetPods(t *testing.T) { } } +func TestGetPodsSorted(t *testing.T) { + fakeRuntime, _, m, err := createTestRuntimeManager() + assert.NoError(t, err) + + pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}} + + createdTimestamps := []uint64{10, 5, 20} + fakeSandboxes := []*apitest.FakePodSandbox{} + for i, createdAt := range createdTimestamps { + pod.UID = types.UID(fmt.Sprint(i)) + fakeSandboxes = append(fakeSandboxes, makeFakePodSandbox(t, m, sandboxTemplate{ + pod: pod, + createdAt: int64(createdAt), + state: runtimeapi.PodSandboxState_SANDBOX_READY, + })) + } + fakeRuntime.SetFakeSandboxes(fakeSandboxes) + + actual, err := m.GetPods(false) + assert.NoError(t, err) + + assert.Len(t, actual, 3) + + // Verify that the pods are sorted by their creation time (newest/biggest timestamp first) + assert.Equal(t, uint64(createdTimestamps[2]), actual[0].CreatedAt) + assert.Equal(t, uint64(createdTimestamps[0]), actual[1].CreatedAt) + assert.Equal(t, uint64(createdTimestamps[1]), actual[2].CreatedAt) +} + func TestKillPod(t *testing.T) { fakeRuntime, _, m, err := createTestRuntimeManager() assert.NoError(t, err)