diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index 33576e194ec..e15665aaa4d 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -1645,7 +1645,7 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri pc := d.CoreV1().Pods(namespace) - pods, err := getPodsForPVC(pc, pvc.Name, describerSettings) + pods, err := getPodsForPVC(pc, pvc, describerSettings) if err != nil { return "", err } @@ -1655,7 +1655,7 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri return describePersistentVolumeClaim(pvc, events, pods) } -func getPodsForPVC(c corev1client.PodInterface, pvcName string, settings DescriberSettings) ([]corev1.Pod, error) { +func getPodsForPVC(c corev1client.PodInterface, pvc *corev1.PersistentVolumeClaim, settings DescriberSettings) ([]corev1.Pod, error) { nsPods, err := getPodsInChunks(c, metav1.ListOptions{Limit: settings.ChunkSize}) if err != nil { return []corev1.Pod{}, err @@ -1665,12 +1665,40 @@ func getPodsForPVC(c corev1client.PodInterface, pvcName string, settings Describ for _, pod := range nsPods.Items { for _, volume := range pod.Spec.Volumes { - if volume.VolumeSource.PersistentVolumeClaim != nil && volume.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { + if volume.VolumeSource.PersistentVolumeClaim != nil && volume.VolumeSource.PersistentVolumeClaim.ClaimName == pvc.Name { pods = append(pods, pod) } } } +ownersLoop: + for _, ownerRef := range pvc.ObjectMeta.OwnerReferences { + if ownerRef.Kind != "Pod" { + continue + } + + podIndex := -1 + for i, pod := range nsPods.Items { + if pod.UID == ownerRef.UID { + podIndex = i + break + } + } + if podIndex == -1 { + // Maybe the pod has been deleted + continue + } + + for _, pod := range pods { + if pod.UID == nsPods.Items[podIndex].UID { + // This owner pod is already recorded, look for pods between other owners + continue ownersLoop + } + } + + pods = append(pods, nsPods.Items[podIndex]) + } + return pods, nil } diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go index 2db89026ace..7b493d951ed 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -1994,6 +1994,123 @@ func TestPersistentVolumeClaimDescriber(t *testing.T) { } } +func TestGetPodsForPVC(t *testing.T) { + goldClassName := "gold" + testCases := []struct { + name string + pvc *corev1.PersistentVolumeClaim + requiredObjects []runtime.Object + expectedPods []string + }{ + { + name: "pvc-unused", + pvc: &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pvc-name"}, + Spec: corev1.PersistentVolumeClaimSpec{ + VolumeName: "volume1", + StorageClassName: &goldClassName, + }, + Status: corev1.PersistentVolumeClaimStatus{ + Phase: corev1.ClaimBound, + }, + }, + expectedPods: []string{}, + }, + { + name: "pvc-in-pods-volumes-list", + pvc: &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pvc-name"}, + Spec: corev1.PersistentVolumeClaimSpec{ + VolumeName: "volume1", + StorageClassName: &goldClassName, + }, + Status: corev1.PersistentVolumeClaimStatus{ + Phase: corev1.ClaimBound, + }, + }, + requiredObjects: []runtime.Object{ + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pod-name"}, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "volume", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "pvc-name", + }, + }, + }, + }, + }, + }, + }, + expectedPods: []string{"pod-name"}, + }, + { + name: "pvc-owned-by-pod", + pvc: &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "pvc-name", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Pod", + Name: "pod-name", + UID: "pod-uid", + }, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + VolumeName: "volume1", + StorageClassName: &goldClassName, + }, + Status: corev1.PersistentVolumeClaimStatus{ + Phase: corev1.ClaimBound, + }, + }, + requiredObjects: []runtime.Object{ + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pod-name", UID: "pod-uid"}, + }, + }, + expectedPods: []string{"pod-name"}, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + var objects []runtime.Object + objects = append(objects, test.requiredObjects...) + objects = append(objects, test.pvc) + fake := fake.NewSimpleClientset(objects...) + + pods, err := getPodsForPVC(fake.CoreV1().Pods(test.pvc.ObjectMeta.Namespace), test.pvc, DescriberSettings{}) + if err != nil { + t.Errorf("Unexpected error for test %s: %v", test.name, err) + } + + for _, expectedPod := range test.expectedPods { + foundPod := false + for _, pod := range pods { + if pod.Name == expectedPod { + foundPod = true + break + } + } + + if !foundPod { + t.Errorf("Expected pod %s, but it was not returned: %v", expectedPod, pods) + } + } + + if len(test.expectedPods) != len(pods) { + t.Errorf("Expected %d pods, but got %d pods", len(test.expectedPods), len(pods)) + } + }) + } +} + func TestDescribeDeployment(t *testing.T) { labels := map[string]string{"k8s-app": "bar"} testCases := []struct {