top_node.go 7.3 KB

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