From 62482f437cd95ce7fbbbb6be5f628800d2ca3382 Mon Sep 17 00:00:00 2001 From: chaunceyjiang Date: Sun, 28 May 2023 12:13:16 +0800 Subject: [PATCH] feat: introducing a karmada top command Signed-off-by: chaunceyjiang --- pkg/karmadactl/karmadactl.go | 2 + pkg/karmadactl/top/metrics_printer.go | 207 +++++++++++++ pkg/karmadactl/top/metrics_sorter.go | 73 +++++ pkg/karmadactl/top/top.go | 58 ++++ pkg/karmadactl/top/top_pods.go | 274 ++++++++++++++++++ .../pkg/metricsutil/metrics_printer.go | 236 +++++++++++++++ .../pkg/metricsutil/metrics_resource_adder.go | 45 +++ .../kubectl/pkg/metricsutil/metrics_sorter.go | 130 +++++++++ .../client/clientset/versioned/clientset.go | 133 +++++++++ .../versioned/typed/metrics/v1alpha1/doc.go | 20 ++ .../metrics/v1alpha1/generated_expansion.go | 23 ++ .../typed/metrics/v1alpha1/metrics_client.go | 112 +++++++ .../typed/metrics/v1alpha1/nodemetrics.go | 98 +++++++ .../typed/metrics/v1alpha1/podmetrics.go | 103 +++++++ vendor/modules.txt | 3 + 15 files changed, 1517 insertions(+) create mode 100644 pkg/karmadactl/top/metrics_printer.go create mode 100644 pkg/karmadactl/top/metrics_sorter.go create mode 100644 pkg/karmadactl/top/top.go create mode 100644 pkg/karmadactl/top/top_pods.go create mode 100644 vendor/k8s.io/kubectl/pkg/metricsutil/metrics_printer.go create mode 100644 vendor/k8s.io/kubectl/pkg/metricsutil/metrics_resource_adder.go create mode 100644 vendor/k8s.io/kubectl/pkg/metricsutil/metrics_sorter.go create mode 100644 vendor/k8s.io/metrics/pkg/client/clientset/versioned/clientset.go create mode 100644 vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/doc.go create mode 100644 vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/generated_expansion.go create mode 100644 vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/metrics_client.go create mode 100644 vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/nodemetrics.go create mode 100644 vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/podmetrics.go diff --git a/pkg/karmadactl/karmadactl.go b/pkg/karmadactl/karmadactl.go index cf246a3b2d91..7373ceb11370 100644 --- a/pkg/karmadactl/karmadactl.go +++ b/pkg/karmadactl/karmadactl.go @@ -28,6 +28,7 @@ import ( "github.com/karmada-io/karmada/pkg/karmadactl/register" "github.com/karmada-io/karmada/pkg/karmadactl/taint" "github.com/karmada-io/karmada/pkg/karmadactl/token" + "github.com/karmada-io/karmada/pkg/karmadactl/top" "github.com/karmada-io/karmada/pkg/karmadactl/unjoin" "github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/version/sharedcommand" @@ -106,6 +107,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command { Commands: []*cobra.Command{ apply.NewCmdApply(f, parentCommand, ioStreams), promote.NewCmdPromote(f, parentCommand), + top.NewCmdTop(f, parentCommand, ioStreams), }, }, } diff --git a/pkg/karmadactl/top/metrics_printer.go b/pkg/karmadactl/top/metrics_printer.go new file mode 100644 index 000000000000..cd088b0e0882 --- /dev/null +++ b/pkg/karmadactl/top/metrics_printer.go @@ -0,0 +1,207 @@ +package top + +import ( + "fmt" + "io" + "sort" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/cli-runtime/pkg/printers" + "k8s.io/kubectl/pkg/metricsutil" + metricsapi "k8s.io/metrics/pkg/apis/metrics" + + autoscalingv1alpha1 "github.com/karmada-io/karmada/pkg/apis/autoscaling/v1alpha1" +) + +var ( + MeasuredResources = []corev1.ResourceName{ + corev1.ResourceCPU, + corev1.ResourceMemory, + } + PodColumns = []string{"NAME", "CLUSTER", "CPU(cores)", "MEMORY(bytes)"} + NamespaceColumn = "NAMESPACE" + PodColumn = "POD" +) + +type ResourceMetricsInfo struct { + Cluster string + Name string + Metrics corev1.ResourceList + Available corev1.ResourceList +} + +type TopCmdPrinter struct { + out io.Writer +} + +func NewTopCmdPrinter(out io.Writer) *TopCmdPrinter { + return &TopCmdPrinter{out: out} +} + +func (printer *TopCmdPrinter) PrintPodMetrics(metrics []metricsapi.PodMetrics, printContainers, withNamespace, noHeaders bool, sortBy string, sum bool) error { + if len(metrics) == 0 { + return nil + } + w := printers.GetNewTabWriter(printer.out) + defer w.Flush() + + columnWidth := len(PodColumns) + if !noHeaders { + if withNamespace { + printValue(w, NamespaceColumn) + columnWidth++ + } + if printContainers { + printValue(w, PodColumn) + columnWidth++ + } + printColumnNames(w, PodColumns) + } + + sort.Sort(NewPodMetricsSorter(metrics, withNamespace, sortBy)) + + for i := range metrics { + if printContainers { + sort.Sort(metricsutil.NewContainerMetricsSorter(metrics[i].Containers, sortBy)) + printSinglePodContainerMetrics(w, &metrics[i], withNamespace) + } else { + printSinglePodMetrics(w, &metrics[i], withNamespace) + } + } + + if sum { + adder := NewResourceAdder(MeasuredResources) + for i := range metrics { + adder.AddPodMetrics(&metrics[i]) + } + printPodResourcesSum(w, adder.total, columnWidth) + } + + return nil +} + +func printColumnNames(out io.Writer, names []string) { + for _, name := range names { + printValue(out, name) + } + fmt.Fprint(out, "\n") +} + +func printSinglePodMetrics(out io.Writer, m *metricsapi.PodMetrics, withNamespace bool) { + podMetrics := getPodMetrics(m) + if withNamespace { + printValue(out, m.Namespace) + } + printMetricsLine(out, &ResourceMetricsInfo{ + Name: m.Name, + Cluster: m.Annotations[autoscalingv1alpha1.QuerySourceAnnotationKey], + Metrics: podMetrics, + Available: corev1.ResourceList{}, + }) +} + +func printSinglePodContainerMetrics(out io.Writer, m *metricsapi.PodMetrics, withNamespace bool) { + for _, c := range m.Containers { + if withNamespace { + printValue(out, m.Namespace) + } + printValue(out, m.Name) + printMetricsLine(out, &ResourceMetricsInfo{ + Name: c.Name, + Cluster: m.Annotations[autoscalingv1alpha1.QuerySourceAnnotationKey], + Metrics: c.Usage, + Available: corev1.ResourceList{}, + }) + } +} + +func getPodMetrics(m *metricsapi.PodMetrics) corev1.ResourceList { + podMetrics := make(corev1.ResourceList) + for _, res := range MeasuredResources { + podMetrics[res], _ = resource.ParseQuantity("0") + } + + for _, c := range m.Containers { + for _, res := range MeasuredResources { + quantity := podMetrics[res] + quantity.Add(c.Usage[res]) + podMetrics[res] = quantity + } + } + return podMetrics +} + +func printMetricsLine(out io.Writer, metrics *ResourceMetricsInfo) { + printValue(out, metrics.Name) + printValue(out, metrics.Cluster) + printAllResourceUsages(out, metrics) + fmt.Fprint(out, "\n") +} + +func printValue(out io.Writer, value interface{}) { + fmt.Fprintf(out, "%v\t", value) +} + +func printAllResourceUsages(out io.Writer, metrics *ResourceMetricsInfo) { + for _, res := range MeasuredResources { + quantity := metrics.Metrics[res] + printSingleResourceUsage(out, res, quantity) + fmt.Fprint(out, "\t") + if available, found := metrics.Available[res]; found { + fraction := float64(quantity.MilliValue()) / float64(available.MilliValue()) * 100 + fmt.Fprintf(out, "%d%%\t", int64(fraction)) + } + } +} + +func printSingleResourceUsage(out io.Writer, resourceType corev1.ResourceName, quantity resource.Quantity) { + switch resourceType { + case corev1.ResourceCPU: + fmt.Fprintf(out, "%vm", quantity.MilliValue()) + case corev1.ResourceMemory: + fmt.Fprintf(out, "%vMi", quantity.Value()/(1024*1024)) + default: + fmt.Fprintf(out, "%v", quantity.Value()) + } +} + +func printPodResourcesSum(out io.Writer, total corev1.ResourceList, columnWidth int) { + for i := 0; i < columnWidth-2; i++ { + printValue(out, "") + } + printValue(out, "________") + printValue(out, "________") + fmt.Fprintf(out, "\n") + for i := 0; i < columnWidth-4; i++ { + printValue(out, "") + } + printMetricsLine(out, &ResourceMetricsInfo{ + Name: "", + Metrics: total, + Available: corev1.ResourceList{}, + }) +} + +type ResourceAdder struct { + resources []corev1.ResourceName + total corev1.ResourceList +} + +func NewResourceAdder(resources []corev1.ResourceName) *ResourceAdder { + return &ResourceAdder{ + resources: resources, + total: make(corev1.ResourceList), + } +} + +// AddPodMetrics adds each pod metric to the total +func (adder *ResourceAdder) AddPodMetrics(m *metricsapi.PodMetrics) { + for _, c := range m.Containers { + for _, res := range adder.resources { + total := adder.total[res] + total.Add(c.Usage[res]) + adder.total[res] = total + } + } +} diff --git a/pkg/karmadactl/top/metrics_sorter.go b/pkg/karmadactl/top/metrics_sorter.go new file mode 100644 index 000000000000..4437da3632dc --- /dev/null +++ b/pkg/karmadactl/top/metrics_sorter.go @@ -0,0 +1,73 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package top + +import ( + corev1 "k8s.io/api/core/v1" + metricsapi "k8s.io/metrics/pkg/apis/metrics" + + autoscalingv1alpha1 "github.com/karmada-io/karmada/pkg/apis/autoscaling/v1alpha1" +) + +type PodMetricsSorter struct { + metrics []metricsapi.PodMetrics + sortBy string + withNamespace bool + podMetrics []corev1.ResourceList +} + +func (p *PodMetricsSorter) Len() int { + return len(p.metrics) +} + +func (p *PodMetricsSorter) Swap(i, j int) { + p.metrics[i], p.metrics[j] = p.metrics[j], p.metrics[i] + p.podMetrics[i], p.podMetrics[j] = p.podMetrics[j], p.podMetrics[i] +} + +func (p *PodMetricsSorter) Less(i, j int) bool { + switch p.sortBy { + case "cpu": + return p.podMetrics[i].Cpu().MilliValue() > p.podMetrics[j].Cpu().MilliValue() + case "memory": + return p.podMetrics[i].Memory().Value() > p.podMetrics[j].Memory().Value() + default: + if p.metrics[i].Annotations[autoscalingv1alpha1.QuerySourceAnnotationKey] != p.metrics[j].Annotations[autoscalingv1alpha1.QuerySourceAnnotationKey] { + return p.metrics[i].Annotations[autoscalingv1alpha1.QuerySourceAnnotationKey] < p.metrics[j].Annotations[autoscalingv1alpha1.QuerySourceAnnotationKey] + } + if p.withNamespace && p.metrics[i].Namespace != p.metrics[j].Namespace { + return p.metrics[i].Namespace < p.metrics[j].Namespace + } + return p.metrics[i].Name < p.metrics[j].Name + } +} + +func NewPodMetricsSorter(metrics []metricsapi.PodMetrics, withNamespace bool, sortBy string) *PodMetricsSorter { + var podMetrics = make([]corev1.ResourceList, len(metrics)) + if len(sortBy) > 0 { + for i := range metrics { + podMetrics[i] = getPodMetrics(&metrics[i]) + } + } + + return &PodMetricsSorter{ + metrics: metrics, + sortBy: sortBy, + withNamespace: withNamespace, + podMetrics: podMetrics, + } +} diff --git a/pkg/karmadactl/top/top.go b/pkg/karmadactl/top/top.go new file mode 100644 index 000000000000..2d5294627f8c --- /dev/null +++ b/pkg/karmadactl/top/top.go @@ -0,0 +1,58 @@ +package top + +import ( + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/cli-runtime/pkg/genericclioptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + metricsapi "k8s.io/metrics/pkg/apis/metrics" +) + +const ( + sortByCPU = "cpu" + sortByMemory = "memory" +) + +var ( + supportedMetricsAPIVersions = []string{ + "v1beta1", + } + topLong = templates.LongDesc(` + Display Resource (CPU/Memory) usage of member clusters. + + The top command allows you to see the resource consumption for pods of member clusters. + + This command requires karmada-metrics-adapter to be correctly configured and working on the Karmada control plane and + Metrics Server to be correctly configured and working on the member clusters.`) +) + +func NewCmdTop(f cmdutil.Factory, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "top", + Short: "Display resource (CPU/memory) usage of member clusters", + Long: topLong, + Run: cmdutil.DefaultSubCommandRun(streams.ErrOut), + } + + // create subcommands + cmd.AddCommand(NewCmdTopPod(f, parentCommand, nil, streams)) + + return cmd +} + +func SupportedMetricsAPIVersionAvailable(discoveredAPIGroups *metav1.APIGroupList) bool { + for _, discoveredAPIGroup := range discoveredAPIGroups.Groups { + if discoveredAPIGroup.Name != metricsapi.GroupName { + continue + } + for _, version := range discoveredAPIGroup.Versions { + for _, supportedVersion := range supportedMetricsAPIVersions { + if version.Version == supportedVersion { + return true + } + } + } + } + return false +} diff --git a/pkg/karmadactl/top/top_pods.go b/pkg/karmadactl/top/top_pods.go new file mode 100644 index 000000000000..b0c42e5a091b --- /dev/null +++ b/pkg/karmadactl/top/top_pods.go @@ -0,0 +1,274 @@ +package top + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/discovery" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/klog/v2" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/completion" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" + metricsapi "k8s.io/metrics/pkg/apis/metrics" + metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1" + metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned" + + "github.com/karmada-io/karmada/pkg/karmadactl/options" +) + +type TopPodOptions struct { + ResourceName string + Namespace string + LabelSelector string + FieldSelector string + SortBy string + AllNamespaces bool + PrintContainers bool + NoHeaders bool + UseProtocolBuffers bool + Sum bool + + PodClient corev1client.PodsGetter + Printer *TopCmdPrinter + DiscoveryClient discovery.DiscoveryInterface + MetricsClient metricsclientset.Interface + + genericclioptions.IOStreams +} + +const metricsCreationDelay = 2 * time.Minute + +var ( + topPodLong = templates.LongDesc(i18n.T(` + Display resource (CPU/memory) usage of pods. + + The 'top pod' command allows you to see the resource consumption of pods of member clusters. + + Due to the metrics pipeline delay, they may be unavailable for a few minutes + since pod creation.`)) + + topPodExample = templates.Examples(i18n.T(` + # Show metrics for all pods in the default namespace + %[1]s top pod + + # Show metrics for all pods in the given namespace + %[1]s top pod --namespace=NAMESPACE + + # Show metrics for a given pod and its containers + %[1]s top pod POD_NAME --containers + + # Show metrics for the pods defined by label name=myLabel + %[1]s top pod -l name=myLabel`)) +) + +func NewCmdTopPod(f cmdutil.Factory, parentCommand string, o *TopPodOptions, streams genericclioptions.IOStreams) *cobra.Command { + if o == nil { + o = &TopPodOptions{ + IOStreams: streams, + UseProtocolBuffers: true, + } + } + + cmd := &cobra.Command{ + Use: "pod [NAME | -l label]", + DisableFlagsInUseLine: true, + Short: i18n.T("Display resource (CPU/memory) usage of pods of member clusters"), + Long: topPodLong, + Example: fmt.Sprintf(topPodExample, parentCommand), + ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "pod"), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(o.Complete(f, cmd, args)) + cmdutil.CheckErr(o.Validate()) + cmdutil.CheckErr(o.RunTopPod()) + }, + Aliases: []string{"pods", "po"}, + } + cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector) + options.AddKubeConfigFlags(cmd.Flags()) + cmd.Flags().StringVarP(options.DefaultConfigFlags.Namespace, "namespace", "n", *options.DefaultConfigFlags.Namespace, "If present, the namespace scope for this CLI request") + cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.") + cmd.Flags().StringVar(&o.SortBy, "sort-by", o.SortBy, "If non-empty, sort pods list using specified field. The field can be either 'cpu' or 'memory'.") + cmd.Flags().BoolVar(&o.PrintContainers, "containers", o.PrintContainers, "If present, print usage of containers within a pod.") + cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") + cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers.") + cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.") + cmd.Flags().BoolVar(&o.Sum, "sum", o.Sum, "Print the sum of the resource usage") + return cmd +} + +func (o *TopPodOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { + var err error + if len(args) == 1 { + o.ResourceName = args[0] + } else if len(args) > 1 { + return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) + } + + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return err + } + clientset, err := f.KubernetesClientSet() + if err != nil { + return err + } + + o.DiscoveryClient = clientset.DiscoveryClient + config, err := f.ToRESTConfig() + if err != nil { + return err + } + if o.UseProtocolBuffers { + config.ContentType = "application/vnd.kubernetes.protobuf" + } + o.MetricsClient, err = metricsclientset.NewForConfig(config) + if err != nil { + return err + } + + o.PodClient = clientset.CoreV1() + + o.Printer = NewTopCmdPrinter(o.Out) + return nil +} + +func (o *TopPodOptions) Validate() error { + if len(o.SortBy) > 0 { + if o.SortBy != sortByCPU && o.SortBy != sortByMemory { + return errors.New("--sort-by accepts only cpu or memory") + } + } + if len(o.ResourceName) > 0 && (len(o.LabelSelector) > 0 || len(o.FieldSelector) > 0) { + return errors.New("only one of NAME or selector can be provided") + } + return nil +} + +func (o *TopPodOptions) RunTopPod() error { + var err error + labelSelector := labels.Everything() + if len(o.LabelSelector) > 0 { + labelSelector, err = labels.Parse(o.LabelSelector) + if err != nil { + return err + } + } + fieldSelector := fields.Everything() + if len(o.FieldSelector) > 0 { + fieldSelector, err = fields.ParseSelector(o.FieldSelector) + if err != nil { + return err + } + } + + apiGroups, err := o.DiscoveryClient.ServerGroups() + if err != nil { + return err + } + + metricsAPIAvailable := SupportedMetricsAPIVersionAvailable(apiGroups) + + if !metricsAPIAvailable { + return errors.New("Metrics API not available") + } + metrics, err := getMetricsFromMetricsAPI(o.MetricsClient, o.Namespace, o.ResourceName, o.AllNamespaces, labelSelector, fieldSelector) + if err != nil { + return err + } + + // First we check why no metrics have been received. + if len(metrics.Items) == 0 { + // If the API server query is successful but all the pods are newly created, + // the metrics are probably not ready yet, so we return the error here in the first place. + err := verifyEmptyMetrics(o, labelSelector, fieldSelector) + if err != nil { + return err + } + + // if we had no errors, be sure we output something. + if o.AllNamespaces { + fmt.Fprintln(o.ErrOut, "No resources found") + } else { + fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace) + } + } + + return o.Printer.PrintPodMetrics(metrics.Items, o.PrintContainers, o.AllNamespaces, o.NoHeaders, o.SortBy, o.Sum) +} + +func getMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespace, resourceName string, allNamespaces bool, labelSelector labels.Selector, fieldSelector fields.Selector) (*metricsapi.PodMetricsList, error) { + var err error + ns := metav1.NamespaceAll + if !allNamespaces { + ns = namespace + } + versionedMetrics := &metricsv1beta1api.PodMetricsList{} + if resourceName != "" { + m, err := metricsClient.MetricsV1beta1().PodMetricses(ns).Get(context.TODO(), resourceName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + versionedMetrics.Items = []metricsv1beta1api.PodMetrics{*m} + } else { + versionedMetrics, err = metricsClient.MetricsV1beta1().PodMetricses(ns).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String(), FieldSelector: fieldSelector.String()}) + if err != nil { + return nil, err + } + } + metrics := &metricsapi.PodMetricsList{} + err = metricsv1beta1api.Convert_v1beta1_PodMetricsList_To_metrics_PodMetricsList(versionedMetrics, metrics, nil) + if err != nil { + return nil, err + } + return metrics, nil +} + +func verifyEmptyMetrics(o *TopPodOptions, labelSelector labels.Selector, fieldSelector fields.Selector) error { + if len(o.ResourceName) > 0 { + pod, err := o.PodClient.Pods(o.Namespace).Get(context.TODO(), o.ResourceName, metav1.GetOptions{}) + if err != nil { + return err + } + if err := checkPodAge(pod); err != nil { + return err + } + } else { + pods, err := o.PodClient.Pods(o.Namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labelSelector.String(), + FieldSelector: fieldSelector.String(), + }) + if err != nil { + return err + } + if len(pods.Items) == 0 { + return nil + } + for i := range pods.Items { + if err := checkPodAge(&pods.Items[i]); err != nil { + return err + } + } + } + return errors.New("metrics not available yet") +} + +func checkPodAge(pod *corev1.Pod) error { + age := time.Since(pod.CreationTimestamp.Time) + if age > metricsCreationDelay { + message := fmt.Sprintf("Metrics not available for pod %s/%s, age: %s", pod.Namespace, pod.Name, age.String()) + return errors.New(message) + } else { + klog.V(2).Infof("Metrics not yet available for pod %s/%s, age: %s", pod.Namespace, pod.Name, age.String()) + return nil + } +} diff --git a/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_printer.go b/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_printer.go new file mode 100644 index 000000000000..17091d924910 --- /dev/null +++ b/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_printer.go @@ -0,0 +1,236 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package metricsutil + +import ( + "fmt" + "io" + "sort" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/cli-runtime/pkg/printers" + metricsapi "k8s.io/metrics/pkg/apis/metrics" +) + +var ( + MeasuredResources = []v1.ResourceName{ + v1.ResourceCPU, + v1.ResourceMemory, + } + NodeColumns = []string{"NAME", "CPU(cores)", "CPU%", "MEMORY(bytes)", "MEMORY%"} + PodColumns = []string{"NAME", "CPU(cores)", "MEMORY(bytes)"} + NamespaceColumn = "NAMESPACE" + PodColumn = "POD" +) + +type ResourceMetricsInfo struct { + Name string + Metrics v1.ResourceList + Available v1.ResourceList +} + +type TopCmdPrinter struct { + out io.Writer +} + +func NewTopCmdPrinter(out io.Writer) *TopCmdPrinter { + return &TopCmdPrinter{out: out} +} + +func (printer *TopCmdPrinter) PrintNodeMetrics(metrics []metricsapi.NodeMetrics, availableResources map[string]v1.ResourceList, noHeaders bool, sortBy string) error { + if len(metrics) == 0 { + return nil + } + w := printers.GetNewTabWriter(printer.out) + defer w.Flush() + + sort.Sort(NewNodeMetricsSorter(metrics, sortBy)) + + if !noHeaders { + printColumnNames(w, NodeColumns) + } + var usage v1.ResourceList + for _, m := range metrics { + m.Usage.DeepCopyInto(&usage) + printMetricsLine(w, &ResourceMetricsInfo{ + Name: m.Name, + Metrics: usage, + Available: availableResources[m.Name], + }) + delete(availableResources, m.Name) + } + + // print lines for nodes of which the metrics is unreachable. + for nodeName := range availableResources { + printMissingMetricsNodeLine(w, nodeName) + } + return nil +} + +func (printer *TopCmdPrinter) PrintPodMetrics(metrics []metricsapi.PodMetrics, printContainers bool, withNamespace bool, noHeaders bool, sortBy string, sum bool) error { + if len(metrics) == 0 { + return nil + } + w := printers.GetNewTabWriter(printer.out) + defer w.Flush() + + columnWidth := len(PodColumns) + if !noHeaders { + if withNamespace { + printValue(w, NamespaceColumn) + columnWidth++ + } + if printContainers { + printValue(w, PodColumn) + columnWidth++ + } + printColumnNames(w, PodColumns) + } + + sort.Sort(NewPodMetricsSorter(metrics, withNamespace, sortBy)) + + for _, m := range metrics { + if printContainers { + sort.Sort(NewContainerMetricsSorter(m.Containers, sortBy)) + printSinglePodContainerMetrics(w, &m, withNamespace) + } else { + printSinglePodMetrics(w, &m, withNamespace) + } + + } + + if sum { + adder := NewResourceAdder(MeasuredResources) + for _, m := range metrics { + adder.AddPodMetrics(&m) + } + printPodResourcesSum(w, adder.total, columnWidth) + } + + return nil +} + +func printColumnNames(out io.Writer, names []string) { + for _, name := range names { + printValue(out, name) + } + fmt.Fprint(out, "\n") +} + +func printSinglePodMetrics(out io.Writer, m *metricsapi.PodMetrics, withNamespace bool) { + podMetrics := getPodMetrics(m) + if withNamespace { + printValue(out, m.Namespace) + } + printMetricsLine(out, &ResourceMetricsInfo{ + Name: m.Name, + Metrics: podMetrics, + Available: v1.ResourceList{}, + }) +} + +func printSinglePodContainerMetrics(out io.Writer, m *metricsapi.PodMetrics, withNamespace bool) { + for _, c := range m.Containers { + if withNamespace { + printValue(out, m.Namespace) + } + printValue(out, m.Name) + printMetricsLine(out, &ResourceMetricsInfo{ + Name: c.Name, + Metrics: c.Usage, + Available: v1.ResourceList{}, + }) + } +} + +func getPodMetrics(m *metricsapi.PodMetrics) v1.ResourceList { + podMetrics := make(v1.ResourceList) + for _, res := range MeasuredResources { + podMetrics[res], _ = resource.ParseQuantity("0") + } + + for _, c := range m.Containers { + for _, res := range MeasuredResources { + quantity := podMetrics[res] + quantity.Add(c.Usage[res]) + podMetrics[res] = quantity + } + } + return podMetrics +} + +func printMetricsLine(out io.Writer, metrics *ResourceMetricsInfo) { + printValue(out, metrics.Name) + printAllResourceUsages(out, metrics) + fmt.Fprint(out, "\n") +} + +func printMissingMetricsNodeLine(out io.Writer, nodeName string) { + printValue(out, nodeName) + unknownMetricsStatus := "" + for i := 0; i < len(MeasuredResources); i++ { + printValue(out, unknownMetricsStatus) + printValue(out, unknownMetricsStatus) + } + fmt.Fprint(out, "\n") +} + +func printValue(out io.Writer, value interface{}) { + fmt.Fprintf(out, "%v\t", value) +} + +func printAllResourceUsages(out io.Writer, metrics *ResourceMetricsInfo) { + for _, res := range MeasuredResources { + quantity := metrics.Metrics[res] + printSingleResourceUsage(out, res, quantity) + fmt.Fprint(out, "\t") + if available, found := metrics.Available[res]; found { + fraction := float64(quantity.MilliValue()) / float64(available.MilliValue()) * 100 + fmt.Fprintf(out, "%d%%\t", int64(fraction)) + } + } +} + +func printSingleResourceUsage(out io.Writer, resourceType v1.ResourceName, quantity resource.Quantity) { + switch resourceType { + case v1.ResourceCPU: + fmt.Fprintf(out, "%vm", quantity.MilliValue()) + case v1.ResourceMemory: + fmt.Fprintf(out, "%vMi", quantity.Value()/(1024*1024)) + default: + fmt.Fprintf(out, "%v", quantity.Value()) + } +} + +func printPodResourcesSum(out io.Writer, total v1.ResourceList, columnWidth int) { + for i := 0; i < columnWidth-2; i++ { + printValue(out, "") + } + printValue(out, "________") + printValue(out, "________") + fmt.Fprintf(out, "\n") + for i := 0; i < columnWidth-3; i++ { + printValue(out, "") + } + printMetricsLine(out, &ResourceMetricsInfo{ + Name: "", + Metrics: total, + Available: v1.ResourceList{}, + }) + +} diff --git a/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_resource_adder.go b/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_resource_adder.go new file mode 100644 index 000000000000..b0ee3f22e0ea --- /dev/null +++ b/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_resource_adder.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package metricsutil + +import ( + corev1 "k8s.io/api/core/v1" + metricsapi "k8s.io/metrics/pkg/apis/metrics" +) + +type ResourceAdder struct { + resources []corev1.ResourceName + total corev1.ResourceList +} + +func NewResourceAdder(resources []corev1.ResourceName) *ResourceAdder { + return &ResourceAdder{ + resources: resources, + total: make(corev1.ResourceList), + } +} + +// AddPodMetrics adds each pod metric to the total +func (adder *ResourceAdder) AddPodMetrics(m *metricsapi.PodMetrics) { + for _, c := range m.Containers { + for _, res := range adder.resources { + total := adder.total[res] + total.Add(c.Usage[res]) + adder.total[res] = total + } + } +} diff --git a/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_sorter.go b/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_sorter.go new file mode 100644 index 000000000000..608d35e43729 --- /dev/null +++ b/vendor/k8s.io/kubectl/pkg/metricsutil/metrics_sorter.go @@ -0,0 +1,130 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package metricsutil + +import ( + "k8s.io/api/core/v1" + metricsapi "k8s.io/metrics/pkg/apis/metrics" +) + +type NodeMetricsSorter struct { + metrics []metricsapi.NodeMetrics + sortBy string +} + +func (n *NodeMetricsSorter) Len() int { + return len(n.metrics) +} + +func (n *NodeMetricsSorter) Swap(i, j int) { + n.metrics[i], n.metrics[j] = n.metrics[j], n.metrics[i] +} + +func (n *NodeMetricsSorter) Less(i, j int) bool { + switch n.sortBy { + case "cpu": + return n.metrics[i].Usage.Cpu().MilliValue() > n.metrics[j].Usage.Cpu().MilliValue() + case "memory": + return n.metrics[i].Usage.Memory().Value() > n.metrics[j].Usage.Memory().Value() + default: + return n.metrics[i].Name < n.metrics[j].Name + } +} + +func NewNodeMetricsSorter(metrics []metricsapi.NodeMetrics, sortBy string) *NodeMetricsSorter { + return &NodeMetricsSorter{ + metrics: metrics, + sortBy: sortBy, + } +} + +type PodMetricsSorter struct { + metrics []metricsapi.PodMetrics + sortBy string + withNamespace bool + podMetrics []v1.ResourceList +} + +func (p *PodMetricsSorter) Len() int { + return len(p.metrics) +} + +func (p *PodMetricsSorter) Swap(i, j int) { + p.metrics[i], p.metrics[j] = p.metrics[j], p.metrics[i] + p.podMetrics[i], p.podMetrics[j] = p.podMetrics[j], p.podMetrics[i] +} + +func (p *PodMetricsSorter) Less(i, j int) bool { + switch p.sortBy { + case "cpu": + return p.podMetrics[i].Cpu().MilliValue() > p.podMetrics[j].Cpu().MilliValue() + case "memory": + return p.podMetrics[i].Memory().Value() > p.podMetrics[j].Memory().Value() + default: + if p.withNamespace && p.metrics[i].Namespace != p.metrics[j].Namespace { + return p.metrics[i].Namespace < p.metrics[j].Namespace + } + return p.metrics[i].Name < p.metrics[j].Name + } +} + +func NewPodMetricsSorter(metrics []metricsapi.PodMetrics, withNamespace bool, sortBy string) *PodMetricsSorter { + var podMetrics = make([]v1.ResourceList, len(metrics)) + if len(sortBy) > 0 { + for i, v := range metrics { + podMetrics[i] = getPodMetrics(&v) + } + } + + return &PodMetricsSorter{ + metrics: metrics, + sortBy: sortBy, + withNamespace: withNamespace, + podMetrics: podMetrics, + } +} + +type ContainerMetricsSorter struct { + metrics []metricsapi.ContainerMetrics + sortBy string +} + +func (s *ContainerMetricsSorter) Len() int { + return len(s.metrics) +} + +func (s *ContainerMetricsSorter) Swap(i, j int) { + s.metrics[i], s.metrics[j] = s.metrics[j], s.metrics[i] +} + +func (s *ContainerMetricsSorter) Less(i, j int) bool { + switch s.sortBy { + case "cpu": + return s.metrics[i].Usage.Cpu().MilliValue() > s.metrics[j].Usage.Cpu().MilliValue() + case "memory": + return s.metrics[i].Usage.Memory().Value() > s.metrics[j].Usage.Memory().Value() + default: + return s.metrics[i].Name < s.metrics[j].Name + } +} + +func NewContainerMetricsSorter(metrics []metricsapi.ContainerMetrics, sortBy string) *ContainerMetricsSorter { + return &ContainerMetricsSorter{ + metrics: metrics, + sortBy: sortBy, + } +} diff --git a/vendor/k8s.io/metrics/pkg/client/clientset/versioned/clientset.go b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 000000000000..f5fba6d0c992 --- /dev/null +++ b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,133 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + "net/http" + + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" + metricsv1alpha1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1" + metricsv1beta1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + MetricsV1alpha1() metricsv1alpha1.MetricsV1alpha1Interface + MetricsV1beta1() metricsv1beta1.MetricsV1beta1Interface +} + +// Clientset contains the clients for groups. +type Clientset struct { + *discovery.DiscoveryClient + metricsV1alpha1 *metricsv1alpha1.MetricsV1alpha1Client + metricsV1beta1 *metricsv1beta1.MetricsV1beta1Client +} + +// MetricsV1alpha1 retrieves the MetricsV1alpha1Client +func (c *Clientset) MetricsV1alpha1() metricsv1alpha1.MetricsV1alpha1Interface { + return c.metricsV1alpha1 +} + +// MetricsV1beta1 retrieves the MetricsV1beta1Client +func (c *Clientset) MetricsV1beta1() metricsv1beta1.MetricsV1beta1Interface { + return c.metricsV1beta1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + + if configShallowCopy.UserAgent == "" { + configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() + } + + // share the transport between all clients + httpClient, err := rest.HTTPClientFor(&configShallowCopy) + if err != nil { + return nil, err + } + + return NewForConfigAndClient(&configShallowCopy, httpClient) +} + +// NewForConfigAndClient creates a new Clientset for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfigAndClient will generate a rate-limiter in configShallowCopy. +func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + + var cs Clientset + var err error + cs.metricsV1alpha1, err = metricsv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } + cs.metricsV1beta1, err = metricsv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + cs, err := NewForConfig(c) + if err != nil { + panic(err) + } + return cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.metricsV1alpha1 = metricsv1alpha1.New(c) + cs.metricsV1beta1 = metricsv1beta1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/doc.go b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/doc.go new file mode 100644 index 000000000000..df51baa4d4c1 --- /dev/null +++ b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/generated_expansion.go b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/generated_expansion.go new file mode 100644 index 000000000000..e8fc33bbb140 --- /dev/null +++ b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type NodeMetricsExpansion interface{} + +type PodMetricsExpansion interface{} diff --git a/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/metrics_client.go b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/metrics_client.go new file mode 100644 index 000000000000..efc23042d476 --- /dev/null +++ b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/metrics_client.go @@ -0,0 +1,112 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "net/http" + + rest "k8s.io/client-go/rest" + v1alpha1 "k8s.io/metrics/pkg/apis/metrics/v1alpha1" + "k8s.io/metrics/pkg/client/clientset/versioned/scheme" +) + +type MetricsV1alpha1Interface interface { + RESTClient() rest.Interface + NodeMetricsesGetter + PodMetricsesGetter +} + +// MetricsV1alpha1Client is used to interact with features provided by the metrics.k8s.io group. +type MetricsV1alpha1Client struct { + restClient rest.Interface +} + +func (c *MetricsV1alpha1Client) NodeMetricses() NodeMetricsInterface { + return newNodeMetricses(c) +} + +func (c *MetricsV1alpha1Client) PodMetricses(namespace string) PodMetricsInterface { + return newPodMetricses(c, namespace) +} + +// NewForConfig creates a new MetricsV1alpha1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*MetricsV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new MetricsV1alpha1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*MetricsV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &MetricsV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new MetricsV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *MetricsV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new MetricsV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *MetricsV1alpha1Client { + return &MetricsV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *MetricsV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/nodemetrics.go b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/nodemetrics.go new file mode 100644 index 000000000000..d79163ddb816 --- /dev/null +++ b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/nodemetrics.go @@ -0,0 +1,98 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "k8s.io/metrics/pkg/apis/metrics/v1alpha1" + scheme "k8s.io/metrics/pkg/client/clientset/versioned/scheme" +) + +// NodeMetricsesGetter has a method to return a NodeMetricsInterface. +// A group's client should implement this interface. +type NodeMetricsesGetter interface { + NodeMetricses() NodeMetricsInterface +} + +// NodeMetricsInterface has methods to work with NodeMetrics resources. +type NodeMetricsInterface interface { + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NodeMetrics, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.NodeMetricsList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + NodeMetricsExpansion +} + +// nodeMetricses implements NodeMetricsInterface +type nodeMetricses struct { + client rest.Interface +} + +// newNodeMetricses returns a NodeMetricses +func newNodeMetricses(c *MetricsV1alpha1Client) *nodeMetricses { + return &nodeMetricses{ + client: c.RESTClient(), + } +} + +// Get takes name of the nodeMetrics, and returns the corresponding nodeMetrics object, and an error if there is any. +func (c *nodeMetricses) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.NodeMetrics, err error) { + result = &v1alpha1.NodeMetrics{} + err = c.client.Get(). + Resource("nodes"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of NodeMetricses that match those selectors. +func (c *nodeMetricses) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.NodeMetricsList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.NodeMetricsList{} + err = c.client.Get(). + Resource("nodes"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested nodeMetricses. +func (c *nodeMetricses) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("nodes"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} diff --git a/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/podmetrics.go b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/podmetrics.go new file mode 100644 index 000000000000..49d57c8e887e --- /dev/null +++ b/vendor/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1/podmetrics.go @@ -0,0 +1,103 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "k8s.io/metrics/pkg/apis/metrics/v1alpha1" + scheme "k8s.io/metrics/pkg/client/clientset/versioned/scheme" +) + +// PodMetricsesGetter has a method to return a PodMetricsInterface. +// A group's client should implement this interface. +type PodMetricsesGetter interface { + PodMetricses(namespace string) PodMetricsInterface +} + +// PodMetricsInterface has methods to work with PodMetrics resources. +type PodMetricsInterface interface { + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PodMetrics, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PodMetricsList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + PodMetricsExpansion +} + +// podMetricses implements PodMetricsInterface +type podMetricses struct { + client rest.Interface + ns string +} + +// newPodMetricses returns a PodMetricses +func newPodMetricses(c *MetricsV1alpha1Client, namespace string) *podMetricses { + return &podMetricses{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the podMetrics, and returns the corresponding podMetrics object, and an error if there is any. +func (c *podMetricses) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PodMetrics, err error) { + result = &v1alpha1.PodMetrics{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pods"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PodMetricses that match those selectors. +func (c *podMetricses) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PodMetricsList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PodMetricsList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pods"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested podMetricses. +func (c *podMetricses) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pods"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c3162a511aed..ea94f5687e8f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1608,6 +1608,7 @@ k8s.io/kubectl/pkg/cmd/util/editor/crlf k8s.io/kubectl/pkg/cmd/util/podcmd k8s.io/kubectl/pkg/cmd/wait k8s.io/kubectl/pkg/describe +k8s.io/kubectl/pkg/metricsutil k8s.io/kubectl/pkg/polymorphichelpers k8s.io/kubectl/pkg/rawhttp k8s.io/kubectl/pkg/scheme @@ -1644,7 +1645,9 @@ k8s.io/metrics/pkg/apis/metrics k8s.io/metrics/pkg/apis/metrics/install k8s.io/metrics/pkg/apis/metrics/v1alpha1 k8s.io/metrics/pkg/apis/metrics/v1beta1 +k8s.io/metrics/pkg/client/clientset/versioned k8s.io/metrics/pkg/client/clientset/versioned/scheme +k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1 k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1 k8s.io/metrics/pkg/client/custom_metrics k8s.io/metrics/pkg/client/custom_metrics/scheme