jetstream v2 in zig stream.waow.tech
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114#!/usr/bin/env python3"""Offline contract check between the pinned dashboard and a metrics scrape."""
from __future__ import annotations
import argparseimport hashlibimport jsonimport pathlibimport reimport sysimport urllib.request
ROOT = pathlib.Path(__file__).resolve().parents[1]UPSTREAM_DASHBOARD = ROOT / "deploy/grafana/jetstream-upstream.json"DEFAULT_DASHBOARD = ROOT / "deploy/grafana/stream.json"EXPECTED_SHA256 = { UPSTREAM_DASHBOARD.resolve(): "ef6033c182d8c3f9d88af7d91e61126724efbe72e6b940984c9de12e9530cd91", DEFAULT_DASHBOARD.resolve(): "d00582d478cdf0c8b00d348dd953c79651c4961e161592bf5eee9f389b3b51d9",}METRIC_RE = re.compile(r"\b(?:jetstream|go|process|stream)_[a-zA-Z_:][a-zA-Z0-9_:]*")TYPE_RE = re.compile(r"^# TYPE ([a-zA-Z_:][a-zA-Z0-9_:]*) (\w+)$")SAMPLE_RE = re.compile(r"^([a-zA-Z_:][a-zA-Z0-9_:]*)(?:\{|\s)")
def expressions(value: object): if isinstance(value, dict): expr = value.get("expr") if isinstance(expr, str): yield expr for child in value.values(): yield from expressions(child) elif isinstance(value, list): for child in value: yield from expressions(child)
def dashboard_families(raw: bytes) -> set[str]: dashboard = json.loads(raw) return {name.rstrip(":") for expr in expressions(dashboard) for name in METRIC_RE.findall(expr)}
def scrape_families(raw: str) -> set[str]: families: set[str] = set() types: dict[str, str] = {} for line in raw.splitlines(): type_match = TYPE_RE.match(line) if type_match: name, kind = type_match.groups() families.add(name) types[name] = kind continue if line.startswith("#"): continue sample_match = SAMPLE_RE.match(line) if sample_match: families.add(sample_match.group(1))
# PromQL addresses histogram/summary children even though exposition TYPE # names only the parent family. for name, kind in types.items(): if kind in {"histogram", "summary"}: families.update({name + "_count", name + "_sum"}) if kind == "histogram": families.add(name + "_bucket") return families
def read_metrics(source: str) -> str: if source == "-": return sys.stdin.read() if source.startswith(("http://", "https://")): with urllib.request.urlopen(source, timeout=10) as response: return response.read().decode() return pathlib.Path(source).read_text()
def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--dashboard", type=pathlib.Path, default=DEFAULT_DASHBOARD) parser.add_argument("--metrics", help="scrape file, URL, or - for stdin") parser.add_argument("--list", action="store_true", help="list dashboard families only") args = parser.parse_args()
raw = args.dashboard.read_bytes() digest = hashlib.sha256(raw).hexdigest() expected_digest = EXPECTED_SHA256.get(args.dashboard.resolve()) if expected_digest is not None and digest != expected_digest: print(f"dashboard checksum mismatch: expected {expected_digest}, got {digest}", file=sys.stderr) return 2
required = dashboard_families(raw) if args.list: print("\n".join(sorted(required))) return 0 if not args.metrics: parser.error("--metrics is required unless --list is used")
present = scrape_families(read_metrics(args.metrics)) missing = sorted(required - present) print( f"dashboard={args.dashboard} sha256={digest} " f"required={len(required)} present={len(required) - len(missing)} missing={len(missing)}" ) if missing: print("\n".join(missing)) return 1 return 0
if __name__ == "__main__": raise SystemExit(main())