diff --git a/Cargo.lock b/Cargo.lock index 16fcd0c4c9..a93f7d6ef4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1133,7 +1133,9 @@ dependencies = [ "compute_api", "flate2", "futures", + "git-version", "hyper", + "metrics", "notify", "num_cpus", "opentelemetry", diff --git a/compute_tools/Cargo.toml b/compute_tools/Cargo.toml index 47378f1910..3869967162 100644 --- a/compute_tools/Cargo.toml +++ b/compute_tools/Cargo.toml @@ -12,8 +12,10 @@ cfg-if.workspace = true clap.workspace = true flate2.workspace = true futures.workspace = true +git-version.workspace = true hyper = { workspace = true, features = ["full"] } notify.workspace = true +metrics.workspace = true num_cpus.workspace = true opentelemetry.workspace = true postgres.workspace = true diff --git a/compute_tools/src/bin/compute_ctl.rs b/compute_tools/src/bin/compute_ctl.rs index 36e9ca0731..b48522b8f6 100644 --- a/compute_tools/src/bin/compute_ctl.rs +++ b/compute_tools/src/bin/compute_ctl.rs @@ -57,18 +57,27 @@ use compute_tools::logger::*; use compute_tools::monitor::launch_monitor; use compute_tools::params::*; use compute_tools::spec::*; +use metrics::set_build_info_metric; +use utils::{project_build_tag, project_git_version}; // this is an arbitrary build tag. Fine as a default / for testing purposes // in-case of not-set environment var const BUILD_TAG_DEFAULT: &str = "latest"; +project_git_version!(GIT_VERSION); +project_build_tag!(BUILD_TAG); + fn main() -> Result<()> { init_tracing_and_logging(DEFAULT_LOG_LEVEL)?; let build_tag = option_env!("BUILD_TAG") .unwrap_or(BUILD_TAG_DEFAULT) .to_string(); - info!("build_tag: {build_tag}"); + + info!("Version: {GIT_VERSION}"); + info!("Build_tag: {BUILD_TAG}"); + + set_build_info_metric(GIT_VERSION, BUILD_TAG); let matches = cli().get_matches(); let pgbin_default = String::from("postgres"); diff --git a/compute_tools/src/http/api.rs b/compute_tools/src/http/api.rs index ef6ca6eee3..6f49431970 100644 --- a/compute_tools/src/http/api.rs +++ b/compute_tools/src/http/api.rs @@ -11,7 +11,8 @@ use compute_api::responses::{ComputeStatus, ComputeStatusResponse, GenericAPIErr use anyhow::Result; use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Method, Request, Response, Server, StatusCode}; +use hyper::{header::CONTENT_TYPE, Body, Method, Request, Response, Server, StatusCode}; +use metrics::{Encoder, TextEncoder}; use num_cpus; use serde_json; use tokio::task; @@ -51,6 +52,20 @@ async fn routes(req: Request, compute: &Arc) -> Response { + let mut buffer = vec![]; + let metrics = metrics::gather(); + let encoder = TextEncoder::new(); + encoder.encode(&metrics, &mut buffer).unwrap(); + + Response::builder() + .status(StatusCode::OK) + .header(CONTENT_TYPE, encoder.format_type()) + .body(Body::from(buffer)) + .unwrap() + } + // Startup metrics in JSON format. Keep /metrics reserved for a possible // future use for Prometheus metrics format. (&Method::GET, "/metrics.json") => { diff --git a/test_runner/fixtures/neon_fixtures.py b/test_runner/fixtures/neon_fixtures.py index f060a63344..0e4954f48e 100644 --- a/test_runner/fixtures/neon_fixtures.py +++ b/test_runner/fixtures/neon_fixtures.py @@ -2640,6 +2640,11 @@ class Endpoint(PgProtocol): return self + def get_metrics_str(self) -> str: + request_result = requests.get(f"http://localhost:{self.http_port}/metrics") + request_result.raise_for_status() + return request_result.text + def __enter__(self) -> "Endpoint": return self diff --git a/test_runner/regress/test_build_info_metric.py b/test_runner/regress/test_build_info_metric.py index 8f714dae67..aaa789ed5e 100644 --- a/test_runner/regress/test_build_info_metric.py +++ b/test_runner/regress/test_build_info_metric.py @@ -6,11 +6,15 @@ def test_build_info_metric(neon_env_builder: NeonEnvBuilder, link_proxy: NeonPro neon_env_builder.num_safekeepers = 1 env = neon_env_builder.init_start() + env.neon_cli.create_branch("test_build_info_metric") + endpoint = env.endpoints.create_start("test_build_info_metric") + parsed_metrics = {} parsed_metrics["pageserver"] = parse_metrics(env.pageserver.http_client().get_metrics_str()) parsed_metrics["safekeeper"] = parse_metrics(env.safekeepers[0].http_client().get_metrics_str()) parsed_metrics["proxy"] = parse_metrics(link_proxy.get_metrics()) + parsed_metrics["compute_ctl"] = parse_metrics(endpoint.get_metrics_str()) for _component, metrics in parsed_metrics.items(): sample = metrics.query_one("libmetrics_build_info")