...

Source file src/k8s.io/kubernetes/pkg/controller/podautoscaler/metrics/client.go

Documentation: k8s.io/kubernetes/pkg/controller/podautoscaler/metrics

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package metrics
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"k8s.io/klog/v2"
    25  
    26  	autoscaling "k8s.io/api/autoscaling/v2"
    27  	v1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	customapi "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2"
    32  	metricsapi "k8s.io/metrics/pkg/apis/metrics/v1beta1"
    33  	resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
    34  	customclient "k8s.io/metrics/pkg/client/custom_metrics"
    35  	externalclient "k8s.io/metrics/pkg/client/external_metrics"
    36  )
    37  
    38  const (
    39  	metricServerDefaultMetricWindow = time.Minute
    40  )
    41  
    42  func NewRESTMetricsClient(resourceClient resourceclient.PodMetricsesGetter, customClient customclient.CustomMetricsClient, externalClient externalclient.ExternalMetricsClient) MetricsClient {
    43  	return &restMetricsClient{
    44  		&resourceMetricsClient{resourceClient},
    45  		&customMetricsClient{customClient},
    46  		&externalMetricsClient{externalClient},
    47  	}
    48  }
    49  
    50  // restMetricsClient is a client which supports fetching
    51  // metrics from the resource metrics API, the
    52  // custom metrics API and the external metrics API.
    53  type restMetricsClient struct {
    54  	*resourceMetricsClient
    55  	*customMetricsClient
    56  	*externalMetricsClient
    57  }
    58  
    59  // resourceMetricsClient implements the resource-metrics-related parts of MetricsClient,
    60  // using data from the resource metrics API.
    61  type resourceMetricsClient struct {
    62  	client resourceclient.PodMetricsesGetter
    63  }
    64  
    65  // GetResourceMetric gets the given resource metric (and an associated oldest timestamp)
    66  // for all pods matching the specified selector in the given namespace
    67  func (c *resourceMetricsClient) GetResourceMetric(ctx context.Context, resource v1.ResourceName, namespace string, selector labels.Selector, container string) (PodMetricsInfo, time.Time, error) {
    68  	metrics, err := c.client.PodMetricses(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector.String()})
    69  	if err != nil {
    70  		return nil, time.Time{}, fmt.Errorf("unable to fetch metrics from resource metrics API: %v", err)
    71  	}
    72  
    73  	if len(metrics.Items) == 0 {
    74  		return nil, time.Time{}, fmt.Errorf("no metrics returned from resource metrics API")
    75  	}
    76  	var res PodMetricsInfo
    77  	if container != "" {
    78  		res, err = getContainerMetrics(metrics.Items, resource, container)
    79  		if err != nil {
    80  			return nil, time.Time{}, fmt.Errorf("failed to get container metrics: %v", err)
    81  		}
    82  	} else {
    83  		res = getPodMetrics(ctx, metrics.Items, resource)
    84  	}
    85  	timestamp := metrics.Items[0].Timestamp.Time
    86  	return res, timestamp, nil
    87  }
    88  
    89  func getContainerMetrics(rawMetrics []metricsapi.PodMetrics, resource v1.ResourceName, container string) (PodMetricsInfo, error) {
    90  	res := make(PodMetricsInfo, len(rawMetrics))
    91  	for _, m := range rawMetrics {
    92  		containerFound := false
    93  		for _, c := range m.Containers {
    94  			if c.Name == container {
    95  				containerFound = true
    96  				if val, resFound := c.Usage[resource]; resFound {
    97  					res[m.Name] = PodMetric{
    98  						Timestamp: m.Timestamp.Time,
    99  						Window:    m.Window.Duration,
   100  						Value:     val.MilliValue(),
   101  					}
   102  				}
   103  				break
   104  			}
   105  		}
   106  		if !containerFound {
   107  			return nil, fmt.Errorf("container %s not present in metrics for pod %s/%s", container, m.Namespace, m.Name)
   108  		}
   109  	}
   110  	return res, nil
   111  }
   112  
   113  func getPodMetrics(ctx context.Context, rawMetrics []metricsapi.PodMetrics, resource v1.ResourceName) PodMetricsInfo {
   114  	res := make(PodMetricsInfo, len(rawMetrics))
   115  	for _, m := range rawMetrics {
   116  		podSum := int64(0)
   117  		missing := len(m.Containers) == 0
   118  		for _, c := range m.Containers {
   119  			resValue, found := c.Usage[resource]
   120  			if !found {
   121  				missing = true
   122  				klog.FromContext(ctx).V(2).Info("Missing resource metric", "resourceMetric", resource, "pod", klog.KRef(m.Namespace, m.Name))
   123  				break
   124  			}
   125  			podSum += resValue.MilliValue()
   126  		}
   127  		if !missing {
   128  			res[m.Name] = PodMetric{
   129  				Timestamp: m.Timestamp.Time,
   130  				Window:    m.Window.Duration,
   131  				Value:     podSum,
   132  			}
   133  		}
   134  	}
   135  	return res
   136  }
   137  
   138  // customMetricsClient implements the custom-metrics-related parts of MetricsClient,
   139  // using data from the custom metrics API.
   140  type customMetricsClient struct {
   141  	client customclient.CustomMetricsClient
   142  }
   143  
   144  // GetRawMetric gets the given metric (and an associated oldest timestamp)
   145  // for all pods matching the specified selector in the given namespace
   146  func (c *customMetricsClient) GetRawMetric(metricName string, namespace string, selector labels.Selector, metricSelector labels.Selector) (PodMetricsInfo, time.Time, error) {
   147  	metrics, err := c.client.NamespacedMetrics(namespace).GetForObjects(schema.GroupKind{Kind: "Pod"}, selector, metricName, metricSelector)
   148  	if err != nil {
   149  		return nil, time.Time{}, fmt.Errorf("unable to fetch metrics from custom metrics API: %v", err)
   150  	}
   151  
   152  	if len(metrics.Items) == 0 {
   153  		return nil, time.Time{}, fmt.Errorf("no metrics returned from custom metrics API")
   154  	}
   155  
   156  	res := make(PodMetricsInfo, len(metrics.Items))
   157  	for _, m := range metrics.Items {
   158  		window := metricServerDefaultMetricWindow
   159  		if m.WindowSeconds != nil {
   160  			window = time.Duration(*m.WindowSeconds) * time.Second
   161  		}
   162  		res[m.DescribedObject.Name] = PodMetric{
   163  			Timestamp: m.Timestamp.Time,
   164  			Window:    window,
   165  			Value:     int64(m.Value.MilliValue()),
   166  		}
   167  
   168  		m.Value.MilliValue()
   169  	}
   170  
   171  	timestamp := metrics.Items[0].Timestamp.Time
   172  
   173  	return res, timestamp, nil
   174  }
   175  
   176  // GetObjectMetric gets the given metric (and an associated timestamp) for the given
   177  // object in the given namespace
   178  func (c *customMetricsClient) GetObjectMetric(metricName string, namespace string, objectRef *autoscaling.CrossVersionObjectReference, metricSelector labels.Selector) (int64, time.Time, error) {
   179  	gvk := schema.FromAPIVersionAndKind(objectRef.APIVersion, objectRef.Kind)
   180  	var metricValue *customapi.MetricValue
   181  	var err error
   182  	if gvk.Kind == "Namespace" && gvk.Group == "" {
   183  		// handle namespace separately
   184  		// NB: we ignore namespace name here, since CrossVersionObjectReference isn't
   185  		// supposed to allow you to escape your namespace
   186  		metricValue, err = c.client.RootScopedMetrics().GetForObject(gvk.GroupKind(), namespace, metricName, metricSelector)
   187  	} else {
   188  		metricValue, err = c.client.NamespacedMetrics(namespace).GetForObject(gvk.GroupKind(), objectRef.Name, metricName, metricSelector)
   189  	}
   190  
   191  	if err != nil {
   192  		return 0, time.Time{}, fmt.Errorf("unable to fetch metrics from custom metrics API: %v", err)
   193  	}
   194  
   195  	return metricValue.Value.MilliValue(), metricValue.Timestamp.Time, nil
   196  }
   197  
   198  // externalMetricsClient implements the external metrics related parts of MetricsClient,
   199  // using data from the external metrics API.
   200  type externalMetricsClient struct {
   201  	client externalclient.ExternalMetricsClient
   202  }
   203  
   204  // GetExternalMetric gets all the values of a given external metric
   205  // that match the specified selector.
   206  func (c *externalMetricsClient) GetExternalMetric(metricName, namespace string, selector labels.Selector) ([]int64, time.Time, error) {
   207  	metrics, err := c.client.NamespacedMetrics(namespace).List(metricName, selector)
   208  	if err != nil {
   209  		return []int64{}, time.Time{}, fmt.Errorf("unable to fetch metrics from external metrics API: %v", err)
   210  	}
   211  
   212  	if len(metrics.Items) == 0 {
   213  		return nil, time.Time{}, fmt.Errorf("no metrics returned from external metrics API")
   214  	}
   215  
   216  	res := make([]int64, 0)
   217  	for _, m := range metrics.Items {
   218  		res = append(res, m.Value.MilliValue())
   219  	}
   220  	timestamp := metrics.Items[0].Timestamp.Time
   221  	return res, timestamp, nil
   222  }
   223  

View as plain text