diff --git a/test_runner/fixtures/neon_fixtures.py b/test_runner/fixtures/neon_fixtures.py index a232bf8b6d..c24158e9ec 100644 --- a/test_runner/fixtures/neon_fixtures.py +++ b/test_runner/fixtures/neon_fixtures.py @@ -17,6 +17,7 @@ import uuid from collections import defaultdict from contextlib import closing, contextmanager from dataclasses import dataclass, field +from datetime import datetime from enum import Flag, auto from functools import cached_property from itertools import chain, product @@ -48,6 +49,7 @@ from fixtures.types import Lsn, TenantId, TimelineId from fixtures.utils import ( ATTACHMENT_NAME_REGEX, Fn, + allure_add_grafana_links, allure_attach_from_dir, get_self_dir, subprocess_capture, @@ -2436,10 +2438,16 @@ def remote_pg( connstr = os.getenv("BENCHMARK_CONNSTR") if connstr is None: raise ValueError("no connstr provided, use BENCHMARK_CONNSTR environment variable") - + start_ms = int(datetime.utcnow().timestamp() * 1000) with RemotePostgres(pg_bin, connstr) as remote_pg: yield remote_pg + end_ms = int(datetime.utcnow().timestamp() * 1000) + host = parse_dsn(connstr).get("host", "") + if host.endswith(".neon.build"): + # Add 10s margin to the start and end times + allure_add_grafana_links(host, start_ms - 10_000, end_ms + 10_000) + class PSQL: """ diff --git a/test_runner/fixtures/utils.py b/test_runner/fixtures/utils.py index 1e15fea3c2..b58539ca86 100644 --- a/test_runner/fixtures/utils.py +++ b/test_runner/fixtures/utils.py @@ -1,4 +1,5 @@ import contextlib +import json import os import re import subprocess @@ -6,6 +7,7 @@ import tarfile import time from pathlib import Path from typing import Any, Callable, Dict, List, Tuple, TypeVar +from urllib.parse import urlencode import allure from psycopg2.extensions import cursor @@ -184,6 +186,46 @@ def allure_attach_from_dir(dir: Path): allure.attach.file(source, name, attachment_type, extension) +DATASOURCE_ID = "xHHYY0dVz" + + +def allure_add_grafana_links(host: str, start_ms: int, end_ms: int): + """Add links to server logs in Grafana to Allure report""" + # We expect host to be in format like ep-divine-night-159320.us-east-2.aws.neon.build + endpoint_id, region_id, _ = host.split(".", 2) + + expressions = { + "compute logs": f'{{app="compute-node-{endpoint_id}", neon_region="{region_id}"}}', + "k8s events": f'{{job="integrations/kubernetes/eventhandler"}} |~ "name=compute-node-{endpoint_id}-"', + "console logs": f'{{neon_service="console", neon_region="{region_id}"}} | json | endpoint_id = "{endpoint_id}"', + "proxy logs": f'{{neon_service="proxy-scram", neon_region="{region_id}"}}', + } + + params: Dict[str, Any] = { + "datasource": DATASOURCE_ID, + "queries": [ + { + "expr": "", + "refId": "A", + "datasource": {"type": "loki", "uid": DATASOURCE_ID}, + "editorMode": "code", + "queryType": "range", + } + ], + "range": { + "from": str(start_ms), + "to": str(end_ms), + }, + } + for name, expr in expressions.items(): + params["queries"][0]["expr"] = expr + query_string = urlencode({"orgId": 1, "left": json.dumps(params)}) + link = f"https://neonprod.grafana.net/explore?{query_string}" + + allure.dynamic.link(link, name=name) + log.info(f"{name}: {link}") + + def start_in_background( command: list[str], cwd: Path, log_file_name: str, is_started: Fn ) -> subprocess.Popen[bytes]: