rest_metrics_client.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. Copyright 2017 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 metrics
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. "k8s.io/klog"
  19. autoscaling "k8s.io/api/autoscaling/v2beta2"
  20. "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/labels"
  23. "k8s.io/apimachinery/pkg/runtime/schema"
  24. customapi "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2"
  25. resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
  26. customclient "k8s.io/metrics/pkg/client/custom_metrics"
  27. externalclient "k8s.io/metrics/pkg/client/external_metrics"
  28. )
  29. const (
  30. metricServerDefaultMetricWindow = time.Minute
  31. )
  32. func NewRESTMetricsClient(resourceClient resourceclient.PodMetricsesGetter, customClient customclient.CustomMetricsClient, externalClient externalclient.ExternalMetricsClient) MetricsClient {
  33. return &restMetricsClient{
  34. &resourceMetricsClient{resourceClient},
  35. &customMetricsClient{customClient},
  36. &externalMetricsClient{externalClient},
  37. }
  38. }
  39. // restMetricsClient is a client which supports fetching
  40. // metrics from both the resource metrics API and the
  41. // custom metrics API.
  42. type restMetricsClient struct {
  43. *resourceMetricsClient
  44. *customMetricsClient
  45. *externalMetricsClient
  46. }
  47. // resourceMetricsClient implements the resource-metrics-related parts of MetricsClient,
  48. // using data from the resource metrics API.
  49. type resourceMetricsClient struct {
  50. client resourceclient.PodMetricsesGetter
  51. }
  52. // GetResourceMetric gets the given resource metric (and an associated oldest timestamp)
  53. // for all pods matching the specified selector in the given namespace
  54. func (c *resourceMetricsClient) GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector) (PodMetricsInfo, time.Time, error) {
  55. metrics, err := c.client.PodMetricses(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()})
  56. if err != nil {
  57. return nil, time.Time{}, fmt.Errorf("unable to fetch metrics from resource metrics API: %v", err)
  58. }
  59. if len(metrics.Items) == 0 {
  60. return nil, time.Time{}, fmt.Errorf("no metrics returned from resource metrics API")
  61. }
  62. res := make(PodMetricsInfo, len(metrics.Items))
  63. for _, m := range metrics.Items {
  64. podSum := int64(0)
  65. missing := len(m.Containers) == 0
  66. for _, c := range m.Containers {
  67. resValue, found := c.Usage[v1.ResourceName(resource)]
  68. if !found {
  69. missing = true
  70. klog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name)
  71. break // containers loop
  72. }
  73. podSum += resValue.MilliValue()
  74. }
  75. if !missing {
  76. res[m.Name] = PodMetric{
  77. Timestamp: m.Timestamp.Time,
  78. Window: m.Window.Duration,
  79. Value: int64(podSum),
  80. }
  81. }
  82. }
  83. timestamp := metrics.Items[0].Timestamp.Time
  84. return res, timestamp, nil
  85. }
  86. // customMetricsClient implements the custom-metrics-related parts of MetricsClient,
  87. // using data from the custom metrics API.
  88. type customMetricsClient struct {
  89. client customclient.CustomMetricsClient
  90. }
  91. // GetRawMetric gets the given metric (and an associated oldest timestamp)
  92. // for all pods matching the specified selector in the given namespace
  93. func (c *customMetricsClient) GetRawMetric(metricName string, namespace string, selector labels.Selector, metricSelector labels.Selector) (PodMetricsInfo, time.Time, error) {
  94. metrics, err := c.client.NamespacedMetrics(namespace).GetForObjects(schema.GroupKind{Kind: "Pod"}, selector, metricName, metricSelector)
  95. if err != nil {
  96. return nil, time.Time{}, fmt.Errorf("unable to fetch metrics from custom metrics API: %v", err)
  97. }
  98. if len(metrics.Items) == 0 {
  99. return nil, time.Time{}, fmt.Errorf("no metrics returned from custom metrics API")
  100. }
  101. res := make(PodMetricsInfo, len(metrics.Items))
  102. for _, m := range metrics.Items {
  103. window := metricServerDefaultMetricWindow
  104. if m.WindowSeconds != nil {
  105. window = time.Duration(*m.WindowSeconds) * time.Second
  106. }
  107. res[m.DescribedObject.Name] = PodMetric{
  108. Timestamp: m.Timestamp.Time,
  109. Window: window,
  110. Value: int64(m.Value.MilliValue()),
  111. }
  112. m.Value.MilliValue()
  113. }
  114. timestamp := metrics.Items[0].Timestamp.Time
  115. return res, timestamp, nil
  116. }
  117. // GetObjectMetric gets the given metric (and an associated timestamp) for the given
  118. // object in the given namespace
  119. func (c *customMetricsClient) GetObjectMetric(metricName string, namespace string, objectRef *autoscaling.CrossVersionObjectReference, metricSelector labels.Selector) (int64, time.Time, error) {
  120. gvk := schema.FromAPIVersionAndKind(objectRef.APIVersion, objectRef.Kind)
  121. var metricValue *customapi.MetricValue
  122. var err error
  123. if gvk.Kind == "Namespace" && gvk.Group == "" {
  124. // handle namespace separately
  125. // NB: we ignore namespace name here, since CrossVersionObjectReference isn't
  126. // supposed to allow you to escape your namespace
  127. metricValue, err = c.client.RootScopedMetrics().GetForObject(gvk.GroupKind(), namespace, metricName, metricSelector)
  128. } else {
  129. metricValue, err = c.client.NamespacedMetrics(namespace).GetForObject(gvk.GroupKind(), objectRef.Name, metricName, metricSelector)
  130. }
  131. if err != nil {
  132. return 0, time.Time{}, fmt.Errorf("unable to fetch metrics from custom metrics API: %v", err)
  133. }
  134. return metricValue.Value.MilliValue(), metricValue.Timestamp.Time, nil
  135. }
  136. // externalMetricsClient implenets the external metrics related parts of MetricsClient,
  137. // using data from the external metrics API.
  138. type externalMetricsClient struct {
  139. client externalclient.ExternalMetricsClient
  140. }
  141. // GetExternalMetric gets all the values of a given external metric
  142. // that match the specified selector.
  143. func (c *externalMetricsClient) GetExternalMetric(metricName, namespace string, selector labels.Selector) ([]int64, time.Time, error) {
  144. metrics, err := c.client.NamespacedMetrics(namespace).List(metricName, selector)
  145. if err != nil {
  146. return []int64{}, time.Time{}, fmt.Errorf("unable to fetch metrics from external metrics API: %v", err)
  147. }
  148. if len(metrics.Items) == 0 {
  149. return nil, time.Time{}, fmt.Errorf("no metrics returned from external metrics API")
  150. }
  151. res := make([]int64, 0)
  152. for _, m := range metrics.Items {
  153. res = append(res, m.Value.MilliValue())
  154. }
  155. timestamp := metrics.Items[0].Timestamp.Time
  156. return res, timestamp, nil
  157. }