Merge pull request #3593 from chaunceyjiang/top

feat: introducing a karmadactl top command
This commit is contained in:
karmada-bot 2023-08-15 20:02:55 +08:00 committed by GitHub
commit f243a18296
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1517 additions and 0 deletions

View File

@ -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),
},
},
}

View File

@ -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
}
}
}

View File

@ -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,
}
}

58
pkg/karmadactl/top/top.go Normal file
View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 := "<unknown>"
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{},
})
}

View File

@ -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
}
}
}

130
vendor/k8s.io/kubectl/pkg/metricsutil/metrics_sorter.go generated vendored Normal file
View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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{}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

3
vendor/modules.txt vendored
View File

@ -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