metrics.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. package prometheus
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. v1 "k8s.io/api/core/v1"
  8. "k8s.io/client-go/kubernetes"
  9. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  10. )
  11. // returns the prometheus service name
  12. func GetPrometheusService(clientset kubernetes.Interface) (*v1.Service, bool, error) {
  13. services, err := clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
  14. LabelSelector: "app=prometheus,component=server,heritage=Helm",
  15. })
  16. if err != nil {
  17. return nil, false, err
  18. }
  19. if len(services.Items) == 0 {
  20. return nil, false, nil
  21. }
  22. return &services.Items[0], true, nil
  23. }
  24. // returns the prometheus service name
  25. func getKubeStateMetricsService(clientset kubernetes.Interface) (*v1.Service, bool, error) {
  26. services, err := clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
  27. LabelSelector: "app.kubernetes.io/name=kube-state-metrics",
  28. })
  29. if err != nil {
  30. return nil, false, err
  31. }
  32. if len(services.Items) == 0 {
  33. return nil, false, nil
  34. }
  35. return &services.Items[0], true, nil
  36. }
  37. type SimpleIngress struct {
  38. Name string `json:"name"`
  39. Namespace string `json:"namespace"`
  40. }
  41. // GetIngressesWithNGINXAnnotation gets an array of names for all ingresses controlled by
  42. // NGINX
  43. func GetIngressesWithNGINXAnnotation(clientset kubernetes.Interface) ([]SimpleIngress, error) {
  44. ingressList, err := clientset.NetworkingV1beta1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
  45. if err != nil {
  46. return nil, err
  47. }
  48. res := make([]SimpleIngress, 0)
  49. for _, ingress := range ingressList.Items {
  50. if ingressAnn, found := ingress.ObjectMeta.Annotations["kubernetes.io/ingress.class"]; found {
  51. if ingressAnn == "nginx" {
  52. res = append(res, SimpleIngress{
  53. Name: ingress.ObjectMeta.Name,
  54. Namespace: ingress.ObjectMeta.Namespace,
  55. })
  56. }
  57. }
  58. }
  59. return res, nil
  60. }
  61. type QueryOpts struct {
  62. Metric string `schema:"metric"`
  63. ShouldSum bool `schema:"shouldsum"`
  64. Kind string `schema:"kind"`
  65. PodList []string `schema:"pods"`
  66. Name string `schema:"name"`
  67. Namespace string `schema:"namespace"`
  68. StartRange uint `schema:"startrange"`
  69. EndRange uint `schema:"endrange"`
  70. Resolution string `schema:"resolution"`
  71. Percentile float64 `schema:"percentile"`
  72. }
  73. func QueryPrometheus(
  74. clientset kubernetes.Interface,
  75. service *v1.Service,
  76. opts *QueryOpts,
  77. ) ([]*promParsedSingletonQuery, error) {
  78. if len(service.Spec.Ports) == 0 {
  79. return nil, fmt.Errorf("prometheus service has no exposed ports to query")
  80. }
  81. selectionRegex, err := getSelectionRegex(opts.Kind, opts.Name)
  82. if err != nil {
  83. return nil, err
  84. }
  85. var podSelector string
  86. if len(opts.PodList) > 0 {
  87. podSelector = fmt.Sprintf(`namespace="%s",pod=~"%s",container!="POD",container!=""`, opts.Namespace, strings.Join(opts.PodList, "|"))
  88. } else {
  89. podSelector = fmt.Sprintf(`namespace="%s",pod=~"%s",container!="POD",container!=""`, opts.Namespace, selectionRegex)
  90. }
  91. query := ""
  92. if opts.Metric == "cpu" {
  93. query = fmt.Sprintf("rate(container_cpu_usage_seconds_total{%s}[5m])", podSelector)
  94. } else if opts.Metric == "memory" {
  95. query = fmt.Sprintf("container_memory_usage_bytes{%s}", podSelector)
  96. } else if opts.Metric == "network" {
  97. netPodSelector := fmt.Sprintf(`namespace="%s",pod=~"%s",container="POD"`, opts.Namespace, selectionRegex)
  98. query = fmt.Sprintf("rate(container_network_receive_bytes_total{%s}[5m])", netPodSelector)
  99. } else if opts.Metric == "nginx:errors" {
  100. num := fmt.Sprintf(`sum(rate(nginx_ingress_controller_requests{status=~"5.*",namespace="%s",ingress=~"%s"}[5m]) OR on() vector(0))`, opts.Namespace, selectionRegex)
  101. denom := fmt.Sprintf(`sum(rate(nginx_ingress_controller_requests{namespace="%s",ingress=~"%s"}[5m]) > 0)`, opts.Namespace, selectionRegex)
  102. query = fmt.Sprintf(`%s / %s * 100 OR on() vector(0)`, num, denom)
  103. } else if opts.Metric == "nginx:latency" {
  104. num := fmt.Sprintf(`sum(rate(nginx_ingress_controller_request_duration_seconds_sum{namespace=~"%s",ingress=~"%s"}[5m]) OR on() vector(0))`, opts.Namespace, selectionRegex)
  105. denom := fmt.Sprintf(`sum(rate(nginx_ingress_controller_request_duration_seconds_count{namespace=~"%s",ingress=~"%s"}[5m]))`, opts.Namespace, selectionRegex)
  106. query = fmt.Sprintf(`%s / %s OR on() vector(0)`, num, denom)
  107. } else if opts.Metric == "nginx:latency-histogram" {
  108. query = fmt.Sprintf(`histogram_quantile(%f, sum(rate(nginx_ingress_controller_request_duration_seconds_bucket{status!="404",status!="500",namespace=~"%s",ingress=~"%s"}[5m])) by (le, ingress))`, opts.Percentile, opts.Namespace, selectionRegex)
  109. } else if opts.Metric == "cpu_hpa_threshold" {
  110. // get the name of the kube hpa metric
  111. metricName, hpaMetricName := getKubeHPAMetricName(clientset, service, opts, "spec_target_metric")
  112. cpuMetricName := getKubeCPUMetricName(clientset, service, opts)
  113. ksmSvc, found, _ := getKubeStateMetricsService(clientset)
  114. appLabel := ""
  115. if found {
  116. appLabel = ksmSvc.ObjectMeta.Labels["app.kubernetes.io/instance"]
  117. }
  118. query = createHPAAbsoluteCPUThresholdQuery(cpuMetricName, metricName, selectionRegex, opts.Name, opts.Namespace, appLabel, hpaMetricName)
  119. } else if opts.Metric == "memory_hpa_threshold" {
  120. metricName, hpaMetricName := getKubeHPAMetricName(clientset, service, opts, "spec_target_metric")
  121. memMetricName := getKubeMemoryMetricName(clientset, service, opts)
  122. ksmSvc, found, _ := getKubeStateMetricsService(clientset)
  123. appLabel := ""
  124. if found {
  125. appLabel = ksmSvc.ObjectMeta.Labels["app.kubernetes.io/instance"]
  126. }
  127. query = createHPAAbsoluteMemoryThresholdQuery(memMetricName, metricName, selectionRegex, opts.Name, opts.Namespace, appLabel, hpaMetricName)
  128. } else if opts.Metric == "hpa_replicas" {
  129. metricName, hpaMetricName := getKubeHPAMetricName(clientset, service, opts, "status_current_replicas")
  130. ksmSvc, found, _ := getKubeStateMetricsService(clientset)
  131. appLabel := ""
  132. if found {
  133. appLabel = ksmSvc.ObjectMeta.Labels["app.kubernetes.io/instance"]
  134. }
  135. query = createHPACurrentReplicasQuery(metricName, opts.Name, opts.Namespace, appLabel, hpaMetricName)
  136. }
  137. if opts.ShouldSum {
  138. query = fmt.Sprintf("sum(%s)", query)
  139. }
  140. queryParams := map[string]string{
  141. "query": query,
  142. "start": fmt.Sprintf("%d", opts.StartRange),
  143. "end": fmt.Sprintf("%d", opts.EndRange),
  144. "step": opts.Resolution,
  145. }
  146. resp := clientset.CoreV1().Services(service.Namespace).ProxyGet(
  147. "http",
  148. service.Name,
  149. fmt.Sprintf("%d", service.Spec.Ports[0].Port),
  150. "/api/v1/query_range",
  151. queryParams,
  152. )
  153. rawQuery, err := resp.DoRaw(context.TODO())
  154. if err != nil {
  155. return nil, err
  156. }
  157. return parseQuery(rawQuery, opts.Metric)
  158. }
  159. type promRawQuery struct {
  160. Data struct {
  161. Result []struct {
  162. Metric struct {
  163. Pod string `json:"pod,omitempty"`
  164. } `json:"metric,omitempty"`
  165. Values [][]interface{} `json:"values"`
  166. } `json:"result"`
  167. } `json:"data"`
  168. }
  169. type promParsedSingletonQueryResult struct {
  170. Date interface{} `json:"date,omitempty"`
  171. CPU interface{} `json:"cpu,omitempty"`
  172. Replicas interface{} `json:"replicas,omitempty"`
  173. Memory interface{} `json:"memory,omitempty"`
  174. Bytes interface{} `json:"bytes,omitempty"`
  175. ErrorPct interface{} `json:"error_pct,omitempty"`
  176. Latency interface{} `json:"latency,omitempty"`
  177. }
  178. type promParsedSingletonQuery struct {
  179. Pod string `json:"pod,omitempty"`
  180. Results []promParsedSingletonQueryResult `json:"results"`
  181. }
  182. func parseQuery(rawQuery []byte, metric string) ([]*promParsedSingletonQuery, error) {
  183. rawQueryObj := &promRawQuery{}
  184. err := json.Unmarshal(rawQuery, rawQueryObj)
  185. if err != nil {
  186. return nil, err
  187. }
  188. res := make([]*promParsedSingletonQuery, 0)
  189. for _, result := range rawQueryObj.Data.Result {
  190. singleton := &promParsedSingletonQuery{
  191. Pod: result.Metric.Pod,
  192. }
  193. singletonResults := make([]promParsedSingletonQueryResult, 0)
  194. for _, values := range result.Values {
  195. singletonResult := &promParsedSingletonQueryResult{
  196. Date: values[0],
  197. }
  198. if metric == "cpu" {
  199. singletonResult.CPU = values[1]
  200. } else if metric == "memory" {
  201. singletonResult.Memory = values[1]
  202. } else if metric == "network" {
  203. singletonResult.Bytes = values[1]
  204. } else if metric == "nginx:errors" {
  205. singletonResult.ErrorPct = values[1]
  206. } else if metric == "cpu_hpa_threshold" {
  207. singletonResult.CPU = values[1]
  208. } else if metric == "memory_hpa_threshold" {
  209. singletonResult.Memory = values[1]
  210. } else if metric == "hpa_replicas" {
  211. singletonResult.Replicas = values[1]
  212. } else if metric == "nginx:latency" || metric == "nginx:latency-histogram" {
  213. singletonResult.Latency = values[1]
  214. }
  215. singletonResults = append(singletonResults, *singletonResult)
  216. }
  217. singleton.Results = singletonResults
  218. res = append(res, singleton)
  219. }
  220. return res, nil
  221. }
  222. func getSelectionRegex(kind, name string) (string, error) {
  223. var suffix string
  224. switch strings.ToLower(kind) {
  225. case "deployment":
  226. suffix = "[a-z0-9]+-[a-z0-9]+"
  227. case "statefulset":
  228. suffix = "[0-9]+"
  229. case "job":
  230. suffix = "[a-z0-9]+"
  231. case "cronjob":
  232. suffix = "[a-z0-9]+-[a-z0-9]+"
  233. case "ingress":
  234. return name, nil
  235. default:
  236. return "", fmt.Errorf("not a supported controller to query for metrics")
  237. }
  238. return fmt.Sprintf("%s-%s", name, suffix), nil
  239. }
  240. func createHPAAbsoluteCPUThresholdQuery(cpuMetricName, metricName, podSelectionRegex, hpaName, namespace, appLabel, hpaMetricName string) string {
  241. kubeMetricsPodSelectorOne := getKubeMetricsPodSelector(podSelectionRegex, namespace, "namespace")
  242. kubeMetricsPodSelectorTwo := getKubeMetricsPodSelector(podSelectionRegex, namespace, "exported_namespace")
  243. kubeMetricsHPASelectorOne := fmt.Sprintf(
  244. `%s="%s",namespace="%s",metric_name="cpu",metric_target_type="utilization"`,
  245. hpaMetricName,
  246. hpaName,
  247. namespace,
  248. )
  249. kubeMetricsHPASelectorTwo := fmt.Sprintf(
  250. `%s="%s",exported_namespace="%s",metric_name="cpu",metric_target_type="utilization"`,
  251. hpaMetricName,
  252. hpaName,
  253. namespace,
  254. )
  255. if cpuMetricName == "kube_pod_container_resource_requests" {
  256. kubeMetricsPodSelectorOne += `,resource="cpu",unit="core"`
  257. kubeMetricsPodSelectorTwo += `,resource="cpu",unit="core"`
  258. }
  259. // the kube-state-metrics queries are less prone to error if the field app_kubernetes_io_instance is matched
  260. // as well
  261. if appLabel != "" {
  262. kubeMetricsPodSelectorOne += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  263. kubeMetricsPodSelectorTwo += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  264. kubeMetricsHPASelectorOne += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  265. kubeMetricsHPASelectorTwo += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  266. }
  267. requestCPUOne := fmt.Sprintf(
  268. `sum by (%s) (label_replace(%s{%s},"%s", "%s", "", ""))`,
  269. hpaMetricName,
  270. cpuMetricName,
  271. kubeMetricsPodSelectorOne,
  272. hpaMetricName,
  273. hpaName,
  274. )
  275. targetCPUUtilThresholdOne := fmt.Sprintf(
  276. `%s{%s} / 100`,
  277. metricName,
  278. kubeMetricsHPASelectorOne,
  279. )
  280. requestCPUTwo := fmt.Sprintf(
  281. `sum by (%s) (label_replace(%s{%s},"%s", "%s", "", ""))`,
  282. hpaMetricName,
  283. cpuMetricName,
  284. kubeMetricsPodSelectorTwo,
  285. hpaMetricName,
  286. hpaName,
  287. )
  288. targetCPUUtilThresholdTwo := fmt.Sprintf(
  289. `%s{%s} / 100`,
  290. metricName,
  291. kubeMetricsHPASelectorTwo,
  292. )
  293. return fmt.Sprintf(
  294. `(%s * on(%s) %s) or (%s * on(%s) %s)`,
  295. requestCPUOne, hpaMetricName, targetCPUUtilThresholdOne,
  296. requestCPUTwo, hpaMetricName, targetCPUUtilThresholdTwo,
  297. )
  298. }
  299. func createHPAAbsoluteMemoryThresholdQuery(memMetricName, metricName, podSelectionRegex, hpaName, namespace, appLabel, hpaMetricName string) string {
  300. kubeMetricsPodSelectorOne := getKubeMetricsPodSelector(podSelectionRegex, namespace, "namespace")
  301. kubeMetricsPodSelectorTwo := getKubeMetricsPodSelector(podSelectionRegex, namespace, "exported_namespace")
  302. kubeMetricsHPASelectorOne := fmt.Sprintf(
  303. `%s="%s",namespace="%s",metric_name="memory",metric_target_type="utilization"`,
  304. hpaMetricName,
  305. hpaName,
  306. namespace,
  307. )
  308. kubeMetricsHPASelectorTwo := fmt.Sprintf(
  309. `%s="%s",exported_namespace="%s",metric_name="memory",metric_target_type="utilization"`,
  310. hpaMetricName,
  311. hpaName,
  312. namespace,
  313. )
  314. if memMetricName == "kube_pod_container_resource_requests" {
  315. kubeMetricsPodSelectorOne += `,resource="memory",unit="byte"`
  316. kubeMetricsPodSelectorTwo += `,resource="memory",unit="byte"`
  317. }
  318. // the kube-state-metrics queries are less prone to error if the field app_kubernetes_io_instance is matched
  319. // as well
  320. if appLabel != "" {
  321. kubeMetricsPodSelectorOne += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  322. kubeMetricsPodSelectorTwo += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  323. kubeMetricsHPASelectorOne += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  324. kubeMetricsHPASelectorTwo += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  325. }
  326. requestMemOne := fmt.Sprintf(
  327. `sum by (%s) (label_replace(%s{%s},"%s", "%s", "", ""))`,
  328. hpaMetricName,
  329. memMetricName,
  330. kubeMetricsPodSelectorOne,
  331. hpaMetricName,
  332. hpaName,
  333. )
  334. targetMemUtilThresholdOne := fmt.Sprintf(
  335. `%s{%s} / 100`,
  336. metricName,
  337. kubeMetricsHPASelectorOne,
  338. )
  339. requestMemTwo := fmt.Sprintf(
  340. `sum by (%s) (label_replace(%s{%s},"%s", "%s", "", ""))`,
  341. hpaMetricName,
  342. memMetricName,
  343. kubeMetricsPodSelectorTwo,
  344. hpaMetricName,
  345. hpaName,
  346. )
  347. targetMemUtilThresholdTwo := fmt.Sprintf(
  348. `%s{%s} / 100`,
  349. metricName,
  350. kubeMetricsHPASelectorTwo,
  351. )
  352. return fmt.Sprintf(
  353. `(%s * on(%s) %s) or (%s * on(%s) %s)`,
  354. requestMemOne, hpaMetricName, targetMemUtilThresholdOne,
  355. requestMemTwo, hpaMetricName, targetMemUtilThresholdTwo,
  356. )
  357. }
  358. func getKubeMetricsPodSelector(podSelectionRegex, namespace, namespaceLabel string) string {
  359. return fmt.Sprintf(
  360. `pod=~"%s",%s="%s",container!="POD",container!=""`,
  361. podSelectionRegex,
  362. namespaceLabel,
  363. namespace,
  364. )
  365. }
  366. func createHPACurrentReplicasQuery(metricName, hpaName, namespace, appLabel, hpaMetricName string) string {
  367. kubeMetricsHPASelectorOne := fmt.Sprintf(
  368. `%s="%s",namespace="%s"`,
  369. hpaMetricName,
  370. hpaName,
  371. namespace,
  372. )
  373. kubeMetricsHPASelectorTwo := fmt.Sprintf(
  374. `%s="%s",exported_namespace="%s"`,
  375. hpaMetricName,
  376. hpaName,
  377. namespace,
  378. )
  379. // the kube-state-metrics queries are less prone to error if the field app_kubernetes_io_instance is matched
  380. // as well
  381. if appLabel != "" {
  382. kubeMetricsHPASelectorOne += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  383. kubeMetricsHPASelectorTwo += fmt.Sprintf(`,app_kubernetes_io_instance="%s"`, appLabel)
  384. }
  385. return fmt.Sprintf(
  386. `(%s{%s}) or (%s{%s})`,
  387. metricName,
  388. kubeMetricsHPASelectorOne,
  389. metricName,
  390. kubeMetricsHPASelectorTwo,
  391. )
  392. }
  393. type promRawValuesQuery struct {
  394. Status string `json:"status"`
  395. Data []string `json:"data"`
  396. }
  397. // getKubeHPAMetricName performs a "best guess" for the name of the kube HPA metric,
  398. // which was renamed to kube_horizontalpodautoscaler... in later versions of kube-state-metrics.
  399. // we query Prometheus for a list of metric names to see if any match the new query
  400. // value, otherwise we return the deprecated name.
  401. func getKubeHPAMetricName(
  402. clientset kubernetes.Interface,
  403. service *v1.Service,
  404. opts *QueryOpts,
  405. suffix string,
  406. ) (string, string) {
  407. queryParams := map[string]string{
  408. "match[]": fmt.Sprintf("kube_horizontalpodautoscaler_%s", suffix),
  409. "start": fmt.Sprintf("%d", opts.StartRange),
  410. "end": fmt.Sprintf("%d", opts.EndRange),
  411. }
  412. resp := clientset.CoreV1().Services(service.Namespace).ProxyGet(
  413. "http",
  414. service.Name,
  415. fmt.Sprintf("%d", service.Spec.Ports[0].Port),
  416. "/api/v1/label/__name__/values",
  417. queryParams,
  418. )
  419. rawQuery, err := resp.DoRaw(context.TODO())
  420. if err != nil {
  421. return fmt.Sprintf("kube_hpa_%s", suffix), "hpa"
  422. }
  423. rawQueryObj := &promRawValuesQuery{}
  424. json.Unmarshal(rawQuery, rawQueryObj)
  425. if rawQueryObj.Status == "success" && len(rawQueryObj.Data) == 1 {
  426. return fmt.Sprintf("kube_horizontalpodautoscaler_%s", suffix), "horizontalpodautoscaler"
  427. }
  428. return fmt.Sprintf("kube_hpa_%s", suffix), "hpa"
  429. }
  430. func getKubeCPUMetricName(
  431. clientset kubernetes.Interface,
  432. service *v1.Service,
  433. opts *QueryOpts,
  434. ) string {
  435. queryParams := map[string]string{
  436. "match[]": "kube_pod_container_resource_requests",
  437. "start": fmt.Sprintf("%d", opts.StartRange),
  438. "end": fmt.Sprintf("%d", opts.EndRange),
  439. }
  440. resp := clientset.CoreV1().Services(service.Namespace).ProxyGet(
  441. "http",
  442. service.Name,
  443. fmt.Sprintf("%d", service.Spec.Ports[0].Port),
  444. "/api/v1/label/__name__/values",
  445. queryParams,
  446. )
  447. rawQuery, err := resp.DoRaw(context.TODO())
  448. if err != nil {
  449. return "kube_pod_container_resource_requests_cpu_cores"
  450. }
  451. rawQueryObj := &promRawValuesQuery{}
  452. json.Unmarshal(rawQuery, rawQueryObj)
  453. if rawQueryObj.Status == "success" && len(rawQueryObj.Data) == 1 {
  454. return "kube_pod_container_resource_requests"
  455. }
  456. return "kube_pod_container_resource_requests_cpu_cores"
  457. }
  458. func getKubeMemoryMetricName(
  459. clientset kubernetes.Interface,
  460. service *v1.Service,
  461. opts *QueryOpts,
  462. ) string {
  463. queryParams := map[string]string{
  464. "match[]": "kube_pod_container_resource_requests",
  465. "start": fmt.Sprintf("%d", opts.StartRange),
  466. "end": fmt.Sprintf("%d", opts.EndRange),
  467. }
  468. resp := clientset.CoreV1().Services(service.Namespace).ProxyGet(
  469. "http",
  470. service.Name,
  471. fmt.Sprintf("%d", service.Spec.Ports[0].Port),
  472. "/api/v1/label/__name__/values",
  473. queryParams,
  474. )
  475. rawQuery, err := resp.DoRaw(context.TODO())
  476. if err != nil {
  477. return "kube_pod_container_resource_requests_memory_bytes"
  478. }
  479. rawQueryObj := &promRawValuesQuery{}
  480. json.Unmarshal(rawQuery, rawQueryObj)
  481. if rawQueryObj.Status == "success" && len(rawQueryObj.Data) == 1 {
  482. return "kube_pod_container_resource_requests"
  483. }
  484. return "kube_pod_container_resource_requests_memory_bytes"
  485. }