...

Source file src/k8s.io/kubectl/pkg/cmd/top/top_node.go

Documentation: k8s.io/kubectl/pkg/cmd/top

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package top
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  
    23  	"github.com/spf13/cobra"
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/cli-runtime/pkg/genericiooptions"
    28  	"k8s.io/client-go/discovery"
    29  	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
    30  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    31  	"k8s.io/kubectl/pkg/metricsutil"
    32  	"k8s.io/kubectl/pkg/util/completion"
    33  	"k8s.io/kubectl/pkg/util/i18n"
    34  	"k8s.io/kubectl/pkg/util/templates"
    35  	metricsapi "k8s.io/metrics/pkg/apis/metrics"
    36  	metricsV1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
    37  	metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned"
    38  )
    39  
    40  // TopNodeOptions contains all the options for running the top-node cli command.
    41  type TopNodeOptions struct {
    42  	ResourceName       string
    43  	Selector           string
    44  	SortBy             string
    45  	NoHeaders          bool
    46  	UseProtocolBuffers bool
    47  	ShowCapacity       bool
    48  
    49  	NodeClient      corev1client.CoreV1Interface
    50  	Printer         *metricsutil.TopCmdPrinter
    51  	DiscoveryClient discovery.DiscoveryInterface
    52  	MetricsClient   metricsclientset.Interface
    53  
    54  	genericiooptions.IOStreams
    55  }
    56  
    57  var (
    58  	topNodeLong = templates.LongDesc(i18n.T(`
    59  		Display resource (CPU/memory) usage of nodes.
    60  
    61  		The top-node command allows you to see the resource consumption of nodes.`))
    62  
    63  	topNodeExample = templates.Examples(i18n.T(`
    64  		  # Show metrics for all nodes
    65  		  kubectl top node
    66  
    67  		  # Show metrics for a given node
    68  		  kubectl top node NODE_NAME`))
    69  )
    70  
    71  func NewCmdTopNode(f cmdutil.Factory, o *TopNodeOptions, streams genericiooptions.IOStreams) *cobra.Command {
    72  	if o == nil {
    73  		o = &TopNodeOptions{
    74  			IOStreams:          streams,
    75  			UseProtocolBuffers: true,
    76  		}
    77  	}
    78  
    79  	cmd := &cobra.Command{
    80  		Use:                   "node [NAME | -l label]",
    81  		DisableFlagsInUseLine: true,
    82  		Short:                 i18n.T("Display resource (CPU/memory) usage of nodes"),
    83  		Long:                  topNodeLong,
    84  		Example:               topNodeExample,
    85  		ValidArgsFunction:     completion.ResourceNameCompletionFunc(f, "node"),
    86  		Run: func(cmd *cobra.Command, args []string) {
    87  			cmdutil.CheckErr(o.Complete(f, cmd, args))
    88  			cmdutil.CheckErr(o.Validate())
    89  			cmdutil.CheckErr(o.RunTopNode())
    90  		},
    91  		Aliases: []string{"nodes", "no"},
    92  	}
    93  	cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector)
    94  	cmd.Flags().StringVar(&o.SortBy, "sort-by", o.SortBy, "If non-empty, sort nodes list using specified field. The field can be either 'cpu' or 'memory'.")
    95  	cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers")
    96  	cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.")
    97  	cmd.Flags().BoolVar(&o.ShowCapacity, "show-capacity", o.ShowCapacity, "Print node resources based on Capacity instead of Allocatable(default) of the nodes.")
    98  
    99  	return cmd
   100  }
   101  
   102  func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
   103  	if len(args) == 1 {
   104  		o.ResourceName = args[0]
   105  	} else if len(args) > 1 {
   106  		return cmdutil.UsageErrorf(cmd, "%s", cmd.Use)
   107  	}
   108  
   109  	clientset, err := f.KubernetesClientSet()
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	o.DiscoveryClient = clientset.DiscoveryClient
   115  
   116  	config, err := f.ToRESTConfig()
   117  	if err != nil {
   118  		return err
   119  	}
   120  	if o.UseProtocolBuffers {
   121  		config.ContentType = "application/vnd.kubernetes.protobuf"
   122  	}
   123  	o.MetricsClient, err = metricsclientset.NewForConfig(config)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	o.NodeClient = clientset.CoreV1()
   129  
   130  	o.Printer = metricsutil.NewTopCmdPrinter(o.Out)
   131  	return nil
   132  }
   133  
   134  func (o *TopNodeOptions) Validate() error {
   135  	if len(o.SortBy) > 0 {
   136  		if o.SortBy != sortByCPU && o.SortBy != sortByMemory {
   137  			return errors.New("--sort-by accepts only cpu or memory")
   138  		}
   139  	}
   140  	if len(o.ResourceName) > 0 && len(o.Selector) > 0 {
   141  		return errors.New("only one of NAME or --selector can be provided")
   142  	}
   143  	return nil
   144  }
   145  
   146  func (o TopNodeOptions) RunTopNode() error {
   147  	var err error
   148  	selector := labels.Everything()
   149  	if len(o.Selector) > 0 {
   150  		selector, err = labels.Parse(o.Selector)
   151  		if err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	apiGroups, err := o.DiscoveryClient.ServerGroups()
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	metricsAPIAvailable := SupportedMetricsAPIVersionAvailable(apiGroups)
   162  
   163  	if !metricsAPIAvailable {
   164  		return errors.New("Metrics API not available")
   165  	}
   166  
   167  	metrics, err := getNodeMetricsFromMetricsAPI(o.MetricsClient, o.ResourceName, selector)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	if len(metrics.Items) == 0 {
   173  		return errors.New("metrics not available yet")
   174  	}
   175  
   176  	var nodes []v1.Node
   177  	if len(o.ResourceName) > 0 {
   178  		node, err := o.NodeClient.Nodes().Get(context.TODO(), o.ResourceName, metav1.GetOptions{})
   179  		if err != nil {
   180  			return err
   181  		}
   182  		nodes = append(nodes, *node)
   183  	} else {
   184  		nodeList, err := o.NodeClient.Nodes().List(context.TODO(), metav1.ListOptions{
   185  			LabelSelector: selector.String(),
   186  		})
   187  		if err != nil {
   188  			return err
   189  		}
   190  		nodes = append(nodes, nodeList.Items...)
   191  	}
   192  
   193  	availableResources := make(map[string]v1.ResourceList)
   194  
   195  	for _, n := range nodes {
   196  		if !o.ShowCapacity {
   197  			availableResources[n.Name] = n.Status.Allocatable
   198  		} else {
   199  			availableResources[n.Name] = n.Status.Capacity
   200  		}
   201  	}
   202  
   203  	return o.Printer.PrintNodeMetrics(metrics.Items, availableResources, o.NoHeaders, o.SortBy)
   204  }
   205  
   206  func getNodeMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, resourceName string, selector labels.Selector) (*metricsapi.NodeMetricsList, error) {
   207  	var err error
   208  	versionedMetrics := &metricsV1beta1api.NodeMetricsList{}
   209  	mc := metricsClient.MetricsV1beta1()
   210  	nm := mc.NodeMetricses()
   211  	if resourceName != "" {
   212  		m, err := nm.Get(context.TODO(), resourceName, metav1.GetOptions{})
   213  		if err != nil {
   214  			return nil, err
   215  		}
   216  		versionedMetrics.Items = []metricsV1beta1api.NodeMetrics{*m}
   217  	} else {
   218  		versionedMetrics, err = nm.List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()})
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  	}
   223  	metrics := &metricsapi.NodeMetricsList{}
   224  	err = metricsV1beta1api.Convert_v1beta1_NodeMetricsList_To_metrics_NodeMetricsList(versionedMetrics, metrics, nil)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	return metrics, nil
   229  }
   230  

View as plain text