rest_metrics_client.go 6.6 KB

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