diff --git a/pkg/kubectl/describe/versioned/BUILD b/pkg/kubectl/describe/versioned/BUILD index 950c7120184..8b9a517754f 100644 --- a/pkg/kubectl/describe/versioned/BUILD +++ b/pkg/kubectl/describe/versioned/BUILD @@ -18,6 +18,7 @@ go_library( "//pkg/kubectl/util/slice:go_default_library", "//pkg/kubectl/util/storage:go_default_library", "//staging/src/k8s.io/api/apps/v1:go_default_library", + "//staging/src/k8s.io/api/autoscaling/v1:go_default_library", "//staging/src/k8s.io/api/autoscaling/v2beta2:go_default_library", "//staging/src/k8s.io/api/batch/v1:go_default_library", "//staging/src/k8s.io/api/batch/v1beta1:go_default_library", @@ -73,6 +74,7 @@ go_test( deps = [ "//pkg/kubectl/describe:go_default_library", "//staging/src/k8s.io/api/apps/v1:go_default_library", + "//staging/src/k8s.io/api/autoscaling/v1:go_default_library", "//staging/src/k8s.io/api/autoscaling/v2beta2:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/networking/v1:go_default_library", diff --git a/pkg/kubectl/describe/versioned/describe.go b/pkg/kubectl/describe/versioned/describe.go index 5579521047b..062aebb021f 100644 --- a/pkg/kubectl/describe/versioned/describe.go +++ b/pkg/kubectl/describe/versioned/describe.go @@ -33,6 +33,7 @@ import ( "github.com/fatih/camelcase" appsv1 "k8s.io/api/apps/v1" + autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" batchv1 "k8s.io/api/batch/v1" batchv1beta1 "k8s.io/api/batch/v1beta1" @@ -3074,20 +3075,31 @@ type HorizontalPodAutoscalerDescriber struct { } func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings describe.DescriberSettings) (string, error) { - hpa, err := d.client.AutoscalingV2beta2().HorizontalPodAutoscalers(namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return "", err - } - var events *corev1.EventList - if describerSettings.ShowEvents { - events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, hpa) + + // autoscaling/v2beta2 is introduced since v1.12 and autoscaling/v1 does not have full backward compatibility + // with autoscaling/v2beta2, so describer will try to get and describe hpa v2beta2 object firstly, if it fails, + // describer will fall back to do with hpa v1 object + hpaV2beta2, err := d.client.AutoscalingV2beta2().HorizontalPodAutoscalers(namespace).Get(name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, hpaV2beta2) + } + return describeHorizontalPodAutoscalerV2beta2(hpaV2beta2, events, d) } - return describeHorizontalPodAutoscaler(hpa, events, d) + hpaV1, err := d.client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, hpaV1) + } + return describeHorizontalPodAutoscalerV1(hpaV1, events, d) + } + + return "", err } -func describeHorizontalPodAutoscaler(hpa *autoscalingv2beta2.HorizontalPodAutoscaler, events *corev1.EventList, d *HorizontalPodAutoscalerDescriber) (string, error) { +func describeHorizontalPodAutoscalerV2beta2(hpa *autoscalingv2beta2.HorizontalPodAutoscaler, events *corev1.EventList, d *HorizontalPodAutoscalerDescriber) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) w.Write(LEVEL_0, "Name:\t%s\n", hpa.Name) @@ -3188,6 +3200,44 @@ func describeHorizontalPodAutoscaler(hpa *autoscalingv2beta2.HorizontalPodAutosc }) } +func describeHorizontalPodAutoscalerV1(hpa *autoscalingv1.HorizontalPodAutoscaler, events *corev1.EventList, d *HorizontalPodAutoscalerDescriber) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%s\n", hpa.Name) + w.Write(LEVEL_0, "Namespace:\t%s\n", hpa.Namespace) + printLabelsMultiline(w, "Labels", hpa.Labels) + printAnnotationsMultiline(w, "Annotations", hpa.Annotations) + w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", hpa.CreationTimestamp.Time.Format(time.RFC1123Z)) + w.Write(LEVEL_0, "Reference:\t%s/%s\n", + hpa.Spec.ScaleTargetRef.Kind, + hpa.Spec.ScaleTargetRef.Name) + + if hpa.Spec.TargetCPUUtilizationPercentage != nil { + w.Write(LEVEL_0, "Target CPU utilization:\t%d%%\n", *hpa.Spec.TargetCPUUtilizationPercentage) + current := "" + if hpa.Status.CurrentCPUUtilizationPercentage != nil { + current = fmt.Sprintf("%d", *hpa.Status.CurrentCPUUtilizationPercentage) + } + w.Write(LEVEL_0, "Current CPU utilization:\t%s%%\n", current) + } + + minReplicas := "" + if hpa.Spec.MinReplicas != nil { + minReplicas = fmt.Sprintf("%d", *hpa.Spec.MinReplicas) + } + w.Write(LEVEL_0, "Min replicas:\t%s\n", minReplicas) + w.Write(LEVEL_0, "Max replicas:\t%d\n", hpa.Spec.MaxReplicas) + w.Write(LEVEL_0, "%s pods:\t", hpa.Spec.ScaleTargetRef.Kind) + w.Write(LEVEL_0, "%d current / %d desired\n", hpa.Status.CurrentReplicas, hpa.Status.DesiredReplicas) + + if events != nil { + DescribeEvents(events, w) + } + + return nil + }) +} + func describeNodeResource(nodeNonTerminatedPodsList *corev1.PodList, node *corev1.Node, w PrefixWriter) { w.Write(LEVEL_0, "Non-terminated Pods:\t(%d in total)\n", len(nodeNonTerminatedPodsList.Items)) w.Write(LEVEL_1, "Namespace\tName\t\tCPU Requests\tCPU Limits\tMemory Requests\tMemory Limits\tAGE\n") diff --git a/pkg/kubectl/describe/versioned/describe_test.go b/pkg/kubectl/describe/versioned/describe_test.go index f58c10373df..882255cf1cf 100644 --- a/pkg/kubectl/describe/versioned/describe_test.go +++ b/pkg/kubectl/describe/versioned/describe_test.go @@ -26,6 +26,7 @@ import ( "time" appsv1 "k8s.io/api/apps/v1" + autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" @@ -1628,7 +1629,7 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) { if err != nil { t.Errorf("unable to parse label selector: %v", err) } - tests := []struct { + testsV2beta2 := []struct { name string hpa autoscalingv2beta2.HorizontalPodAutoscaler }{ @@ -2270,7 +2271,102 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) { }, } - for _, test := range tests { + for _, test := range testsV2beta2 { + t.Run(test.name, func(t *testing.T) { + test.hpa.ObjectMeta = metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + } + fake := fake.NewSimpleClientset(&test.hpa) + desc := HorizontalPodAutoscalerDescriber{fake} + str, err := desc.Describe("foo", "bar", describe.DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("Unexpected error for test %s: %v", test.name, err) + } + if str == "" { + t.Errorf("Unexpected empty string for test %s. Expected HPA Describer output", test.name) + } + t.Logf("Description for %q:\n%s", test.name, str) + }) + } + + testsV1 := []struct { + name string + hpa autoscalingv1.HorizontalPodAutoscaler + }{ + { + "minReplicas unset", + autoscalingv1.HorizontalPodAutoscaler{ + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Name: "some-rc", + Kind: "ReplicationController", + }, + MaxReplicas: 10, + }, + Status: autoscalingv1.HorizontalPodAutoscalerStatus{ + CurrentReplicas: 4, + DesiredReplicas: 5, + }, + }, + }, + { + "minReplicas set", + autoscalingv1.HorizontalPodAutoscaler{ + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Name: "some-rc", + Kind: "ReplicationController", + }, + MinReplicas: &minReplicasVal, + MaxReplicas: 10, + }, + Status: autoscalingv1.HorizontalPodAutoscalerStatus{ + CurrentReplicas: 4, + DesiredReplicas: 5, + }, + }, + }, + { + "with target no current", + autoscalingv1.HorizontalPodAutoscaler{ + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Name: "some-rc", + Kind: "ReplicationController", + }, + MinReplicas: &minReplicasVal, + MaxReplicas: 10, + TargetCPUUtilizationPercentage: &targetUtilizationVal, + }, + Status: autoscalingv1.HorizontalPodAutoscalerStatus{ + CurrentReplicas: 4, + DesiredReplicas: 5, + }, + }, + }, + { + "with target and current", + autoscalingv1.HorizontalPodAutoscaler{ + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Name: "some-rc", + Kind: "ReplicationController", + }, + MinReplicas: &minReplicasVal, + MaxReplicas: 10, + TargetCPUUtilizationPercentage: &targetUtilizationVal, + }, + Status: autoscalingv1.HorizontalPodAutoscalerStatus{ + CurrentReplicas: 4, + DesiredReplicas: 5, + CurrentCPUUtilizationPercentage: ¤tUtilizationVal, + }, + }, + }, + } + + for _, test := range testsV1 { t.Run(test.name, func(t *testing.T) { test.hpa.ObjectMeta = metav1.ObjectMeta{ Name: "bar",