2019-01-12 04:58:27 +00:00
/ *
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 top
import (
2020-03-26 21:07:15 +00:00
"context"
2019-01-12 04:58:27 +00:00
"errors"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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"
2019-09-27 21:51:53 +00:00
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/metricsutil"
2021-07-02 08:43:15 +00:00
"k8s.io/kubectl/pkg/util"
2019-09-27 21:51:53 +00:00
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
2019-01-12 04:58:27 +00:00
metricsapi "k8s.io/metrics/pkg/apis/metrics"
metricsV1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned"
)
// TopNodeOptions contains all the options for running the top-node cli command.
type TopNodeOptions struct {
2021-03-18 22:40:29 +00:00
ResourceName string
Selector string
SortBy string
NoHeaders bool
UseProtocolBuffers bool
2019-01-12 04:58:27 +00:00
NodeClient corev1client . CoreV1Interface
Printer * metricsutil . TopCmdPrinter
DiscoveryClient discovery . DiscoveryInterface
MetricsClient metricsclientset . Interface
genericclioptions . IOStreams
}
var (
topNodeLong = templates . LongDesc ( i18n . T ( `
2021-07-02 08:43:15 +00:00
Display resource ( CPU / memory ) usage of nodes .
2019-01-12 04:58:27 +00:00
The top - node command allows you to see the resource consumption of nodes . ` ) )
topNodeExample = templates . Examples ( i18n . T ( `
# Show metrics for all nodes
kubectl top node
# Show metrics for a given node
kubectl top node NODE_NAME ` ) )
)
func NewCmdTopNode ( f cmdutil . Factory , o * TopNodeOptions , streams genericclioptions . IOStreams ) * cobra . Command {
if o == nil {
o = & TopNodeOptions {
2021-07-02 08:43:15 +00:00
IOStreams : streams ,
UseProtocolBuffers : true ,
2019-01-12 04:58:27 +00:00
}
}
cmd := & cobra . Command {
Use : "node [NAME | -l label]" ,
DisableFlagsInUseLine : true ,
2021-07-02 08:43:15 +00:00
Short : i18n . T ( "Display resource (CPU/memory) usage of nodes" ) ,
2019-01-12 04:58:27 +00:00
Long : topNodeLong ,
Example : topNodeExample ,
2021-07-02 08:43:15 +00:00
ValidArgsFunction : util . ResourceNameCompletionFunc ( f , "node" ) ,
2019-01-12 04:58:27 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
cmdutil . CheckErr ( o . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( o . Validate ( ) )
cmdutil . CheckErr ( o . RunTopNode ( ) )
} ,
Aliases : [ ] string { "nodes" , "no" } ,
}
cmd . Flags ( ) . StringVarP ( & o . Selector , "selector" , "l" , o . Selector , "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)" )
2021-07-02 08:43:15 +00:00
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'." )
2019-01-12 04:58:27 +00:00
cmd . Flags ( ) . BoolVar ( & o . NoHeaders , "no-headers" , o . NoHeaders , "If present, print output without headers" )
2021-07-02 08:43:15 +00:00
cmd . Flags ( ) . BoolVar ( & o . UseProtocolBuffers , "use-protocol-buffers" , o . UseProtocolBuffers , "Enables using protocol-buffers to access Metrics API." )
2019-01-12 04:58:27 +00:00
return cmd
}
func ( o * TopNodeOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string ) error {
if len ( args ) == 1 {
o . ResourceName = args [ 0 ]
} else if len ( args ) > 1 {
return cmdutil . UsageErrorf ( cmd , "%s" , cmd . Use )
}
clientset , err := f . KubernetesClientSet ( )
if err != nil {
return err
}
o . DiscoveryClient = clientset . DiscoveryClient
config , err := f . ToRESTConfig ( )
if err != nil {
return err
}
2021-03-18 22:40:29 +00:00
if o . UseProtocolBuffers {
config . ContentType = "application/vnd.kubernetes.protobuf"
}
2019-01-12 04:58:27 +00:00
o . MetricsClient , err = metricsclientset . NewForConfig ( config )
if err != nil {
return err
}
o . NodeClient = clientset . CoreV1 ( )
o . Printer = metricsutil . NewTopCmdPrinter ( o . Out )
return nil
}
func ( o * TopNodeOptions ) Validate ( ) error {
2019-08-30 18:33:25 +00:00
if len ( o . SortBy ) > 0 {
if o . SortBy != sortByCPU && o . SortBy != sortByMemory {
return errors . New ( "--sort-by accepts only cpu or memory" )
}
}
2019-01-12 04:58:27 +00:00
if len ( o . ResourceName ) > 0 && len ( o . Selector ) > 0 {
return errors . New ( "only one of NAME or --selector can be provided" )
}
return nil
}
func ( o TopNodeOptions ) RunTopNode ( ) error {
var err error
selector := labels . Everything ( )
if len ( o . Selector ) > 0 {
selector , err = labels . Parse ( o . Selector )
if err != nil {
return err
}
}
apiGroups , err := o . DiscoveryClient . ServerGroups ( )
if err != nil {
return err
}
metricsAPIAvailable := SupportedMetricsAPIVersionAvailable ( apiGroups )
2020-08-10 17:43:49 +00:00
if ! metricsAPIAvailable {
return errors . New ( "Metrics API not available" )
}
metrics , err := getNodeMetricsFromMetricsAPI ( o . MetricsClient , o . ResourceName , selector )
if err != nil {
return err
2019-01-12 04:58:27 +00:00
}
if len ( metrics . Items ) == 0 {
return errors . New ( "metrics not available yet" )
}
var nodes [ ] v1 . Node
if len ( o . ResourceName ) > 0 {
2020-03-26 21:07:15 +00:00
node , err := o . NodeClient . Nodes ( ) . Get ( context . TODO ( ) , o . ResourceName , metav1 . GetOptions { } )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
nodes = append ( nodes , * node )
} else {
2020-03-26 21:07:15 +00:00
nodeList , err := o . NodeClient . Nodes ( ) . List ( context . TODO ( ) , metav1 . ListOptions {
2019-01-12 04:58:27 +00:00
LabelSelector : selector . String ( ) ,
} )
if err != nil {
return err
}
nodes = append ( nodes , nodeList . Items ... )
}
allocatable := make ( map [ string ] v1 . ResourceList )
for _ , n := range nodes {
allocatable [ n . Name ] = n . Status . Allocatable
}
2019-08-30 18:33:25 +00:00
return o . Printer . PrintNodeMetrics ( metrics . Items , allocatable , o . NoHeaders , o . SortBy )
2019-01-12 04:58:27 +00:00
}
func getNodeMetricsFromMetricsAPI ( metricsClient metricsclientset . Interface , resourceName string , selector labels . Selector ) ( * metricsapi . NodeMetricsList , error ) {
var err error
versionedMetrics := & metricsV1beta1api . NodeMetricsList { }
2019-04-07 17:07:55 +00:00
mc := metricsClient . MetricsV1beta1 ( )
2019-01-12 04:58:27 +00:00
nm := mc . NodeMetricses ( )
if resourceName != "" {
2020-03-26 21:07:15 +00:00
m , err := nm . Get ( context . TODO ( ) , resourceName , metav1 . GetOptions { } )
2019-01-12 04:58:27 +00:00
if err != nil {
return nil , err
}
versionedMetrics . Items = [ ] metricsV1beta1api . NodeMetrics { * m }
} else {
2020-03-26 21:07:15 +00:00
versionedMetrics , err = nm . List ( context . TODO ( ) , metav1 . ListOptions { LabelSelector : selector . String ( ) } )
2019-01-12 04:58:27 +00:00
if err != nil {
return nil , err
}
}
metrics := & metricsapi . NodeMetricsList { }
err = metricsV1beta1api . Convert_v1beta1_NodeMetricsList_To_metrics_NodeMetricsList ( versionedMetrics , metrics , nil )
if err != nil {
return nil , err
}
return metrics , nil
}