mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 22:17:14 +00:00
Merge pull request #102155 from lauchokyip/addTop
Added field-selector option for kubectl top pod
This commit is contained in:
commit
7d9f476337
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
@ -44,7 +45,8 @@ import (
|
|||||||
type TopPodOptions struct {
|
type TopPodOptions struct {
|
||||||
ResourceName string
|
ResourceName string
|
||||||
Namespace string
|
Namespace string
|
||||||
Selector string
|
LabelSelector string
|
||||||
|
FieldSelector string
|
||||||
SortBy string
|
SortBy string
|
||||||
AllNamespaces bool
|
AllNamespaces bool
|
||||||
PrintContainers bool
|
PrintContainers bool
|
||||||
@ -106,7 +108,8 @@ func NewCmdTopPod(f cmdutil.Factory, o *TopPodOptions, streams genericclioptions
|
|||||||
},
|
},
|
||||||
Aliases: []string{"pods", "po"},
|
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().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().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.")
|
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")
|
return errors.New("--sort-by accepts only cpu or memory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(o.ResourceName) > 0 && len(o.Selector) > 0 {
|
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 errors.New("only one of NAME or selector can be provided")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o TopPodOptions) RunTopPod() error {
|
func (o TopPodOptions) RunTopPod() error {
|
||||||
var err error
|
var err error
|
||||||
selector := labels.Everything()
|
labelSelector := labels.Everything()
|
||||||
if len(o.Selector) > 0 {
|
if len(o.LabelSelector) > 0 {
|
||||||
selector, err = labels.Parse(o.Selector)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -183,7 +193,7 @@ func (o TopPodOptions) RunTopPod() error {
|
|||||||
if !metricsAPIAvailable {
|
if !metricsAPIAvailable {
|
||||||
return errors.New("Metrics API not available")
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -192,7 +202,7 @@ func (o TopPodOptions) RunTopPod() error {
|
|||||||
if len(metrics.Items) == 0 {
|
if len(metrics.Items) == 0 {
|
||||||
// If the API server query is successful but all the pods are newly created,
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
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)
|
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
|
var err error
|
||||||
ns := metav1.NamespaceAll
|
ns := metav1.NamespaceAll
|
||||||
if !allNamespaces {
|
if !allNamespaces {
|
||||||
@ -222,7 +232,7 @@ func getMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespac
|
|||||||
}
|
}
|
||||||
versionedMetrics.Items = []metricsv1beta1api.PodMetrics{*m}
|
versionedMetrics.Items = []metricsv1beta1api.PodMetrics{*m}
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -235,7 +245,7 @@ func getMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespac
|
|||||||
return metrics, nil
|
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 {
|
if len(o.ResourceName) > 0 {
|
||||||
pod, err := o.PodClient.Pods(o.Namespace).Get(context.TODO(), o.ResourceName, metav1.GetOptions{})
|
pod, err := o.PodClient.Pods(o.Namespace).Get(context.TODO(), o.ResourceName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -246,7 +256,8 @@ func verifyEmptyMetrics(o TopPodOptions, selector labels.Selector) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pods, err := o.PodClient.Pods(o.Namespace).List(context.TODO(), metav1.ListOptions{
|
pods, err := o.PodClient.Pods(o.Namespace).List(context.TODO(), metav1.ListOptions{
|
||||||
LabelSelector: selector.String(),
|
LabelSelector: labelSelector.String(),
|
||||||
|
FieldSelector: fieldSelector.String(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -107,10 +107,16 @@ func TestTopPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pod with label selector",
|
name: "pod with label selector",
|
||||||
options: &TopPodOptions{Selector: "key=value"},
|
options: &TopPodOptions{LabelSelector: "key=value"},
|
||||||
expectedQuery: "labelSelector=" + url.QueryEscape("key=value"),
|
expectedQuery: "labelSelector=" + url.QueryEscape("key=value"),
|
||||||
namespaces: []string{testNS, testNS},
|
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",
|
name: "pod with container metrics",
|
||||||
options: &TopPodOptions{PrintContainers: true},
|
options: &TopPodOptions{PrintContainers: true},
|
||||||
|
Loading…
Reference in New Issue
Block a user