2016-08-11 15:24:22 +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 cmd
import (
"errors"
2016-08-26 09:44:14 +00:00
"fmt"
2016-08-11 15:24:22 +00:00
"io"
2016-08-26 09:44:14 +00:00
"time"
2016-08-11 15:24:22 +00:00
2017-10-31 08:15:32 +00:00
"k8s.io/api/core/v1"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
2017-11-15 19:41:04 +00:00
"k8s.io/client-go/discovery"
2017-10-31 08:15:32 +00:00
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
2016-10-07 22:24:42 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
2016-08-11 15:24:22 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/metricsutil"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2017-11-15 19:41:04 +00:00
metricsapi "k8s.io/metrics/pkg/apis/metrics"
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
metricsclientset "k8s.io/metrics/pkg/client/clientset_generated/clientset"
2016-08-11 15:24:22 +00:00
2016-08-26 09:44:14 +00:00
"github.com/golang/glog"
2016-08-11 15:24:22 +00:00
"github.com/spf13/cobra"
)
type TopPodOptions struct {
ResourceName string
2016-08-26 09:44:14 +00:00
Namespace string
Selector string
2016-08-11 15:24:22 +00:00
AllNamespaces bool
PrintContainers bool
2017-10-31 08:15:32 +00:00
PodClient corev1 . PodsGetter
2016-11-29 20:48:53 +00:00
HeapsterOptions HeapsterTopOptions
2016-08-11 15:24:22 +00:00
Client * metricsutil . HeapsterMetricsClient
Printer * metricsutil . TopCmdPrinter
2017-11-15 19:41:04 +00:00
DiscoveryClient discovery . DiscoveryInterface
MetricsClient metricsclientset . Interface
2016-08-11 15:24:22 +00:00
}
2016-08-26 09:44:14 +00:00
const metricsCreationDelay = 2 * time . Minute
2016-08-11 15:24:22 +00:00
var (
2017-03-15 03:49:10 +00:00
topPodLong = templates . LongDesc ( i18n . T ( `
2016-08-11 15:24:22 +00:00
Display Resource ( CPU / Memory / Storage ) usage of pods .
2016-08-26 09:44:14 +00:00
The ' top pod ' command allows you to see the resource consumption of pods .
Due to the metrics pipeline delay , they may be unavailable for a few minutes
2017-03-15 03:49:10 +00:00
since pod creation . ` ) )
2016-08-11 15:24:22 +00:00
2017-03-15 03:49:10 +00:00
topPodExample = templates . Examples ( i18n . T ( `
2016-10-07 22:24:42 +00:00
# Show metrics for all pods in the default namespace
kubectl top pod
2016-08-11 15:24:22 +00:00
2016-10-07 22:24:42 +00:00
# Show metrics for all pods in the given namespace
kubectl top pod -- namespace = NAMESPACE
2016-08-11 15:24:22 +00:00
2016-10-07 22:24:42 +00:00
# Show metrics for a given pod and its containers
kubectl top pod POD_NAME -- containers
2016-08-11 15:24:22 +00:00
2016-10-07 22:24:42 +00:00
# Show metrics for the pods defined by label name = myLabel
2017-03-15 03:49:10 +00:00
kubectl top pod - l name = myLabel ` ) )
2016-08-11 15:24:22 +00:00
)
2018-02-21 16:01:09 +00:00
func NewCmdTopPod ( f cmdutil . Factory , options * TopPodOptions , out io . Writer ) ( * cobra . Command , * TopPodOptions ) {
2017-08-15 18:59:56 +00:00
if options == nil {
options = & TopPodOptions { }
}
2016-08-11 15:24:22 +00:00
cmd := & cobra . Command {
2017-10-11 06:26:02 +00:00
Use : "pod [NAME | -l label]" ,
DisableFlagsInUseLine : true ,
2017-01-25 01:00:32 +00:00
Short : i18n . T ( "Display Resource (CPU/Memory/Storage) usage of pods" ) ,
2016-08-11 15:24:22 +00:00
Long : topPodLong ,
Example : topPodExample ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
if err := options . Complete ( f , cmd , args , out ) ; err != nil {
cmdutil . CheckErr ( err )
}
2016-09-12 15:59:31 +00:00
if err := options . Validate ( ) ; err != nil {
2017-06-14 21:14:42 +00:00
cmdutil . CheckErr ( cmdutil . UsageErrorf ( cmd , "%v" , err ) )
2016-09-12 15:59:31 +00:00
}
2016-08-11 15:24:22 +00:00
if err := options . RunTopPod ( ) ; err != nil {
cmdutil . CheckErr ( err )
}
} ,
2016-12-24 16:25:08 +00:00
Aliases : [ ] string { "pods" , "po" } ,
2016-08-11 15:24:22 +00:00
}
2017-08-17 12:27:11 +00:00
cmd . Flags ( ) . StringVarP ( & options . Selector , "selector" , "l" , "" , "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)" )
2016-08-11 15:24:22 +00:00
cmd . Flags ( ) . BoolVar ( & options . PrintContainers , "containers" , false , "If present, print usage of containers within a pod." )
cmd . Flags ( ) . BoolVar ( & options . AllNamespaces , "all-namespaces" , false , "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace." )
2016-11-29 20:48:53 +00:00
options . HeapsterOptions . Bind ( cmd . Flags ( ) )
2018-02-21 16:01:09 +00:00
return cmd , options
2016-08-11 15:24:22 +00:00
}
2016-10-13 00:18:39 +00:00
func ( o * TopPodOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string , out io . Writer ) error {
2016-08-11 15:24:22 +00:00
var err error
if len ( args ) == 1 {
o . ResourceName = args [ 0 ]
} else if len ( args ) > 1 {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "%s" , cmd . Use )
2016-08-11 15:24:22 +00:00
}
o . Namespace , _ , err = f . DefaultNamespace ( )
if err != nil {
return err
}
2017-10-31 08:15:32 +00:00
clientset , err := f . KubernetesClientSet ( )
2016-08-11 15:24:22 +00:00
if err != nil {
return err
}
2017-11-15 19:41:04 +00:00
o . DiscoveryClient = clientset . DiscoveryClient
2018-02-21 16:01:09 +00:00
config , err := f . ClientConfig ( )
if err != nil {
return err
}
o . MetricsClient , err = metricsclientset . NewForConfig ( config )
2017-11-15 19:41:04 +00:00
if err != nil {
return err
}
2017-11-12 11:00:21 +00:00
o . PodClient = clientset . CoreV1 ( )
o . Client = metricsutil . NewHeapsterMetricsClient ( clientset . CoreV1 ( ) , o . HeapsterOptions . Namespace , o . HeapsterOptions . Scheme , o . HeapsterOptions . Service , o . HeapsterOptions . Port )
2017-11-15 19:41:04 +00:00
2016-08-11 15:24:22 +00:00
o . Printer = metricsutil . NewTopCmdPrinter ( out )
return nil
}
func ( o * TopPodOptions ) Validate ( ) error {
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 TopPodOptions ) RunTopPod ( ) error {
2016-08-26 09:44:14 +00:00
var err error
selector := labels . Everything ( )
if len ( o . Selector ) > 0 {
selector , err = labels . Parse ( o . Selector )
if err != nil {
return err
}
}
2017-11-15 19:41:04 +00:00
apiGroups , err := o . DiscoveryClient . ServerGroups ( )
if err != nil {
return err
}
metricsAPIAvailable := SupportedMetricsAPIVersionAvailable ( apiGroups )
metrics := & metricsapi . PodMetricsList { }
if metricsAPIAvailable {
metrics , err = getMetricsFromMetricsAPI ( o . MetricsClient , o . Namespace , o . ResourceName , o . AllNamespaces , selector )
if err != nil {
return err
}
} else {
metrics , err = o . Client . GetPodMetrics ( o . Namespace , o . ResourceName , o . AllNamespaces , selector )
if err != nil {
return err
}
}
2016-08-26 09:44:14 +00:00
// TODO: Refactor this once Heapster becomes the API server.
// First we check why no metrics have been received.
2017-11-15 19:41:04 +00:00
if len ( metrics . Items ) == 0 {
2016-08-26 09:44:14 +00:00
// 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.
e := verifyEmptyMetrics ( o , selector )
if e != nil {
return e
}
}
2016-08-11 15:24:22 +00:00
if err != nil {
return err
}
2017-11-15 19:41:04 +00:00
return o . Printer . PrintPodMetrics ( metrics . Items , o . PrintContainers , o . AllNamespaces )
}
func getMetricsFromMetricsAPI ( metricsClient metricsclientset . Interface , namespace , resourceName string , allNamespaces bool , selector labels . Selector ) ( * metricsapi . PodMetricsList , error ) {
var err error
ns := metav1 . NamespaceAll
if ! allNamespaces {
ns = namespace
}
versionedMetrics := & metricsv1beta1api . PodMetricsList { }
if resourceName != "" {
m , err := metricsClient . Metrics ( ) . PodMetricses ( ns ) . Get ( resourceName , metav1 . GetOptions { } )
if err != nil {
return nil , err
}
versionedMetrics . Items = [ ] metricsv1beta1api . PodMetrics { * m }
} else {
versionedMetrics , err = metricsClient . Metrics ( ) . PodMetricses ( ns ) . List ( metav1 . ListOptions { LabelSelector : selector . 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
2016-08-11 15:24:22 +00:00
}
2016-08-26 09:44:14 +00:00
func verifyEmptyMetrics ( o TopPodOptions , selector labels . Selector ) error {
if len ( o . ResourceName ) > 0 {
2016-12-07 13:26:33 +00:00
pod , err := o . PodClient . Pods ( o . Namespace ) . Get ( o . ResourceName , metav1 . GetOptions { } )
2016-08-26 09:44:14 +00:00
if err != nil {
return err
}
if err := checkPodAge ( pod ) ; err != nil {
return err
}
} else {
2017-01-22 03:36:02 +00:00
pods , err := o . PodClient . Pods ( o . Namespace ) . List ( metav1 . ListOptions {
LabelSelector : selector . String ( ) ,
2016-08-26 09:44:14 +00:00
} )
if err != nil {
return err
}
if len ( pods . Items ) == 0 {
return nil
}
for _ , pod := range pods . Items {
if err := checkPodAge ( & pod ) ; err != nil {
return err
}
}
}
return errors . New ( "metrics not available yet" )
}
2017-10-31 08:15:32 +00:00
func checkPodAge ( pod * v1 . Pod ) error {
2016-08-26 09:44:14 +00:00
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 ( ) )
glog . Warningf ( message )
return errors . New ( message )
} else {
glog . V ( 2 ) . Infof ( "Metrics not yet available for pod %s/%s, age: %s" , pod . Namespace , pod . Name , age . String ( ) )
return nil
}
}