diff --git a/src/servers/src/http/prometheus.rs b/src/servers/src/http/prometheus.rs index fa7f67f6fff4..51d97813c1d4 100644 --- a/src/servers/src/http/prometheus.rs +++ b/src/servers/src/http/prometheus.rs @@ -719,22 +719,63 @@ pub async fn label_values_query( } let queries = params.matches.0; - if queries.is_empty() { - return PrometheusJsonResponse::error( - StatusCode::InvalidArguments, - "match[] parameter is required", - ); - } - let start = params.start.unwrap_or_else(yesterday_rfc3339); let end = params.end.unwrap_or_else(current_time_rfc3339); let lookback = params .lookback .unwrap_or_else(|| DEFAULT_LOOKBACK_STRING.to_string()); - let mut label_values = HashSet::new(); + // If no match[] is specified, collect label values from all tables. + if queries.is_empty() { + let mut label_values = HashSet::new(); + let mut merge_map = HashMap::new(); + let table_names = match handler + .catalog_manager() + .table_names(&catalog, &schema, Some(&query_ctx)) + .await + { + Ok(tables) => tables, + Err(e) => return PrometheusJsonResponse::error(e.status_code(), e.output_msg()), + }; + for table_name in table_names { + let prom_query = PromQuery { + query: table_name, + start: start.clone(), + end: end.clone(), + step: DEFAULT_LOOKBACK_STRING.to_string(), + lookback: lookback.clone(), + }; + let result = handler.do_query(&prom_query, query_ctx.clone()).await; + if let Err(err) = + retrieve_label_values(result, &label_name, &mut label_values, &mut merge_map).await + { + // skip non-existent tables, columns, or time index errors + if err.status_code() != StatusCode::TableNotFound + && err.status_code() != StatusCode::TableColumnNotFound + && err.status_code() != StatusCode::InvalidArguments + { + return PrometheusJsonResponse::error(err.status_code(), err.output_msg()); + } + } + } + + let merge_map = merge_map + .into_iter() + .map(|(k, v)| (k, Value::from(v))) + .collect(); + let mut label_values: Vec<_> = label_values.into_iter().collect(); + label_values.sort_unstable(); + + let mut resp = + PrometheusJsonResponse::success(PrometheusResponse::LabelValues(label_values)); + resp.resp_metrics = merge_map; + return resp; + } + + let mut label_values = HashSet::new(); let mut merge_map = HashMap::new(); + for query in queries { let prom_query = PromQuery { query, diff --git a/tests-integration/tests/http.rs b/tests-integration/tests/http.rs index 1e1de235a88b..10d83f2773f3 100644 --- a/tests-integration/tests/http.rs +++ b/tests-integration/tests/http.rs @@ -573,20 +573,18 @@ pub async fn test_prom_http_api(store_type: StorageType) { .is_some_and(|err| err.eq_ignore_ascii_case("Table not found: greptime.public.up"))); // label values - // should return error if there is no match[] + // fetch all values for label `instance` let res = client .get("/v1/prometheus/api/v1/label/instance/values") .send() .await; - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - let prom_resp = res.json::().await; - assert_eq!(prom_resp.status, "error"); - assert!(prom_resp - .error - .is_some_and(|err| err.eq_ignore_ascii_case("match[] parameter is required"))); - assert!(prom_resp - .error_type - .is_some_and(|err| err.eq_ignore_ascii_case("InvalidArguments"))); + assert_eq!(res.status(), StatusCode::OK); + let body = serde_json::from_str::(&res.text().await).unwrap(); + assert_eq!(body.status, "success"); + assert_eq!( + body.data, + serde_json::from_value::(json!(["host1", "host2"])).unwrap() + ); // single match[] let res = client