Added field-selector option for kubectl top pod
Kubernetes-commit: 1331c76aa3d7e5db730dc008b2ee94173ca51293
This commit is contained in:
parent
44fa123c77
commit
03baccf059
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"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/client-go/discovery"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
@ -44,7 +45,8 @@ import (
|
|||
type TopPodOptions struct {
|
||||
ResourceName string
|
||||
Namespace string
|
||||
Selector string
|
||||
LabelSelector string
|
||||
FieldSelector string
|
||||
SortBy string
|
||||
AllNamespaces bool
|
||||
PrintContainers bool
|
||||
|
@ -106,7 +108,8 @@ func NewCmdTopPod(f cmdutil.Factory, o *TopPodOptions, streams genericclioptions
|
|||
},
|
||||
Aliases: []string{"pods", "po"},
|
||||
}
|
||||
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
cmd.Flags().StringVarP(&o.LabelSelector, "selector", "l", o.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
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.")
|
||||
|
@ -157,17 +160,24 @@ func (o *TopPodOptions) Validate() error {
|
|||
return errors.New("--sort-by accepts only cpu or memory")
|
||||
}
|
||||
}
|
||||
if len(o.ResourceName) > 0 && len(o.Selector) > 0 {
|
||||
return errors.New("only one of NAME or --selector can be provided")
|
||||
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
|
||||
selector := labels.Everything()
|
||||
if len(o.Selector) > 0 {
|
||||
selector, err = labels.Parse(o.Selector)
|
||||
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
|
||||
}
|
||||
|
@ -183,7 +193,7 @@ func (o TopPodOptions) RunTopPod() error {
|
|||
if !metricsAPIAvailable {
|
||||
return errors.New("Metrics API not available")
|
||||
}
|
||||
metrics, err := getMetricsFromMetricsAPI(o.MetricsClient, o.Namespace, o.ResourceName, o.AllNamespaces, selector)
|
||||
metrics, err := getMetricsFromMetricsAPI(o.MetricsClient, o.Namespace, o.ResourceName, o.AllNamespaces, labelSelector, fieldSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -192,7 +202,7 @@ func (o TopPodOptions) RunTopPod() error {
|
|||
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, selector)
|
||||
err := verifyEmptyMetrics(o, labelSelector, fieldSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -208,7 +218,7 @@ func (o TopPodOptions) RunTopPod() error {
|
|||
return o.Printer.PrintPodMetrics(metrics.Items, o.PrintContainers, o.AllNamespaces, o.NoHeaders, o.SortBy)
|
||||
}
|
||||
|
||||
func getMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespace, resourceName string, allNamespaces bool, selector labels.Selector) (*metricsapi.PodMetricsList, error) {
|
||||
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 {
|
||||
|
@ -222,7 +232,7 @@ func getMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespac
|
|||
}
|
||||
versionedMetrics.Items = []metricsv1beta1api.PodMetrics{*m}
|
||||
} else {
|
||||
versionedMetrics, err = metricsClient.MetricsV1beta1().PodMetricses(ns).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()})
|
||||
versionedMetrics, err = metricsClient.MetricsV1beta1().PodMetricses(ns).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String(), FieldSelector: fieldSelector.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -235,7 +245,7 @@ func getMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespac
|
|||
return metrics, nil
|
||||
}
|
||||
|
||||
func verifyEmptyMetrics(o TopPodOptions, selector labels.Selector) error {
|
||||
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 {
|
||||
|
@ -246,7 +256,8 @@ func verifyEmptyMetrics(o TopPodOptions, selector labels.Selector) error {
|
|||
}
|
||||
} else {
|
||||
pods, err := o.PodClient.Pods(o.Namespace).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: selector.String(),
|
||||
LabelSelector: labelSelector.String(),
|
||||
FieldSelector: fieldSelector.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -107,10 +107,16 @@ func TestTopPod(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "pod with label selector",
|
||||
options: &TopPodOptions{Selector: "key=value"},
|
||||
options: &TopPodOptions{LabelSelector: "key=value"},
|
||||
expectedQuery: "labelSelector=" + url.QueryEscape("key=value"),
|
||||
namespaces: []string{testNS, testNS},
|
||||
},
|
||||
{
|
||||
name: "pod with field selector",
|
||||
options: &TopPodOptions{FieldSelector: "key=value"},
|
||||
expectedQuery: "fieldSelector=" + url.QueryEscape("key=value"),
|
||||
namespaces: []string{testNS, testNS},
|
||||
},
|
||||
{
|
||||
name: "pod with container metrics",
|
||||
options: &TopPodOptions{PrintContainers: true},
|
||||
|
|
Loading…
Reference in New Issue