Add query test script and test mode
* Add a mode to test all metric queries * Add a script to run query tests against different versions of PostgeSQL * Add Docker elements for query testing * Switch to using a --config flag when specifying the config file * Fix some metric queries * Allow the agent address to be configured * Allow the sslmode connection parameter to be configured
This commit is contained in:
22
tests/Dockerfile
Normal file
22
tests/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk update && \
|
||||
apk add py3-psycopg2 \
|
||||
py3-yaml \
|
||||
tini
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY src/pgmon.py /app/
|
||||
|
||||
COPY sample-config/pgmon-metrics.yml /app/
|
||||
|
||||
COPY tests/test-config.yml /app/
|
||||
|
||||
COPY --chmod=0600 --chown=postgres:postgres tests/pgpass /root/.pgpass
|
||||
|
||||
ENTRYPOINT ["tini", "--"]
|
||||
|
||||
EXPOSE 5400
|
||||
|
||||
CMD ["/app/pgmon.py", "-c", "/app/test-config.yml", "--test"]
|
||||
32
tests/docker-compose.yml
Normal file
32
tests/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
|
||||
services:
|
||||
agent:
|
||||
image: pgmon
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: tests/Dockerfile
|
||||
ports:
|
||||
- :5400
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
image: "postgres:${PGTAG:-17-bookworm}"
|
||||
ports:
|
||||
- :5432
|
||||
environment:
|
||||
POSTGRES_PASSWORD: secret
|
||||
healthcheck:
|
||||
#test: [ "CMD", "pg_isready", "-U", "postgres" ]
|
||||
test: [ "CMD-SHELL", "pg_controldata /var/lib/postgresql/data/ | grep -q 'in production'" ]
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
command: >
|
||||
postgres -c ssl=on
|
||||
-c ssl_cert_file='/etc/ssl/certs/ssl-cert-snakeoil.pem'
|
||||
-c ssl_key_file='/etc/ssl/private/ssl-cert-snakeoil.key'
|
||||
-c listen_addresses='*'
|
||||
|
||||
1
tests/pgpass
Normal file
1
tests/pgpass
Normal file
@@ -0,0 +1 @@
|
||||
db:5432:*:postgres:secret
|
||||
62
tests/run-tests.sh
Executable file
62
tests/run-tests.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Versions to test
|
||||
versions=( $@ )
|
||||
|
||||
# If we weren't given any versions, test them all
|
||||
if [ ${#versions[@]} -eq 0 ]
|
||||
then
|
||||
versions=( 9.2 9.6 10 11 12 13 14 15 16 17 )
|
||||
fi
|
||||
|
||||
# Image tags to use
|
||||
declare -A images=()
|
||||
images["9.2"]='9.2'
|
||||
images["9.6"]='9.6-bullseye'
|
||||
images["10"]='10-bullseye'
|
||||
images["11"]='11-bookworm'
|
||||
images["12"]='12-bookworm'
|
||||
images["13"]='13-bookworm'
|
||||
images["14"]='14-bookworm'
|
||||
images["15"]='15-bookworm'
|
||||
images["16"]='16-bookworm'
|
||||
images["17"]='17-bookworm'
|
||||
|
||||
declare -A results=()
|
||||
|
||||
# Make sure everything's down to start with
|
||||
docker compose down
|
||||
|
||||
# Make sure our agent container is up to date
|
||||
docker compose build agent
|
||||
|
||||
for version in "${versions[@]}"
|
||||
do
|
||||
echo
|
||||
echo "Testing: PostgreSQL ${version}"
|
||||
|
||||
# Specify the version we're testing against
|
||||
export PGTAG="${images["$version"]}"
|
||||
|
||||
# Start the containers
|
||||
docker compose up --exit-code-from=agent agent
|
||||
rc=$?
|
||||
|
||||
results["$version"]=$rc
|
||||
|
||||
# Destroy the containers
|
||||
docker compose down
|
||||
done
|
||||
|
||||
echo
|
||||
echo
|
||||
for v in "${versions[@]}"
|
||||
do
|
||||
case "${results["$v"]}" in
|
||||
0) msg="OK" ;;
|
||||
1) msg="Query failure detected" ;;
|
||||
18) msg="Docker image error: 18" ;;
|
||||
*) msg="Unexpected error: ${results["$v"]}" ;;
|
||||
esac
|
||||
echo "$v -> $msg"
|
||||
done
|
||||
@@ -1,94 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from testcontainers import PostgresContainer
|
||||
import requests
|
||||
import yaml
|
||||
import sys
|
||||
|
||||
pg_versions = [9.2, 9.6, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
|
||||
pgmon_port = 93849
|
||||
|
||||
tests = {}
|
||||
|
||||
container = None
|
||||
|
||||
|
||||
def std_version(version):
|
||||
if version[0] == "9":
|
||||
return int(f"{version[0]}0{version[1]}00")
|
||||
else:
|
||||
return int(f"{version}0000")
|
||||
|
||||
|
||||
def run_test(metric, params, status, check):
|
||||
"""
|
||||
Validate the return code and restults of a query
|
||||
|
||||
params:
|
||||
metric: The name of the metric to test
|
||||
params: A dictionary of query parameters to use when testing
|
||||
status: The expected status code
|
||||
check: A regular expression to validate the results (or None)
|
||||
"""
|
||||
result = requests.get(f"http://localhost:{pgmon_port}/{metric}", params=params)
|
||||
|
||||
if result.status_code != status:
|
||||
print(
|
||||
f"FAIL: {metric}[{params}] returned wrong status code: {result.status_code}"
|
||||
)
|
||||
return False
|
||||
|
||||
if re.match(check, result.text):
|
||||
print(f"SUCCESS: {metric}[{params}]")
|
||||
return True
|
||||
else:
|
||||
print(f"FAIL: {metric}[{params}] result is invalid, got:\n {result.text}")
|
||||
return False
|
||||
|
||||
|
||||
def run_all_tests(version):
|
||||
"""
|
||||
Run all defined tests against the current running instance
|
||||
|
||||
params:
|
||||
version: The PostgreSQL version currently being tested (server_version_num format)
|
||||
"""
|
||||
errors = 0
|
||||
|
||||
# Convert versions like 12 to 120000
|
||||
version_num = std_version(version)
|
||||
|
||||
# Loop through all of the metrics to test.
|
||||
for metric in tests.keys():
|
||||
params = metric.get("params", {})
|
||||
status = 200
|
||||
check = ""
|
||||
|
||||
# Find the correct version of the status and check parameters (assuming there are any).
|
||||
# If there are any check conditions, find the highest version that does not exceed the version we're currently testing against.
|
||||
# To do this, we order the keys (versions) in reverse, so we start with the highest.
|
||||
for v in reversed(sorted(metric.get("expect", {}).keys())):
|
||||
# If we've reached a version <= the one we're testing use it.
|
||||
if int(v) <= version_num:
|
||||
status = metric["expect"][v]["status"]
|
||||
check = metric["expect"][v]["check"]
|
||||
break
|
||||
|
||||
if not run_test(metric, metrics[metric].get(params, {}), status, check):
|
||||
errors += 1
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def start_test_db(version):
|
||||
# container = PostgresContainer()
|
||||
pass
|
||||
|
||||
|
||||
# Read the test script
|
||||
try:
|
||||
with open("metric_tests.yml", "r") as f:
|
||||
tests = yaml.safe_load(f)
|
||||
except yaml.parser.ParserError as e:
|
||||
sys.exit("Failed to parse metrics_test.yml: {e}")
|
||||
16
tests/test-config.yml
Normal file
16
tests/test-config.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
|
||||
# Bind to all interfaces so we can submit requests from outside the test container
|
||||
address: 0.0.0.0
|
||||
|
||||
# We always just connect to the db container
|
||||
dbhost: db
|
||||
dbport: 5432
|
||||
dbuser: postgres
|
||||
|
||||
# Allow some insecure SSL parameters for the 9.2 test
|
||||
ssl_ciphers: DEFAULT@SECLEVEL=1
|
||||
|
||||
# Pull in the standard metrics
|
||||
include:
|
||||
- pgmon-metrics.yml
|
||||
Reference in New Issue
Block a user