diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index 4d3843d32d7..e73fb16d846 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -840,6 +840,7 @@ func describePod(pod *corev1.Pod, events *corev1.EventList) (string, error) { } printLabelsMultiline(w, "Node-Selectors", pod.Spec.NodeSelector) printPodTolerationsMultiline(w, "Tolerations", pod.Spec.Tolerations) + describeTopologySpreadConstraints(pod.Spec.TopologySpreadConstraints, w, "") if events != nil { DescribeEvents(events, w) } @@ -865,6 +866,32 @@ func describePodIPs(pod *corev1.Pod, w PrefixWriter, space string) { } } +func describeTopologySpreadConstraints(tscs []corev1.TopologySpreadConstraint, w PrefixWriter, space string) { + if len(tscs) == 0 { + return + } + + sort.Slice(tscs, func(i, j int) bool { + return tscs[i].TopologyKey < tscs[j].TopologyKey + }) + + w.Write(LEVEL_0, "%sTopology Spread Constraints:\t", space) + for i, tsc := range tscs { + if i != 0 { + w.Write(LEVEL_0, "%s", space) + w.Write(LEVEL_0, "%s", "\t") + } + + w.Write(LEVEL_0, "%s:", tsc.TopologyKey) + w.Write(LEVEL_0, "%v", tsc.WhenUnsatisfiable) + w.Write(LEVEL_0, " when max skew %d is exceeded", tsc.MaxSkew) + if tsc.LabelSelector != nil { + w.Write(LEVEL_0, " for selector %s", metav1.FormatLabelSelector(tsc.LabelSelector)) + } + w.Write(LEVEL_0, "\n") + } +} + func describeVolumes(volumes []corev1.Volume, w PrefixWriter, space string) { if len(volumes) == 0 { w.Write(LEVEL_0, "%sVolumes:\t\n", space) @@ -2110,6 +2137,7 @@ func DescribePodTemplate(template *corev1.PodTemplateSpec, w PrefixWriter) { } describeContainers("Containers", template.Spec.Containers, nil, nil, w, " ") describeVolumes(template.Spec.Volumes, w, " ") + describeTopologySpreadConstraints(template.Spec.TopologySpreadConstraints, w, " ") if len(template.Spec.PriorityClassName) > 0 { w.Write(LEVEL_1, "Priority Class Name:\t%s\n", template.Spec.PriorityClassName) } 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 7314feb20e8..f9988f53a81 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -211,6 +211,40 @@ func TestDescribePodTolerations(t *testing.T) { } } +func TestDescribeTopologySpreadConstraints(t *testing.T) { + fake := fake.NewSimpleClientset(&corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: corev1.PodSpec{ + TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ + { + MaxSkew: 3, + TopologyKey: "topology.kubernetes.io/test1", + WhenUnsatisfiable: "DoNotSchedule", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"key1": "val1", "key2": "val2"}}, + }, + { + MaxSkew: 1, + TopologyKey: "topology.kubernetes.io/test2", + WhenUnsatisfiable: "ScheduleAnyway", + }, + }, + }, + }) + c := &describeClient{T: t, Namespace: "foo", Interface: fake} + d := PodDescriber{c} + out, err := d.Describe("foo", "bar", DescriberSettings{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !strings.Contains(out, "topology.kubernetes.io/test1:DoNotSchedule when max skew 3 is exceeded for selector key1=val1,key2=val2\n") || + !strings.Contains(out, "topology.kubernetes.io/test2:ScheduleAnyway when max skew 1 is exceeded\n") { + t.Errorf("unexpected out:\n%s", out) + } +} + func TestDescribeSecret(t *testing.T) { fake := fake.NewSimpleClientset(&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{