122 lines
4.3 KiB
Python
122 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def read(relative: str) -> str:
|
|
return (ROOT / relative).read_text(encoding="utf-8")
|
|
|
|
|
|
def service_block(compose: str, name: str) -> str:
|
|
match = re.search(
|
|
rf"^ {re.escape(name)}:\n(?P<body>(?:^(?: |\s*$).*\n?)*)",
|
|
compose,
|
|
re.MULTILINE,
|
|
)
|
|
assert match, f"missing Compose service {name}"
|
|
return match.group("body")
|
|
|
|
|
|
def test_collector_has_only_approved_prometheus_targets() -> None:
|
|
config = read("observability/otel-collector.yaml")
|
|
|
|
targets = re.findall(r'targets: \["([^"]+)"\]', config)
|
|
assert targets == [
|
|
"127.0.0.1:8888",
|
|
"redis-exporter:9121",
|
|
"nginx-exporter:9113",
|
|
]
|
|
assert "service_name: otel-collector" in config
|
|
assert "service_name: redis" in config
|
|
assert "service_name: nginx" in config
|
|
assert "hostmetrics:" in config
|
|
assert "service.namespace, value: han-chat" in config
|
|
assert "filter/noise:" in config
|
|
assert "tail_sampling:" in config
|
|
|
|
|
|
def test_logs_are_direct_otlp_without_filelog() -> None:
|
|
config = read("observability/otel-collector.yaml")
|
|
logs = config.split(" logs:\n", 1)[1]
|
|
|
|
assert "receivers: [otlp]" in logs
|
|
assert "otlp/remote" in logs
|
|
assert "filelog" not in config
|
|
|
|
|
|
def test_apps_receive_release_and_logging_context() -> None:
|
|
compose = read("docker-compose.yml")
|
|
|
|
assert compose.count("RELEASE_VERSION: ${RELEASE_VERSION:?set RELEASE_VERSION}") >= 3
|
|
assert compose.count("LOG_LEVEL: ${LOG_LEVEL:-INFO}") == 2
|
|
|
|
|
|
def test_collector_exposes_health_and_self_metrics_and_validates_config() -> None:
|
|
compose = read("docker-compose.yml")
|
|
collector = service_block(compose, "otel-collector")
|
|
|
|
assert 'expose: ["4317", "4318", "13133", "8888"]' in collector
|
|
assert '"/otelcol-contrib", "validate"' in collector
|
|
|
|
|
|
def test_exporters_are_internal_only_and_digest_pinned() -> None:
|
|
compose = read("docker-compose.yml")
|
|
|
|
for name, port in (("redis-exporter", "9121"), ("nginx-exporter", "9113")):
|
|
block = service_block(compose, name)
|
|
assert f'expose: ["{port}"]' in block
|
|
assert "\n ports:" not in block
|
|
assert "observability" in block
|
|
assert "${REDIS_EXPORTER_IMAGE:?set immutable Redis exporter image digest}" in compose
|
|
assert "${NGINX_EXPORTER_IMAGE:?set immutable nginx exporter image digest}" in compose
|
|
|
|
|
|
def test_nginx_status_is_internal_and_query_free() -> None:
|
|
template = read("nginx/templates/10-vm2.conf.template")
|
|
|
|
status = template.split("location = /stub_status", 1)[1].split("}", 1)[0]
|
|
assert "stub_status;" in status
|
|
assert "access_log off;" in status
|
|
assert "listen 8081;" in template
|
|
assert ":8081:8081" not in read("docker-compose.yml")
|
|
|
|
|
|
def test_redis_exporter_uses_restricted_acl_and_secret_file() -> None:
|
|
acl = read("redis/redis-safety.acl.template")
|
|
compose = read("docker-compose.yml")
|
|
secret_config = json.loads(read("deployment/secrets/config.example.json"))
|
|
|
|
exporter_line = next(line for line in acl.splitlines() if line.startswith("user exporter "))
|
|
assert "-@all +ping +info" in exporter_line
|
|
assert "~*" not in exporter_line
|
|
assert "REDIS_PASSWORD_FILE: /run/secrets/redis_exporter_password" in compose
|
|
assert secret_config["secrets"]["REDIS_EXPORTER_PASSWORD"]["consumers"] == [
|
|
"redis-exporter"
|
|
]
|
|
|
|
|
|
def test_self_hosted_signoz_does_not_require_fake_auth() -> None:
|
|
collector = read("observability/otel-collector.yaml")
|
|
env_example = read(".env.example")
|
|
|
|
assert "OTEL_REMOTE_AUTH" not in collector
|
|
assert "OTEL_REMOTE_AUTH" not in env_example
|
|
assert "headers:" not in collector
|
|
|
|
|
|
def test_preflight_covers_observability_images_and_acl_pair() -> None:
|
|
preflight = read("deployment/preflight.sh")
|
|
|
|
assert "REDIS_EXPORTER_IMAGE NGINX_EXPORTER_IMAGE" in preflight
|
|
assert "REDIS_EXPORTER_PASSWORD" in preflight
|
|
assert 'target = "redis://redis-safety:6379"' in preflight
|
|
assert "Redis exporter password map does not match REDIS_SAFETY_ACL" in preflight
|
|
assert "Collector must enforce service.namespace=han-chat" in preflight
|
|
assert "Collector direct OTLP logs pipeline is missing" in preflight
|
|
assert "Collector filelog receiver is forbidden" in preflight
|