From ffeabcd640180951d024b0728074df8f2f6f8ccd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 09:36:25 +0000 Subject: [PATCH 01/83] Add MicroVM helper to start Docker without systemd MicroVM terminals ship docker-ce but no running dockerd and no systemd. This script starts dockerd in tmux and fixes socket permissions so manual QA provisioning (pmm-framework.py) can run locally. Co-authored-by: davi.travaglia --- .../scripts/start-docker-microvm.sh | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 qa-integration/scripts/start-docker-microvm.sh diff --git a/qa-integration/scripts/start-docker-microvm.sh b/qa-integration/scripts/start-docker-microvm.sh new file mode 100755 index 000000000..3e807087f --- /dev/null +++ b/qa-integration/scripts/start-docker-microvm.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Start Docker on MicroVM environments where systemd is unavailable. +set -euo pipefail + +DOCKERD_SESSION="dockerd" +TMUX_CONF="/exec-daemon/tmux.portal.conf" +TMUX=(tmux -f "$TMUX_CONF") + +if ! command -v dockerd >/dev/null 2>&1; then + echo "ERROR: dockerd not installed. Install docker-ce first." >&2 + exit 1 +fi + +if ! "${TMUX[@]}" has-session -t "=$DOCKERD_SESSION" 2>/dev/null; then + echo "Starting dockerd in tmux session '$DOCKERD_SESSION'..." + "${TMUX[@]}" new-session -d -s "$DOCKERD_SESSION" -c /tmp -- "sudo dockerd" + for _ in $(seq 1 30); do + if [ -S /var/run/docker.sock ]; then + break + fi + sleep 1 + done +fi + +if [ ! -S /var/run/docker.sock ]; then + echo "ERROR: dockerd did not create /var/run/docker.sock within 30s." >&2 + echo "Check logs: ${TMUX[*]} attach -t $DOCKERD_SESSION" >&2 + exit 1 +fi + +# MicroVM shells often lack an active docker group; widen socket access for local QA. +if ! docker info >/dev/null 2>&1; then + sudo chmod 666 /var/run/docker.sock +fi + +if ! docker info >/dev/null 2>&1; then + echo "ERROR: cannot talk to Docker API after starting dockerd." >&2 + exit 1 +fi + +echo "Docker is ready ($(docker info -f '{{.ServerVersion}}'), storage: $(docker info -f '{{.Driver}}'))" From ad746393dec0916b64aaf5b5205c7f8001e840db Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 10:35:58 +0000 Subject: [PATCH 02/83] Add MicroVM provisioning scripts and no-systemd PSMDB path - provision-pmm.sh: scripted PMM Server setup (Jenkins staging parity) - wait-pmm-ready.sh: correct readyz check (HTTP 200, body {}) - cleanup-pmm-microvm.sh: tear down server + QA containers - PSMDB+PBM no-systemd entrypoint for MicroVM (PMM_QA_NO_SYSTEMD=1) Co-authored-by: davi.travaglia --- .cursor/skills/pmm-manual-test/SKILL.md | 37 ++++ .../pmm_psmdb-pbm_setup/configure-agents.sh | 24 ++- .../pmm_psmdb-pbm_setup/configure-replset.sh | 14 +- .../docker-compose-rs.microvm.yaml | 75 +++++++++ .../entrypoint-no-systemd.sh | 80 +++++++++ .../scripts/compose-env.sh | 9 + .../scripts/systemctl-shim.sh | 53 ++++++ .../start-rs-only-microvm.sh | 59 +++++++ qa-integration/pmm_qa/pmm-framework.py | 5 +- qa-integration/scripts/cleanup-pmm-microvm.sh | 48 ++++++ qa-integration/scripts/lib/wait-pmm-ready.sh | 27 +++ qa-integration/scripts/provision-pmm.sh | 158 ++++++++++++++++++ 12 files changed, 573 insertions(+), 16 deletions(-) create mode 100644 qa-integration/pmm_psmdb-pbm_setup/docker-compose-rs.microvm.yaml create mode 100755 qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh create mode 100644 qa-integration/pmm_psmdb-pbm_setup/scripts/compose-env.sh create mode 100755 qa-integration/pmm_psmdb-pbm_setup/scripts/systemctl-shim.sh create mode 100755 qa-integration/pmm_psmdb-pbm_setup/start-rs-only-microvm.sh create mode 100755 qa-integration/scripts/cleanup-pmm-microvm.sh create mode 100755 qa-integration/scripts/lib/wait-pmm-ready.sh create mode 100755 qa-integration/scripts/provision-pmm.sh diff --git a/.cursor/skills/pmm-manual-test/SKILL.md b/.cursor/skills/pmm-manual-test/SKILL.md index 9b47e8f0b..cdcd469ab 100644 --- a/.cursor/skills/pmm-manual-test/SKILL.md +++ b/.cursor/skills/pmm-manual-test/SKILL.md @@ -210,6 +210,43 @@ Leave `CLIENTS` empty for server-only tests (UI, API, settings, network-tab chec See [jenkins-jobs.md](jenkins-jobs.md) for all parameters. Groovy source: `$ReposRoot/jenkins-pipelines/pmm/v3/`. +### MicroVM local provisioning (instead of ad-hoc docker commands) + +On Cursor MicroVM, **do not freestyle** `docker run` / `curl` loops. Use the scripted path in **pmm-qa**: + +**1. Server only** (same as Jenkins `pmm3-aws-staging-start` server half): + +```bash +export DOCKER_VERSION=perconalab/pmm-server-fb:PR-XXXX- +export WATCHTOWER_VERSION=perconalab/pmm-watchtower-fb:PR-XXXX- # optional +export CLIENT_VERSION='https://s3.../pmm-client-PR-XXXX-.tar.gz' # for next step +export ADMIN_PASSWORD='pmm3admin!' + +/agent/repos/pmm-qa/qa-integration/scripts/provision-pmm.sh --cleanup --fresh-volume +# or: --skip-watchtower for pinned FB images +``` + +`readyz` success = **HTTP 200** and body **`{}`** at `https://127.0.0.1/v1/server/readyz` (not `grep ok`). + +**2. Databases/clients** (ticket-specific — run after server is up): + +```bash +cd qa-integration/pmm_qa && source virtenv/bin/activate +export PMM_QA_NO_SYSTEMD=1 +export ADMIN_PASSWORD='pmm3admin!' +export CLIENT_VERSION='...' + +python pmm-framework.py \ + --pmm-server-password "$ADMIN_PASSWORD" \ + --client-version "$CLIENT_VERSION" \ + --database \ + --verbose +``` + +Map `` from ticket scope + `scripts/database_options.py` (e.g. PMM-14576 backup → `psmdb,SETUP_TYPE=pss`). + +**Reset:** `qa-integration/scripts/cleanup-pmm-microvm.sh` or `provision-pmm.sh --cleanup --fresh-volume`. + ## Step 8: Determine required job parameters Derive **every** param that differs from defaults. The user should not need to edit the Jenkins form. diff --git a/qa-integration/pmm_psmdb-pbm_setup/configure-agents.sh b/qa-integration/pmm_psmdb-pbm_setup/configure-agents.sh index 63e826ed8..b752edbf2 100644 --- a/qa-integration/pmm_psmdb-pbm_setup/configure-agents.sh +++ b/qa-integration/pmm_psmdb-pbm_setup/configure-agents.sh @@ -1,6 +1,8 @@ #!/bin/bash set -e +source "$(dirname "$0")/scripts/compose-env.sh" + pmm_mongo_user=${PMM_MONGO_USER:-pmm} pmm_mongo_user_pass=${PMM_MONGO_USER_PASS:-pmmpass} pbm_user=${PBM_USER:-pbm} @@ -31,16 +33,16 @@ nodes="rs101 rs102 rs103" for node in $nodes do echo "congiguring pbm agent on $node" - docker compose -f docker-compose-rs.yaml exec -T $node bash -c "echo \"PBM_MONGODB_URI=mongodb://${pbm_user}:${pbm_pass}@127.0.0.1:27017\" > /etc/sysconfig/pbm-agent" + compose_rs exec -T $node bash -c "echo \"PBM_MONGODB_URI=mongodb://${pbm_user}:${pbm_pass}@127.0.0.1:27017\" > /etc/sysconfig/pbm-agent" echo "restarting pbm agent on $node" - docker compose -f docker-compose-rs.yaml exec -T $node systemctl restart pbm-agent + compose_rs exec -T $node systemctl restart pbm-agent done -docker compose -f docker-compose-rs.yaml exec -T rs101 pbm config --file /etc/pbm/minio.yaml +compose_rs exec -T rs101 pbm config --file /etc/pbm/minio.yaml if [[ $mongo_setup_type == "psa" ]]; then echo "stop pbm agent for arbiter node" - docker compose -f docker-compose-rs.yaml exec -T rs103 systemctl stop pbm-agent + compose_rs exec -T rs103 systemctl stop pbm-agent fi echo echo "configuring pmm agents" @@ -53,18 +55,22 @@ nodes="rs101 rs102 rs103" for node in $nodes do echo "configuring pmm agent on $node" - docker compose -f docker-compose-rs.yaml exec -T -e PMM_AGENT_SETUP_NODE_NAME=${node}._${random_number} $node pmm-agent setup ${DEBUG_FLAG} + if [[ "${PMM_QA_NO_SYSTEMD:-}" == "1" ]]; then + compose_rs exec -T $node /entrypoint-no-systemd.sh start-pmm-agent + sleep 2 + fi + compose_rs exec -T -e PMM_AGENT_SETUP_NODE_NAME=${node}._${random_number} $node pmm-agent setup ${DEBUG_FLAG} if [[ $mongo_setup_type == "psa" && $node == "rs103" ]]; then - docker compose -f docker-compose-rs.yaml exec -T $node pmm-admin add mongodb --enable-all-collectors --agent-password=mypass --environment=psmdb-dev --cluster=replicaset --replication-set=rs --host=${node} --port=27017 ${node}${gssapi_service_name_part}_${random_number} + compose_rs exec -T $node pmm-admin add mongodb --enable-all-collectors --agent-password=mypass --environment=psmdb-dev --cluster=replicaset --replication-set=rs --host=${node} --port=27017 ${node}${gssapi_service_name_part}_${random_number} else echo - docker compose -f docker-compose-rs.yaml exec -T $node pmm-admin add mongodb --enable-all-collectors --agent-password=mypass --environment=psmdb-dev --cluster=replicaset --replication-set=rs ${client_credentials_flags[*]} --host=${node} --port=27017 ${node}${gssapi_service_name_part}_${random_number} + compose_rs exec -T $node pmm-admin add mongodb --enable-all-collectors --agent-password=mypass --environment=psmdb-dev --cluster=replicaset --replication-set=rs ${client_credentials_flags[*]} --host=${node} --port=27017 ${node}${gssapi_service_name_part}_${random_number} fi done echo echo "adding some data" -docker compose -f docker-compose-rs.yaml exec -T rs101 mgodatagen -f /etc/datagen/replicaset.json --uri=mongodb://${pmm_mongo_user}:${pmm_mongo_user_pass}@127.0.0.1:27017/?replicaSet=rs -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo "mongodb://${pmm_mongo_user}:${pmm_mongo_user_pass}@localhost/?replicaSet=rs" --quiet << EOF +compose_rs exec -T rs101 mgodatagen -f /etc/datagen/replicaset.json --uri=mongodb://${pmm_mongo_user}:${pmm_mongo_user_pass}@127.0.0.1:27017/?replicaSet=rs +compose_rs exec -T rs101 mongo "mongodb://${pmm_mongo_user}:${pmm_mongo_user_pass}@localhost/?replicaSet=rs" --quiet << EOF use students; db.students.insertMany([ { diff --git a/qa-integration/pmm_psmdb-pbm_setup/configure-replset.sh b/qa-integration/pmm_psmdb-pbm_setup/configure-replset.sh index 2b6f7b597..e4c470f54 100644 --- a/qa-integration/pmm_psmdb-pbm_setup/configure-replset.sh +++ b/qa-integration/pmm_psmdb-pbm_setup/configure-replset.sh @@ -1,6 +1,8 @@ #!/bin/bash set -e +source "$(dirname "$0")/scripts/compose-env.sh" + pmm_mongo_user=${PMM_MONGO_USER:-pmm} pmm_mongo_user_pass=${PMM_MONGO_USER_PASS:-pmmpass} pbm_user=${PBM_USER:-pbm} @@ -8,7 +10,7 @@ pbm_pass=${PBM_PASS:-pbmpass} echo echo "configuring replicaset with members priorities" -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo --quiet << EOF +compose_rs exec -T rs101 mongo --quiet << EOF config = { "_id" : "rs", "members" : [ @@ -35,12 +37,12 @@ echo sleep 60 echo echo "configuring root user on primary" -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo --quiet << EOF +compose_rs exec -T rs101 mongo --quiet << EOF db.getSiblingDB("admin").createUser({ user: "root", pwd: "root", roles: [ "root", "userAdminAnyDatabase", "clusterAdmin" ] }); EOF echo echo "configuring pbm and pmm roles" -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF +compose_rs exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF db.getSiblingDB("admin").createRole({ "role": "pbmAnyAction", "privileges": [{ @@ -70,7 +72,7 @@ db.getSiblingDB("admin").createRole({ EOF echo echo "creating pbm user" -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF +compose_rs exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF db.getSiblingDB("admin").createUser({ user: "${pbm_user}", pwd: "${pbm_pass}", @@ -85,7 +87,7 @@ db.getSiblingDB("admin").createUser({ EOF echo echo "creating pmm regular user" -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF +compose_rs exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF db.getSiblingDB("admin").createUser({ user: "${pmm_mongo_user}", pwd: "${pmm_mongo_user_pass}", @@ -102,7 +104,7 @@ db.getSiblingDB("admin").createUser({ }); EOF echo "creating pmm kerberos user" -docker compose -f docker-compose-rs.yaml exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF +compose_rs exec -T rs101 mongo "mongodb://root:root@localhost/?replicaSet=rs" --quiet << EOF db.getSiblingDB("\$external").createUser({ user: "${pmm_mongo_user}@PERCONATEST.COM", roles: [ diff --git a/qa-integration/pmm_psmdb-pbm_setup/docker-compose-rs.microvm.yaml b/qa-integration/pmm_psmdb-pbm_setup/docker-compose-rs.microvm.yaml new file mode 100644 index 000000000..4561e76f8 --- /dev/null +++ b/qa-integration/pmm_psmdb-pbm_setup/docker-compose-rs.microvm.yaml @@ -0,0 +1,75 @@ +# Compose override for MicroVM / environments without systemd as PID 1. +# Usage: docker compose -f docker-compose-rs.yaml -f docker-compose-rs.microvm.yaml up -d +services: + rs101: + privileged: false + cgroup: ~ + volumes: !override + - ./conf/pbm:/etc/pbm + - ${MONGOD_RS_CONFIG_DIR:-./conf/mongod-rs}:/etc/mongod + - ./conf/datagen:/etc/datagen:ro + - /tmp/backup_data:/tmp/backup_data:rw + - keytabs:/keytabs + - ./entrypoint-no-systemd.sh:/entrypoint-no-systemd.sh:ro + - ./scripts/systemctl-shim.sh:/usr/local/bin/systemctl:ro + entrypoint: bash /entrypoint-no-systemd.sh run + + rs102: + privileged: false + cgroup: ~ + volumes: !override + - ./conf/pbm:/etc/pbm + - ${MONGOD_RS_CONFIG_DIR:-./conf/mongod-rs}:/etc/mongod + - /tmp/backup_data:/tmp/backup_data:rw + - keytabs:/keytabs + - ./entrypoint-no-systemd.sh:/entrypoint-no-systemd.sh:ro + - ./scripts/systemctl-shim.sh:/usr/local/bin/systemctl:ro + entrypoint: bash /entrypoint-no-systemd.sh run + + rs103: + privileged: false + cgroup: ~ + volumes: !override + - ./conf/pbm:/etc/pbm + - ${MONGOD_RS_CONFIG_DIR:-./conf/mongod-rs}:/etc/mongod + - /tmp/backup_data:/tmp/backup_data:rw + - keytabs:/keytabs + - ./entrypoint-no-systemd.sh:/entrypoint-no-systemd.sh:ro + - ./scripts/systemctl-shim.sh:/usr/local/bin/systemctl:ro + entrypoint: bash /entrypoint-no-systemd.sh run + + rs201: + privileged: false + cgroup: ~ + volumes: !override + - ./conf/pbm:/etc/pbm + - ${MONGOD_RS_CONFIG_DIR:-./conf/mongod-rs}:/etc/mongod + - /tmp/backup_data:/tmp/backup_data:rw + - keytabs:/keytabs + - ./entrypoint-no-systemd.sh:/entrypoint-no-systemd.sh:ro + - ./scripts/systemctl-shim.sh:/usr/local/bin/systemctl:ro + entrypoint: bash /entrypoint-no-systemd.sh run + + rs202: + privileged: false + cgroup: ~ + volumes: !override + - ./conf/pbm:/etc/pbm + - ${MONGOD_RS_CONFIG_DIR:-./conf/mongod-rs}:/etc/mongod + - /tmp/backup_data:/tmp/backup_data:rw + - keytabs:/keytabs + - ./entrypoint-no-systemd.sh:/entrypoint-no-systemd.sh:ro + - ./scripts/systemctl-shim.sh:/usr/local/bin/systemctl:ro + entrypoint: bash /entrypoint-no-systemd.sh run + + rs203: + privileged: false + cgroup: ~ + volumes: !override + - ./conf/pbm:/etc/pbm + - ${MONGOD_RS_CONFIG_DIR:-./conf/mongod-rs}:/etc/mongod + - /tmp/backup_data:/tmp/backup_data:rw + - keytabs:/keytabs + - ./entrypoint-no-systemd.sh:/entrypoint-no-systemd.sh:ro + - ./scripts/systemctl-shim.sh:/usr/local/bin/systemctl:ro + entrypoint: bash /entrypoint-no-systemd.sh run diff --git a/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh b/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh new file mode 100755 index 000000000..bae72e4bd --- /dev/null +++ b/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh @@ -0,0 +1,80 @@ +#!/bin/bash +set -euo pipefail + +# MicroVM / non-systemd entrypoint for PSMDB+PBM replica set containers. +# Starts mongod and pbm-agent as background processes instead of systemd. + +export PATH="/usr/local/bin:${PATH}" +export MANAGE_THP=0 + +chown -R mongod:mongod /keytabs 2>/dev/null || true +mkdir -p /var/log/mongo /var/lib/mongo /var/run +chown -R mongod:mongod /var/log/mongo /var/lib/mongo + +if [[ -f /etc/sysconfig/pbm-agent ]]; then + # shellcheck disable=SC1091 + source /etc/sysconfig/pbm-agent +fi + +start_mongod() { + /usr/bin/percona-server-mongodb-helper.sh || true + # shellcheck disable=SC1091 + source /etc/sysconfig/mongod + export GLIBC_TUNABLES=glibc.pthread.rseq=0 + export MONGODB_CONFIG_OVERRIDE_NOFORK=1 + su -s /bin/bash mongod -c "/usr/bin/mongod ${OPTIONS}" & + echo $! >/var/run/mongod.pid +} + +start_pbm_agent() { + if [[ -f /var/run/pbm-agent.pid ]] && kill -0 "$(cat /var/run/pbm-agent.pid)" 2>/dev/null; then + return 0 + fi + su -s /bin/bash mongod -c "/usr/bin/pbm-agent" & + echo $! >/var/run/pbm-agent.pid +} + +stop_pbm_agent() { + if [[ -f /var/run/pbm-agent.pid ]]; then + kill "$(cat /var/run/pbm-agent.pid)" 2>/dev/null || true + rm -f /var/run/pbm-agent.pid + fi + pkill -u mongod -x pbm-agent 2>/dev/null || true +} + +restart_pbm_agent() { + stop_pbm_agent + sleep 1 + start_pbm_agent +} + +start_pmm_agent() { + if [[ -f /var/run/pmm-agent.pid ]] && kill -0 "$(cat /var/run/pmm-agent.pid)" 2>/dev/null; then + return 0 + fi + export KRB5_CLIENT_KTNAME=/keytabs/mongodb.keytab + /usr/sbin/pmm-agent --config-file=/usr/local/percona/pmm/config/pmm-agent.yaml >>/var/log/pmm-agent.log 2>&1 & + echo $! >/var/run/pmm-agent.pid +} + +case "${1:-run}" in + run) + start_mongod + for _ in $(seq 1 90); do + if mongosh --quiet --eval 'db.adminCommand({ping:1})' >/dev/null 2>&1; then + break + fi + sleep 1 + done + start_pbm_agent + wait "$(cat /var/run/mongod.pid)" + ;; + start-mongod) start_mongod ;; + start-pbm-agent) start_pbm_agent ;; + restart-pbm-agent) restart_pbm_agent ;; + stop-pbm-agent) stop_pbm_agent ;; + start-pmm-agent) start_pmm_agent ;; + *) + exec "$@" + ;; +esac diff --git a/qa-integration/pmm_psmdb-pbm_setup/scripts/compose-env.sh b/qa-integration/pmm_psmdb-pbm_setup/scripts/compose-env.sh new file mode 100644 index 000000000..d8ddeb52b --- /dev/null +++ b/qa-integration/pmm_psmdb-pbm_setup/scripts/compose-env.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Shared docker compose helper for replica-set setup scripts. +compose_rs() { + if [[ "${PMM_QA_NO_SYSTEMD:-}" == "1" ]]; then + docker compose -f docker-compose-rs.yaml -f docker-compose-rs.microvm.yaml "$@" + else + docker compose -f docker-compose-rs.yaml "$@" + fi +} diff --git a/qa-integration/pmm_psmdb-pbm_setup/scripts/systemctl-shim.sh b/qa-integration/pmm_psmdb-pbm_setup/scripts/systemctl-shim.sh new file mode 100755 index 000000000..045d2b840 --- /dev/null +++ b/qa-integration/pmm_psmdb-pbm_setup/scripts/systemctl-shim.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Minimal systemctl replacement for configure-*.sh scripts in no-systemd mode. +set -euo pipefail + +cmd=${1:-} +unit=${2:-} + +case "$cmd" in + enable|disable|daemon-reload) + exit 0 + ;; + restart) + case "$unit" in + pbm-agent) exec /entrypoint-no-systemd.sh restart-pbm-agent ;; + pmm-agent) exec /entrypoint-no-systemd.sh start-pmm-agent ;; + mongod) exec /entrypoint-no-systemd.sh start-mongod ;; + *) exit 0 ;; + esac + ;; + start) + case "$unit" in + pbm-agent) exec /entrypoint-no-systemd.sh start-pbm-agent ;; + pmm-agent) exec /entrypoint-no-systemd.sh start-pmm-agent ;; + mongod) exec /entrypoint-no-systemd.sh start-mongod ;; + *) exit 0 ;; + esac + ;; + stop) + case "$unit" in + pbm-agent) exec /entrypoint-no-systemd.sh stop-pbm-agent ;; + pmm-agent) + if [[ -f /var/run/pmm-agent.pid ]]; then + kill "$(cat /var/run/pmm-agent.pid)" 2>/dev/null || true + rm -f /var/run/pmm-agent.pid + fi + pkill -x pmm-agent 2>/dev/null || true + ;; + *) exit 0 ;; + esac + ;; + is-active) + case "$unit" in + mongod) + mongosh --quiet --eval 'db.adminCommand({ping:1})' >/dev/null 2>&1 && exit 0 + exit 3 + ;; + *) exit 0 ;; + esac + ;; + *) + exit 0 + ;; +esac diff --git a/qa-integration/pmm_psmdb-pbm_setup/start-rs-only-microvm.sh b/qa-integration/pmm_psmdb-pbm_setup/start-rs-only-microvm.sh new file mode 100755 index 000000000..5eb7901ee --- /dev/null +++ b/qa-integration/pmm_psmdb-pbm_setup/start-rs-only-microvm.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -euo pipefail + +profile=${COMPOSE_PROFILES:-classic} +mongo_setup_type=${MONGO_SETUP_TYPE:-pss} +mongo_setup_type=${mongo_setup_type,,} +mongo_storage_engine=${MONGO_STORAGE_ENGINE:-wiredTiger} +mongo_storage_engine=${mongo_storage_engine,,} +ol_version=${OL_VERSION:-9} + +export COMPOSE_PROFILES=${profile} +export MONGO_SETUP_TYPE=${mongo_setup_type} +export OL_VERSION=${ol_version} +export PMM_QA_NO_SYSTEMD=1 + +compose_args=(-f docker-compose-rs.yaml -f docker-compose-rs.microvm.yaml) + +if [ "$mongo_storage_engine" = "inmemory" ]; then + generated_config_dir="/tmp/pmm-qa-mongod-rs-inmemory" + rm -rf "$generated_config_dir" + mkdir -p "$generated_config_dir" + cp ./conf/mongod-rs-inmemory/mongod.conf "$generated_config_dir/mongod.conf" + export MONGOD_RS_CONFIG_DIR="$generated_config_dir" +else + mongo_storage_engine="wiredTiger" +fi + +docker network create qa-integration 2>/dev/null || true +docker network create pmm-qa 2>/dev/null || true +docker network create pmm-ui-tests_pmm-network 2>/dev/null || true +docker network create pmm2-upgrade-tests_pmm-network 2>/dev/null || true +docker network create pmm2-ui-tests_pmm-network 2>/dev/null || true + +docker compose "${compose_args[@]}" down -v --remove-orphans +docker compose "${compose_args[@]}" build +docker compose "${compose_args[@]}" up -d + +echo +echo "waiting 30 seconds for replica set members to start (no-systemd mode)" +sleep 30 +echo + +if [ "$mongo_setup_type" == "pss" ]; then + bash -e ./configure-replset.sh +else + bash -e ./configure-psa.sh +fi + +# configure-agents uses systemctl; shim is mounted at /usr/local/bin/systemctl in containers. +bash -x ./configure-agents.sh + +if [ "$profile" = "extra" ]; then + if [ "$mongo_setup_type" == "pss" ]; then + bash -x ./configure-extra-replset.sh + else + bash -x ./configure-extra-psa.sh + fi + bash -x ./configure-extra-agents.sh +fi diff --git a/qa-integration/pmm_qa/pmm-framework.py b/qa-integration/pmm_qa/pmm-framework.py index d138a0f74..b91e2b3e5 100755 --- a/qa-integration/pmm_qa/pmm-framework.py +++ b/qa-integration/pmm_qa/pmm-framework.py @@ -517,7 +517,10 @@ def setup_psmdb(db_type, db_version=None, db_config=None, args=None): setup_type = get_value('SETUP_TYPE', db_type, args, db_config).lower() if setup_type in ("pss", "psa"): - shell_scripts = ['start-rs-only.sh'] + if os.getenv('PMM_QA_NO_SYSTEMD', '').lower() in ('1', 'true', 'yes'): + shell_scripts = ['start-rs-only-microvm.sh'] + else: + shell_scripts = ['start-rs-only.sh'] elif setup_type in ("shards", "sharding"): shell_scripts = ['start-sharded.sh'] diff --git a/qa-integration/scripts/cleanup-pmm-microvm.sh b/qa-integration/scripts/cleanup-pmm-microvm.sh new file mode 100755 index 000000000..7556a5284 --- /dev/null +++ b/qa-integration/scripts/cleanup-pmm-microvm.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Tear down PMM Server and QA integration containers on MicroVM. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +QA_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +echo "==> Stopping PMM server stack..." +for name in pmm-server watchtower; do + docker rm -f "$name" 2>/dev/null || true +done + +echo "==> Stopping PSMDB/PBM replica set (if present)..." +if [ -f "${QA_ROOT}/pmm_psmdb-pbm_setup/docker-compose-rs.yaml" ]; then + ( + cd "${QA_ROOT}/pmm_psmdb-pbm_setup" + export PMM_QA_NO_SYSTEMD=1 + docker compose -f docker-compose-rs.yaml -f docker-compose-rs.microvm.yaml down -v --remove-orphans 2>/dev/null \ + || docker compose -f docker-compose-rs.yaml down -v --remove-orphans 2>/dev/null \ + || true + ) +fi + +for name in rs101 rs102 rs103 rs201 rs202 rs203 rs-test minio createbucket kerberos; do + docker rm -f "$name" 2>/dev/null || true +done + +if docker network inspect pmm-qa >/dev/null 2>&1; then + while read -r cid; do + [ -n "$cid" ] || continue + docker rm -f "$cid" 2>/dev/null || true + done < <(docker network inspect pmm-qa -f '{{range .Containers}}{{.Name}}{{"\n"}}{{end}}' 2>/dev/null || true) +fi + +if [ "${REMOVE_VOLUME:-0}" = "1" ]; then + echo "==> Removing pmm-data volume..." + docker volume rm pmm-data 2>/dev/null || true +fi + +if [ "${REMOVE_NETWORK:-0}" = "1" ]; then + echo "==> Removing pmm-qa network..." + docker network rm pmm-qa 2>/dev/null || true +fi + +sudo rm -rf /tmp/backup_data /tmp/minio 2>/dev/null || true +mkdir -m 777 -p /tmp/backup_data + +echo "Cleanup done." diff --git a/qa-integration/scripts/lib/wait-pmm-ready.sh b/qa-integration/scripts/lib/wait-pmm-ready.sh new file mode 100755 index 000000000..13c3f9f0c --- /dev/null +++ b/qa-integration/scripts/lib/wait-pmm-ready.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Wait until PMM Server readyz returns HTTP 200 and body "{}". +# Usage: wait-pmm-ready.sh [readyz-url] +set -euo pipefail + +READYZ_URL="${1:-${PMM_READY_URL:-https://127.0.0.1/v1/server/readyz}}" +TIMEOUT_SEC="${PMM_READY_TIMEOUT:-600}" +INTERVAL_SEC="${PMM_READY_INTERVAL:-5}" + +elapsed=0 +while [ "$elapsed" -lt "$TIMEOUT_SEC" ]; do + http_code=$(curl -ksS -o /tmp/pmm-readyz-body.txt -w '%{http_code}' "$READYZ_URL" 2>/dev/null || echo "000") + body=$(tr -d '[:space:]' /dev/null || true) + + if [ "$http_code" = "200" ] && [ "$body" = "{}" ]; then + echo "PMM Server is ready (${READYZ_URL} → HTTP 200, body={})" + exit 0 + fi + + preview=$(head -c 120 /tmp/pmm-readyz-body.txt 2>/dev/null | tr '\n' ' ') + echo "waiting for readyz... HTTP ${http_code} (${elapsed}s/${TIMEOUT_SEC}s) ${preview}" + sleep "$INTERVAL_SEC" + elapsed=$((elapsed + INTERVAL_SEC)) +done + +echo "ERROR: PMM Server not ready after ${TIMEOUT_SEC}s (${READYZ_URL})" >&2 +exit 1 diff --git a/qa-integration/scripts/provision-pmm.sh b/qa-integration/scripts/provision-pmm.sh new file mode 100755 index 000000000..5b0d56c61 --- /dev/null +++ b/qa-integration/scripts/provision-pmm.sh @@ -0,0 +1,158 @@ +#!/usr/bin/env bash +# Provision PMM Server for manual QA on MicroVM (Jenkins pmm3-aws-staging-start parity). +# +# This script only brings up Docker + PMM Server (+ optional Watchtower). +# Database/client provisioning is a separate step via pmm-framework.py so each +# ticket can choose the right --database flags. +# +# Usage: +# export DOCKER_VERSION=perconalab/pmm-server-fb:PR-4431-c11a557 +# export CLIENT_VERSION='https://s3.../pmm-client-PR-4431-c11a557.tar.gz' +# export ADMIN_PASSWORD='pmm3admin!' +# ./provision-pmm.sh +# +# Options: +# --cleanup Remove existing PMM/QA containers before provisioning +# --fresh-volume Delete and recreate pmm-data volume (fixes stale ClickHouse) +# --skip-watchtower Do not start Watchtower (fine for pinned FB server images) +# --profile NAME Currently only: fb-staging (default) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +QA_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +PMM_QA_DIR="${QA_ROOT}/pmm_qa" + +PROFILE="fb-staging" +DO_CLEANUP=0 +FRESH_VOLUME=0 +SKIP_WATCHTOWER=0 + +usage() { + sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//' + exit "${1:-0}" +} + +while [ $# -gt 0 ]; do + case "$1" in + --cleanup) DO_CLEANUP=1 ;; + --fresh-volume) FRESH_VOLUME=1 ;; + --skip-watchtower) SKIP_WATCHTOWER=1 ;; + --profile) PROFILE="${2:?--profile requires a value}"; shift ;; + -h|--help) usage 0 ;; + *) echo "Unknown option: $1" >&2; usage 1 ;; + esac + shift +done + +if [ "$PROFILE" != "fb-staging" ]; then + echo "ERROR: unsupported profile '$PROFILE' (only fb-staging)" >&2 + exit 1 +fi + +# --- required env (from FB build / Jenkins) --- +: "${DOCKER_VERSION:?Set DOCKER_VERSION (FB server image, e.g. perconalab/pmm-server-fb:PR-4431-c11a557)}" +: "${ADMIN_PASSWORD:?Set ADMIN_PASSWORD (e.g. pmm3admin!)}" + +WATCHTOWER_VERSION="${WATCHTOWER_VERSION:-perconalab/watchtower:dev-latest}" +WATCHTOWER_TOKEN="${WATCHTOWER_TOKEN:-testToken}" +CLIENT_VERSION="${CLIENT_VERSION:-3-dev-latest}" + +echo "==> [1/6] Start Docker (MicroVM)" +"${SCRIPT_DIR}/start-docker-microvm.sh" + +if [ "$DO_CLEANUP" = "1" ]; then + echo "==> [2/6] Cleanup existing stack" + REMOVE_VOLUME=$([ "$FRESH_VOLUME" = "1" ] && echo 1 || echo 0) \ + "${SCRIPT_DIR}/cleanup-pmm-microvm.sh" +else + echo "==> [2/6] Skip cleanup (pass --cleanup to reset)" + if [ "$FRESH_VOLUME" = "1" ]; then + docker rm -f pmm-server watchtower 2>/dev/null || true + docker volume rm pmm-data 2>/dev/null || true + fi +fi + +echo "==> [3/6] Networks, volumes, scratch dirs" +docker network create pmm-qa 2>/dev/null || true +if ! docker volume inspect pmm-data >/dev/null 2>&1; then + docker volume create pmm-data +fi +mkdir -m 777 -p /tmp/backup_data + +echo "==> [4/6] Pull images" +docker pull "$DOCKER_VERSION" +if [ "$SKIP_WATCHTOWER" = "0" ]; then + docker pull "$WATCHTOWER_VERSION" +fi + +echo "==> [5/6] Start Watchtower + PMM Server" +if [ "$SKIP_WATCHTOWER" = "0" ]; then + docker rm -f watchtower 2>/dev/null || true + docker run -d --restart=always --name watchtower \ + --network pmm-qa \ + -p 8080:8080 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e WATCHTOWER_HTTP_API_TOKEN="$WATCHTOWER_TOKEN" \ + "$WATCHTOWER_VERSION" +fi + +docker rm -f pmm-server 2>/dev/null || true +run_args=( + -d --restart=always + --name pmm-server + --hostname pmm-server + --network pmm-qa + -p 443:8443 + -p 4647:4647 + -v pmm-data:/srv + -e "GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD}" +) +if [ "$SKIP_WATCHTOWER" = "0" ]; then + run_args+=( + -e PMM_WATCHTOWER_HOST=http://watchtower:8080 + -e "PMM_WATCHTOWER_TOKEN=${WATCHTOWER_TOKEN}" + ) +fi +# shellcheck disable=SC2068 +docker run ${run_args[@]} "$DOCKER_VERSION" + +echo "==> [6/6] Wait for PMM Server readyz" +"${SCRIPT_DIR}/lib/wait-pmm-ready.sh" "https://127.0.0.1/v1/server/readyz" + +cat </dev/null || true + source virtenv/bin/activate + pip install -q -r requirements.txt + + export PMM_QA_NO_SYSTEMD=1 + export ADMIN_PASSWORD='${ADMIN_PASSWORD}' + export CLIENT_VERSION='${CLIENT_VERSION}' + + # PMM-14576 MongoDB backup / PBM: + python pmm-framework.py \\ + --pmm-server-password "\$ADMIN_PASSWORD" \\ + --client-version "\$CLIENT_VERSION" \\ + --database psmdb,SETUP_TYPE=pss \\ + --verbose + + # Other tickets — change --database per scripts/database_options.py, e.g.: + # --database ps,SETUP_TYPE=gr + # --database pgsql + # --database psmdb,SETUP_TYPE=sharding + +To reset everything: + ${SCRIPT_DIR}/provision-pmm.sh --cleanup --fresh-volume +================================================================================ +EOF From 4a191f7297e2e0f53747f68cf47bd4202844a7c4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 10:39:29 +0000 Subject: [PATCH 03/83] Add MicroVM QA runbook and DOCKER_ENV_VARIABLE in provision script MANUAL-QA-MICROVM.md is the expanded reference for the cloud agent prompt. provision-pmm.sh now accepts Jenkins-style DOCKER_ENV_VARIABLE extra -e flags. Co-authored-by: davi.travaglia --- qa-integration/MANUAL-QA-MICROVM.md | 76 +++++++++++++++++++++++++ qa-integration/scripts/provision-pmm.sh | 7 +++ 2 files changed, 83 insertions(+) create mode 100644 qa-integration/MANUAL-QA-MICROVM.md diff --git a/qa-integration/MANUAL-QA-MICROVM.md b/qa-integration/MANUAL-QA-MICROVM.md new file mode 100644 index 000000000..3c3adc953 --- /dev/null +++ b/qa-integration/MANUAL-QA-MICROVM.md @@ -0,0 +1,76 @@ +# MicroVM manual QA — agent reference + +Condensed runbook for the PMM manual QA cloud agent on Cursor MicroVM. +**Do not freestyle `docker run` or `curl` loops** — use these scripts. + +## 1. Server (always first) + +```bash +export DOCKER_VERSION=... # FB: Server docker +export WATCHTOWER_VERSION=... # FB: Watchtower docker (optional with --skip-watchtower) +export CLIENT_VERSION='...' # FB: Client tarball URL (for step 2) +export ADMIN_PASSWORD='pmm3admin!' +export DOCKER_ENV_VARIABLE='-e PMM_DEBUG=1 -e PMM_ENABLE_TELEMETRY=0' # override per ticket + +qa-integration/scripts/provision-pmm.sh --cleanup --fresh-volume +``` + +- Starts Docker via `start-docker-microvm.sh` +- Jenkins `pmm3-aws-staging-start` server parity (`--hostname pmm-server`, watchtower token `testToken`) +- **readyz:** `https://127.0.0.1/v1/server/readyz` → HTTP **200**, body **`{}`** (not `grep ok`) +- **Do not** run `pmm3-client-setup.sh` on MicroVM (GHA pattern; clients live in DB containers via `pmm-framework.py`) + +## 2. Databases (ticket-specific, after server is up) + +```bash +cd qa-integration/pmm_qa && source virtenv/bin/activate +export PMM_QA_NO_SYSTEMD=1 # required on MicroVM for PSMDB+PBM +export ADMIN_PASSWORD='pmm3admin!' +export CLIENT_VERSION='...' + +python pmm-framework.py \ + --pmm-server-password "$ADMIN_PASSWORD" \ + --client-version "$CLIENT_VERSION" \ + --database \ + --verbose +``` + +Pick `--database` from test plan + `pmm_qa/scripts/database_options.py`. Examples: + +| Ticket scope | `--database` | +|--------------|----------------| +| MongoDB backup/PBM | `psmdb,SETUP_TYPE=pss` | +| MySQL GR | `ps,SETUP_TYPE=gr` | +| PostgreSQL | `pgsql` | +| PSMDB sharded | `psmdb,SETUP_TYPE=sharding` | + +**Never** use `mlaunch_psmdb` for backup/PBM tickets (no PBM). + +## 3. UI (playwright-cli) + +```bash +export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS=true +playwright-cli open https://127.0.0.1/graph/login +``` + +## 4. Reset + +```bash +qa-integration/scripts/cleanup-pmm-microvm.sh +# or: provision-pmm.sh --cleanup --fresh-volume +``` + +## Escalation + +If `pmm-framework.py` fails with RS containers exit 255 **before** `PMM_QA_NO_SYSTEMD=1` was set, retry with that env var. +If still blocked, report BLOCKED and link Jenkins `pmm3-aws-staging-start` parambuild URL. + +## Script map + +| Script | Purpose | +|--------|---------| +| `scripts/provision-pmm.sh` | PMM Server + watchtower | +| `scripts/lib/wait-pmm-ready.sh` | readyz waiter | +| `scripts/cleanup-pmm-microvm.sh` | teardown | +| `scripts/start-docker-microvm.sh` | dockerd on MicroVM | +| `pmm_psmdb-pbm_setup/start-rs-only-microvm.sh` | called via pmm-framework when `PMM_QA_NO_SYSTEMD=1` | diff --git a/qa-integration/scripts/provision-pmm.sh b/qa-integration/scripts/provision-pmm.sh index 5b0d56c61..f767dfc57 100755 --- a/qa-integration/scripts/provision-pmm.sh +++ b/qa-integration/scripts/provision-pmm.sh @@ -113,6 +113,13 @@ if [ "$SKIP_WATCHTOWER" = "0" ]; then -e "PMM_WATCHTOWER_TOKEN=${WATCHTOWER_TOKEN}" ) fi +# Ticket-specific server env (Jenkins DOCKER_ENV_VARIABLE), e.g.: +# export DOCKER_ENV_VARIABLE='-e PMM_DEBUG=1 -e PMM_ENABLE_TELEMETRY=0' +if [ -n "${DOCKER_ENV_VARIABLE:-}" ]; then + # shellcheck disable=SC2206 + extra_env=( ${DOCKER_ENV_VARIABLE} ) + run_args+=( "${extra_env[@]}" ) +fi # shellcheck disable=SC2068 docker run ${run_args[@]} "$DOCKER_VERSION" From db1b12402cf76071653e9a6263af1c21f4dafa96 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 10:41:41 +0000 Subject: [PATCH 04/83] Add temporary automation prompt draft for MicroVM QA agent Co-authored-by: davi.travaglia --- AUTOMATION-PROMPT.md | 149 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 AUTOMATION-PROMPT.md diff --git a/AUTOMATION-PROMPT.md b/AUTOMATION-PROMPT.md new file mode 100644 index 000000000..22b2b71e7 --- /dev/null +++ b/AUTOMATION-PROMPT.md @@ -0,0 +1,149 @@ +# PMM Manual QA Cloud Agent — Automation Prompt + +> **Temporary file** — copy this into the Cursor Automation trigger prompt, then delete this file from the branch. + +--- + +You are **Test Runner** — PMM manual QA cloud agent. + +Triggered when a Jira ticket is **Ready for QA**. + +**MicroVM runbook (read before provisioning):** `qa-integration/MANUAL-QA-MICROVM.md` + +--- + +## 1 — Read the ticket + +Atlassian MCP on **PMM-XXXX** (`XXXX` from trigger): + +- Summary, description, acceptance criteria +- How to test (`customfield_10083`) +- FB test screenshots (`customfield_10492`) if present +- All comments +- Development links → `percona/pmm` and/or `percona/grafana` PRs + +`gh pr diff` on linked product PRs. Read changed files in workspace (`pmm/`) when cloned. + +Cross-check **How to test** against actual code changes. Note gaps and extra cases dev possibly missed. + +--- + +## 2 — Plan testing + +Write a short test plan in your run output: + +- What to verify (mapped to acceptance criteria) +- PMM server env vars (`DOCKER_ENV_VARIABLE`) — defaults are often wrong (e.g. telemetry) +- Monitored databases for `pmm-framework.py` (`--database` args; see `pmm_qa/scripts/database_options.py`) — **not** Jenkins `CLIENTS` +- Post-provision steps (PMM Settings toggles) +- FB images from latest **JNKPercona** comment on linked pmm-submodules PR: + - Server docker → `DOCKER_VERSION` + - Watchtower docker → `WATCHTOWER_VERSION` + - Client tarball → `CLIENT_VERSION` + +--- + +## 3 — Provision PMM + +**Do not freestyle `docker run` / `curl` loops.** Follow `qa-integration/MANUAL-QA-MICROVM.md`. + +### 3a — Server (always) + +```bash +export DOCKER_VERSION=... # FB: Server docker +export WATCHTOWER_VERSION=... # FB: Watchtower docker +export CLIENT_VERSION='...' # FB: Client tarball (for step 3b) +export ADMIN_PASSWORD='pmm3admin!' +export DOCKER_ENV_VARIABLE='-e PMM_DEBUG=1 -e PMM_ENABLE_TELEMETRY=0' # override per ticket + +qa-integration/scripts/provision-pmm.sh --cleanup --fresh-volume +``` + +- **readyz success:** `https://127.0.0.1/v1/server/readyz` → HTTP **200**, body **`{}`** +- **Do not** `grep ok` on readyz +- **Do not** run `pmm3-client-setup.sh` (GHA pattern; not staging-start) + +### 3b — Databases (ticket-specific, only if needed) + +```bash +cd qa-integration/pmm_qa +source virtenv/bin/activate + +export PMM_QA_NO_SYSTEMD=1 +export ADMIN_PASSWORD='pmm3admin!' +export CLIENT_VERSION='...' + +python pmm-framework.py \ + --pmm-server-password "$ADMIN_PASSWORD" \ + --client-version "$CLIENT_VERSION" \ + --database \ + --verbose +``` + +Examples: `psmdb,SETUP_TYPE=pss` (backup/PBM), `ps,SETUP_TYPE=gr`, `pgsql`. + +**Never** use `mlaunch_psmdb` for backup/PBM tickets. + +--- + +## 4 — Execute manual QA + +**Terminal** — API/CLI checks (`curl`, `pmm-admin` via cli suite patterns). + +**UI** — `playwright-cli` (installed globally): + +```bash +export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS=true +playwright-cli open https://127.0.0.1/graph/login +playwright-cli resize 1920 1080 +playwright-cli snapshot +# fill / click / type as needed +playwright-cli close +``` + +Exercise every item in your test plan. Record **pass/fail** per criterion. + +--- + +## 5 — Report manual test results + +**Jira comment:** test plan, results table, screenshot paths, blockers. + +**Slack (#qa-automation):** keep it **brief and concise**: + +- Ticket key + verdict (**PASS** / **FAIL** / **BLOCKED**) +- 3–5 bullet results (one line each) +- Blocker one-liner if any +- Link to Jira comment + +Do **not** paste full logs, provisioning steps, or long narratives to Slack. + +--- + +## 6 — Automation decision (after manual QA complete) + +Ask: can this be automated in **pmm-qa** with **MINIMAL** changes? + +**Yes** if: + +- Stable selectors/flows exist or small helpers suffice +- No heavy multi-VM topology or large new framework work +- Test adds clear value + +**No** if: + +- One-off manual setup, flaky infra, or large new framework work + +- **Yes** → implement smallest test in `e2e_tests` or `cli`; open PR to `percona/pmm-qa` only +- **No** → stop after Jira/Slack comment; no PR + +--- + +## Never + +- Open PRs to `percona/pmm` or `percona/grafana` +- Touch `percona/pmm` or `percona/grafana` code (out of scope for tester role) +- Clone `Percona-Lab/pmm-submodules` +- Write CodeceptJS tests (Playwright only — migration in progress) +- Trust **How to test** without reading PR diff +- Freestyle Docker provisioning instead of `provision-pmm.sh` From ed21b29b040e962e077b93a240143ea900703a3c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 10:47:42 +0000 Subject: [PATCH 05/83] Remove temporary AUTOMATION-PROMPT.md Prompt content was copied to the Cursor Automation config; the committed runbook remains at qa-integration/MANUAL-QA-MICROVM.md. Co-authored-by: davi.travaglia --- AUTOMATION-PROMPT.md | 149 ------------------------------------------- 1 file changed, 149 deletions(-) delete mode 100644 AUTOMATION-PROMPT.md diff --git a/AUTOMATION-PROMPT.md b/AUTOMATION-PROMPT.md deleted file mode 100644 index 22b2b71e7..000000000 --- a/AUTOMATION-PROMPT.md +++ /dev/null @@ -1,149 +0,0 @@ -# PMM Manual QA Cloud Agent — Automation Prompt - -> **Temporary file** — copy this into the Cursor Automation trigger prompt, then delete this file from the branch. - ---- - -You are **Test Runner** — PMM manual QA cloud agent. - -Triggered when a Jira ticket is **Ready for QA**. - -**MicroVM runbook (read before provisioning):** `qa-integration/MANUAL-QA-MICROVM.md` - ---- - -## 1 — Read the ticket - -Atlassian MCP on **PMM-XXXX** (`XXXX` from trigger): - -- Summary, description, acceptance criteria -- How to test (`customfield_10083`) -- FB test screenshots (`customfield_10492`) if present -- All comments -- Development links → `percona/pmm` and/or `percona/grafana` PRs - -`gh pr diff` on linked product PRs. Read changed files in workspace (`pmm/`) when cloned. - -Cross-check **How to test** against actual code changes. Note gaps and extra cases dev possibly missed. - ---- - -## 2 — Plan testing - -Write a short test plan in your run output: - -- What to verify (mapped to acceptance criteria) -- PMM server env vars (`DOCKER_ENV_VARIABLE`) — defaults are often wrong (e.g. telemetry) -- Monitored databases for `pmm-framework.py` (`--database` args; see `pmm_qa/scripts/database_options.py`) — **not** Jenkins `CLIENTS` -- Post-provision steps (PMM Settings toggles) -- FB images from latest **JNKPercona** comment on linked pmm-submodules PR: - - Server docker → `DOCKER_VERSION` - - Watchtower docker → `WATCHTOWER_VERSION` - - Client tarball → `CLIENT_VERSION` - ---- - -## 3 — Provision PMM - -**Do not freestyle `docker run` / `curl` loops.** Follow `qa-integration/MANUAL-QA-MICROVM.md`. - -### 3a — Server (always) - -```bash -export DOCKER_VERSION=... # FB: Server docker -export WATCHTOWER_VERSION=... # FB: Watchtower docker -export CLIENT_VERSION='...' # FB: Client tarball (for step 3b) -export ADMIN_PASSWORD='pmm3admin!' -export DOCKER_ENV_VARIABLE='-e PMM_DEBUG=1 -e PMM_ENABLE_TELEMETRY=0' # override per ticket - -qa-integration/scripts/provision-pmm.sh --cleanup --fresh-volume -``` - -- **readyz success:** `https://127.0.0.1/v1/server/readyz` → HTTP **200**, body **`{}`** -- **Do not** `grep ok` on readyz -- **Do not** run `pmm3-client-setup.sh` (GHA pattern; not staging-start) - -### 3b — Databases (ticket-specific, only if needed) - -```bash -cd qa-integration/pmm_qa -source virtenv/bin/activate - -export PMM_QA_NO_SYSTEMD=1 -export ADMIN_PASSWORD='pmm3admin!' -export CLIENT_VERSION='...' - -python pmm-framework.py \ - --pmm-server-password "$ADMIN_PASSWORD" \ - --client-version "$CLIENT_VERSION" \ - --database \ - --verbose -``` - -Examples: `psmdb,SETUP_TYPE=pss` (backup/PBM), `ps,SETUP_TYPE=gr`, `pgsql`. - -**Never** use `mlaunch_psmdb` for backup/PBM tickets. - ---- - -## 4 — Execute manual QA - -**Terminal** — API/CLI checks (`curl`, `pmm-admin` via cli suite patterns). - -**UI** — `playwright-cli` (installed globally): - -```bash -export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS=true -playwright-cli open https://127.0.0.1/graph/login -playwright-cli resize 1920 1080 -playwright-cli snapshot -# fill / click / type as needed -playwright-cli close -``` - -Exercise every item in your test plan. Record **pass/fail** per criterion. - ---- - -## 5 — Report manual test results - -**Jira comment:** test plan, results table, screenshot paths, blockers. - -**Slack (#qa-automation):** keep it **brief and concise**: - -- Ticket key + verdict (**PASS** / **FAIL** / **BLOCKED**) -- 3–5 bullet results (one line each) -- Blocker one-liner if any -- Link to Jira comment - -Do **not** paste full logs, provisioning steps, or long narratives to Slack. - ---- - -## 6 — Automation decision (after manual QA complete) - -Ask: can this be automated in **pmm-qa** with **MINIMAL** changes? - -**Yes** if: - -- Stable selectors/flows exist or small helpers suffice -- No heavy multi-VM topology or large new framework work -- Test adds clear value - -**No** if: - -- One-off manual setup, flaky infra, or large new framework work - -- **Yes** → implement smallest test in `e2e_tests` or `cli`; open PR to `percona/pmm-qa` only -- **No** → stop after Jira/Slack comment; no PR - ---- - -## Never - -- Open PRs to `percona/pmm` or `percona/grafana` -- Touch `percona/pmm` or `percona/grafana` code (out of scope for tester role) -- Clone `Percona-Lab/pmm-submodules` -- Write CodeceptJS tests (Playwright only — migration in progress) -- Trust **How to test** without reading PR diff -- Freestyle Docker provisioning instead of `provision-pmm.sh` From 4119cbc99056bb5deb5cd498c634eb8a20a8b9bb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 12:18:55 +0000 Subject: [PATCH 06/83] Fix pbm-agent persistence in no-systemd entrypoint for MicroVM backups Use runuser+nohup instead of su so pbm-agent stays alive after start. Required for MongoDB PBM backup QA on MicroVM. Co-authored-by: davi.travaglia --- .../pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh b/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh index bae72e4bd..4445bd314 100755 --- a/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh +++ b/qa-integration/pmm_psmdb-pbm_setup/entrypoint-no-systemd.sh @@ -27,11 +27,13 @@ start_mongod() { } start_pbm_agent() { - if [[ -f /var/run/pbm-agent.pid ]] && kill -0 "$(cat /var/run/pbm-agent.pid)" 2>/dev/null; then + if pgrep -u mongod -x pbm-agent >/dev/null 2>&1; then + pgrep -u mongod -x pbm-agent >/var/run/pbm-agent.pid return 0 fi - su -s /bin/bash mongod -c "/usr/bin/pbm-agent" & - echo $! >/var/run/pbm-agent.pid + nohup runuser -u mongod -- /usr/bin/pbm-agent >>/var/log/pbm-agent.log 2>&1 & + sleep 1 + pgrep -u mongod -x pbm-agent >/var/run/pbm-agent.pid || true } stop_pbm_agent() { From f1e8b287e940270775d442e630f7d3a4dfc0a9f8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 17:30:11 +0000 Subject: [PATCH 07/83] Add PMM UI login script and dual-runtime pmmLogin workflow - Add qa-integration/scripts/pmm-ui-login.sh for playwright-cli login on Cloud Agent / MicroVM (session pmm-, programmatic /graph/login) - Expand .agents/workflows/pmmLogin.md with runtime routing table: playwright-cli vs Playwright MCP - Cross-link MANUAL-QA-MICROVM.md, workflowIndex, mcpRules, AGENTS.md Co-authored-by: davi.travaglia --- .agents/README.md | 2 +- .agents/workflows/mcpRules.md | 6 +- .agents/workflows/pmmLogin.md | 89 +++++++++++++++++++++++--- .agents/workflows/workflowIndex.md | 2 +- AGENTS.md | 1 + qa-integration/MANUAL-QA-MICROVM.md | 10 ++- qa-integration/scripts/pmm-ui-login.sh | 46 +++++++++++++ 7 files changed, 141 insertions(+), 15 deletions(-) create mode 100755 qa-integration/scripts/pmm-ui-login.sh diff --git a/.agents/README.md b/.agents/README.md index ab439a40f..e4cc05ad7 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -19,7 +19,7 @@ - `/apiIndex`: A subset of pmmApi.json, has list of common APIs used across PMM - `/bugReport`: Use this workflow to generate a bug report out of failing tests or unexpected behavior. - `/mcpRules`: Defines execution rules for the LLM when using Playwright MCP. -- `/pmmLogin`: Use this workflow to login to PMM using basic Auth headers via MCP instead of the UI. +- `/pmmLogin`: PMM UI login — **Cloud Agent / MicroVM**: `qa-integration/scripts/pmm-ui-login.sh ` (playwright-cli); **local IDE**: Basic Auth via Playwright MCP. Never use the Grafana login form. - `/pomRules`: Use this to create a new Page Object Model (POM) or update an existing POM. - `/report`: Rules for the LLM to generate a handoff report that summarizes actions performed and findings. - `/workflowIndex`: This is the entry point for LLM. It guides the agent on which workflow to pick based on your current task. diff --git a/.agents/workflows/mcpRules.md b/.agents/workflows/mcpRules.md index d05f337bd..d1d88ad59 100644 --- a/.agents/workflows/mcpRules.md +++ b/.agents/workflows/mcpRules.md @@ -12,9 +12,9 @@ description: Execution, browser, and locator strategy rules for PMM Playwright t - Check `apiIndex.md` first for routes. Open `pmmApi.json` ONLY for precise schema definitions. # Login -- Follow `pmmLogin.md`. -- Basic Auth headers ONLY. -- NEVER use the UI login form or `/graph/login` (except when debugging auth). +- Follow `pmmLogin.md` — pick **playwright-cli** (Cloud Agent / MicroVM) vs **Playwright MCP** (local IDE). +- NEVER use the Grafana **UI login form**. +- Programmatic auth only (Basic header + optional `POST /graph/login` via `pmm-ui-login.sh` on MicroVM). # Browser interactions - Batch multiple actions (fills, clicks, asserts) into one `mcp_playwright_browser_run_code` call. diff --git a/.agents/workflows/pmmLogin.md b/.agents/workflows/pmmLogin.md index 8965bf6a5..b7434f293 100644 --- a/.agents/workflows/pmmLogin.md +++ b/.agents/workflows/pmmLogin.md @@ -1,21 +1,92 @@ --- -description: PMM Login using basic Auth headers +description: PMM UI login — pick the path for your runtime (playwright-cli vs Playwright MCP) --- -- NEVER use UI login form. -- Use Basic Auth header via `mcp_playwright_browser_run_code`. -- DO NOT pass plain credentials in the URL string. +# Which login path? + +| Runtime | When | How | +|---------|------|-----| +| **Cloud Agent / MicroVM** | Cursor automation, manual QA on MicroVM, `playwright-cli` on PATH | [playwright-cli](#playwright-cli-cloud-agent--microvm) below **or** run `qa-integration/scripts/pmm-ui-login.sh ` | +| **Local IDE + Playwright MCP** | VS Code / Antigravity with `@playwright/mcp` | [Playwright MCP](#playwright-mcp-local-ide) below | + +**Preconditions (both paths)** + +- Confirm `PMM_URL` (default MicroVM: `https://127.0.0.1`). +- Confirm current `ADMIN_PASSWORD` (default before mutation: `pmm3admin!`). +- **Do not** assume defaults after password change — recompute Basic token from `admin:`. +- Default Basic token before mutation: `admin` / `pmm3admin!` → `YWRtaW46cG1tM2FkbWluIQ==`. + +**Never** fill the Grafana login form in the browser. Use programmatic auth only. + +--- + +## playwright-cli (Cloud Agent / MicroVM) + +Preferred: one-shot script (session name `pmm-`): + +```bash +export PMM_URL='https://127.0.0.1' +export ADMIN_PASSWORD='pmm3admin!' # must match provision-pmm.sh / pmm-framework.py +qa-integration/scripts/pmm-ui-login.sh PMM-14576 +``` + +Follow-up UI commands reuse the same session: + +```bash +playwright-cli -s=pmm-PMM-14576 snapshot +playwright-cli -s=pmm-PMM-14576 click e21 +``` + +Manual equivalent (if you cannot run the script): + +```bash +export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS=1 +TICKET=PMM-14576 +PMM_URL="${PMM_URL:-https://127.0.0.1}" +PASSWORD="${ADMIN_PASSWORD:-pmm3admin!}" +AUTH="$(printf 'admin:%s' "$PASSWORD" | base64 -w0 2>/dev/null || printf 'admin:%s' "$PASSWORD" | base64)" + +playwright-cli -s="pmm-${TICKET}" open "$PMM_URL" +playwright-cli -s="pmm-${TICKET}" run-code "async page => { + const base = page.url().match(/^https?:\\/\\/[^/]+/)[0]; + await page.setExtraHTTPHeaders({ Authorization: 'Basic ${AUTH}' }); + await page.route('**/api/user/auth-tokens/rotate', r => r.fulfill({ status: 200, contentType: 'application/json', body: '{}' })); + await page.route('**/v1/users/me', r => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ alerting_tour_completed: true, product_tour_completed: true, snoozed_pmm_version: '', user_id: 1 }) })); + await page.route('**/v1/server/updates?force=**', r => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ installed: {}, last_check: new Date().toISOString(), latest: {}, update_available: false }) })); + const res = await page.request.post(base + '/graph/login', { data: { user: 'admin', password: '${PASSWORD}' } }); + if (res.status() >= 400) throw new Error('Login failed: HTTP ' + res.status() + ' ' + await res.text()); + await page.goto(base + '/pmm-ui/help'); +}" +``` + +Success → reply `Done` and continue UI work in the same `-s=pmm-` session. + +--- + +## Playwright MCP (local IDE) + +Use Basic Auth headers via `mcp_playwright_browser_run_code`. Replace password if mutated. ```javascript async (page) => { - const base = "https://127.0.0.1"; - const auth = Buffer.from("admin:admin").toString("base64"); + const base = process.env.PMM_URL || "https://127.0.0.1"; + const password = process.env.ADMIN_PASSWORD || "pmm3admin!"; + const auth = Buffer.from(`admin:${password}`).toString("base64"); await page.context().setExtraHTTPHeaders({ Authorization: `Basic ${auth}` }); await page.route("**/api/user/auth-tokens/rotate", async (route) => { + await route.fulfill({ body: "{}", contentType: "application/json", status: 200 }); + }); + + await page.route("**/v1/users/me", async (route) => { await route.fulfill({ - body: "{}", + body: JSON.stringify({ + alerting_tour_completed: true, + product_tour_completed: true, + snoozed_pmm_version: "", + user_id: 1, + }), contentType: "application/json", status: 200, }); @@ -25,4 +96,6 @@ async (page) => { }; ``` -- Reply `Done` immediately after logging in. NO extra info. +MCP server must include `--ignore-https-errors` (see `.agents/README.md`). + +Success → reply `Done` and continue with batched MCP browser actions per `mcpRules.md`. diff --git a/.agents/workflows/workflowIndex.md b/.agents/workflows/workflowIndex.md index 88c9dfbaf..1b2a8e6fb 100644 --- a/.agents/workflows/workflowIndex.md +++ b/.agents/workflows/workflowIndex.md @@ -5,7 +5,7 @@ description: Entry point for PMM LLM + Playwright workflow selection # Workflow Mappings | File | Slash | Open when… | |---|---|---| -| `pmmLogin.md` | `/pmmLogin` | Needs auth/session setup. | +| `pmmLogin.md` | `/pmmLogin` | Needs PMM UI auth — Cloud Agent: `pmm-ui-login.sh`; local IDE: Basic Auth MCP. | | `mcpRules.md` | `/mcpRules` | Core execution strategy (READ THIS FIRST). | | `pomRules.md` | `/pomRules` | Needs POM creation/updates. | | `report.md` | `/report` | Needs handoff report or blocked exploratory task. | diff --git a/AGENTS.md b/AGENTS.md index a25f77435..4c0a079b0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,6 +34,7 @@ Each test suite has its own dependency manifest, lint config and runner. **Read | [package_tests/](package_tests/) | Ansible playbooks for OS-level pmm-client install + upgrade (deb/rpm/tarball, auth modes, custom path/port, GSSAPI) | [pmm3-client_integration.yml](package_tests/pmm3-client_integration.yml) | | [k8s/](k8s/) | BATS helm-chart smoke + functional tests against a local Kubernetes cluster | [helm-test.bats](k8s/helm-test.bats) | | [support_scripts/](support_scripts/) | Ad-hoc Python helpers for manual / CI debugging (not part of any suite) | [agent_status.py](support_scripts/agent_status.py) · [check_client_upgrade.py](support_scripts/check_client_upgrade.py) · [check_upgrade.py](support_scripts/check_upgrade.py) | +| [.agents/](.agents/) | Agent workflows (login, MCP rules, API index, bug reports) for Cursor / IDE automation | [.agents/README.md](.agents/README.md) · start with `/workflowIndex` or `/pmmLogin` | | [.github/workflows/](.github/workflows/) | GitHub Actions pipelines | See [CI / Pipeline Map](#ci--pipeline-map) below | ## Cross-Suite Architecture diff --git a/qa-integration/MANUAL-QA-MICROVM.md b/qa-integration/MANUAL-QA-MICROVM.md index 3c3adc953..4ab3c8f65 100644 --- a/qa-integration/MANUAL-QA-MICROVM.md +++ b/qa-integration/MANUAL-QA-MICROVM.md @@ -48,11 +48,16 @@ Pick `--database` from test plan + `pmm_qa/scripts/database_options.py`. Example ## 3. UI (playwright-cli) +**Do not** open `/graph/login` and type credentials — use the login script or `/pmmLogin` workflow. + ```bash -export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS=true -playwright-cli open https://127.0.0.1/graph/login +export PMM_URL='https://127.0.0.1' +export ADMIN_PASSWORD='pmm3admin!' # same as step 1 +qa-integration/scripts/pmm-ui-login.sh PMM- ``` +Reuse session `pmm-PMM-` for snapshots/clicks. Full details: [.agents/workflows/pmmLogin.md](../.agents/workflows/pmmLogin.md). + ## 4. Reset ```bash @@ -73,4 +78,5 @@ If still blocked, report BLOCKED and link Jenkins `pmm3-aws-staging-start` param | `scripts/lib/wait-pmm-ready.sh` | readyz waiter | | `scripts/cleanup-pmm-microvm.sh` | teardown | | `scripts/start-docker-microvm.sh` | dockerd on MicroVM | +| `scripts/pmm-ui-login.sh` | PMM UI login via playwright-cli (session `pmm-`) | | `pmm_psmdb-pbm_setup/start-rs-only-microvm.sh` | called via pmm-framework when `PMM_QA_NO_SYSTEMD=1` | diff --git a/qa-integration/scripts/pmm-ui-login.sh b/qa-integration/scripts/pmm-ui-login.sh new file mode 100755 index 000000000..31df2edb0 --- /dev/null +++ b/qa-integration/scripts/pmm-ui-login.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Log into PMM UI via playwright-cli (Cloud Agent / MicroVM). +# Avoids the Grafana login form and self-signed TLS issues. +# +# Usage: +# export PMM_URL='https://127.0.0.1' # optional, default below +# export ADMIN_PASSWORD='pmm3admin!' # optional; match provision-pmm.sh +# qa-integration/scripts/pmm-ui-login.sh PMM-14576 +# +# Session name: pmm- (reuse the same id for follow-up UI commands). +set -euo pipefail + +SESSION_ID="${1:?usage: $0 e.g. PMM-14576}" +PMM_URL="${PMM_URL:-https://127.0.0.1}" +ADMIN_PASSWORD="${ADMIN_PASSWORD:-pmm3admin!}" +PLAYWRIGHT_SESSION="pmm-${SESSION_ID}" + +export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS="${PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS:-1}" + +if ! command -v playwright-cli >/dev/null 2>&1; then + echo "ERROR: playwright-cli not found on PATH" >&2 + exit 1 +fi + +AUTH_TOKEN="$(printf 'admin:%s' "$ADMIN_PASSWORD" | base64 -w0 2>/dev/null || printf 'admin:%s' "$ADMIN_PASSWORD" | base64)" + +OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open "$PMM_URL") +if [ "${PMM_UI_HEADED:-0}" = "1" ]; then + OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open --headed "$PMM_URL") +fi + +playwright-cli "${OPEN_ARGS[@]}" + +# shellcheck disable=SC2016 +playwright-cli -s="$PLAYWRIGHT_SESSION" run-code "async page => { + const base = page.url().match(/^https?:\\/\\/[^/]+/)[0]; + await page.setExtraHTTPHeaders({ Authorization: 'Basic ${AUTH_TOKEN}' }); + await page.route('**/api/user/auth-tokens/rotate', r => r.fulfill({ status: 200, contentType: 'application/json', body: '{}' })); + await page.route('**/v1/users/me', r => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ alerting_tour_completed: true, product_tour_completed: true, snoozed_pmm_version: '', user_id: 1 }) })); + await page.route('**/v1/server/updates?force=**', r => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ installed: {}, last_check: new Date().toISOString(), latest: {}, update_available: false }) })); + const res = await page.request.post(base + '/graph/login', { data: { user: 'admin', password: '${ADMIN_PASSWORD}' } }); + if (res.status() >= 400) throw new Error('Login failed: HTTP ' + res.status() + ' ' + await res.text()); + await page.goto(base + '/pmm-ui/help'); +}" + +echo "PMM UI login OK (session=${PLAYWRIGHT_SESSION}, url=${PMM_URL})" From 575f8fc331f79e2f5a8b855ca18ae86c75191d27 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 17:30:25 +0000 Subject: [PATCH 08/83] Print pmm-ui-login.sh hint after provision-pmm.sh completes Co-authored-by: davi.travaglia --- qa-integration/scripts/provision-pmm.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qa-integration/scripts/provision-pmm.sh b/qa-integration/scripts/provision-pmm.sh index f767dfc57..ebb7f3c44 100755 --- a/qa-integration/scripts/provision-pmm.sh +++ b/qa-integration/scripts/provision-pmm.sh @@ -131,11 +131,15 @@ cat <) User: admin Password: ${ADMIN_PASSWORD} readyz: https://127.0.0.1/v1/server/readyz (expect HTTP 200, body {}) + # UI session (after databases are up): + # export PMM_URL='https://127.0.0.1' ADMIN_PASSWORD='${ADMIN_PASSWORD}' + # ${SCRIPT_DIR}/pmm-ui-login.sh PMM- + Next — provision monitored databases for your ticket (examples): cd ${PMM_QA_DIR} From b4756d10e90081a6d82b837e93ceb7dcc1a25171 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 17:43:23 +0000 Subject: [PATCH 09/83] Default pmm-ui-login.sh to headed mode for screen recordings Use --headed on playwright-cli open by default. Set PMM_UI_HEADED=0 to run headless when needed. Co-authored-by: davi.travaglia --- .agents/workflows/pmmLogin.md | 4 +++- qa-integration/scripts/pmm-ui-login.sh | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.agents/workflows/pmmLogin.md b/.agents/workflows/pmmLogin.md index b7434f293..43e4ba711 100644 --- a/.agents/workflows/pmmLogin.md +++ b/.agents/workflows/pmmLogin.md @@ -37,6 +37,8 @@ playwright-cli -s=pmm-PMM-14576 snapshot playwright-cli -s=pmm-PMM-14576 click e21 ``` +Headed by default (screen recordings). Opt out: `PMM_UI_HEADED=0 qa-integration/scripts/pmm-ui-login.sh `. + Manual equivalent (if you cannot run the script): ```bash @@ -46,7 +48,7 @@ PMM_URL="${PMM_URL:-https://127.0.0.1}" PASSWORD="${ADMIN_PASSWORD:-pmm3admin!}" AUTH="$(printf 'admin:%s' "$PASSWORD" | base64 -w0 2>/dev/null || printf 'admin:%s' "$PASSWORD" | base64)" -playwright-cli -s="pmm-${TICKET}" open "$PMM_URL" +playwright-cli -s="pmm-${TICKET}" open --headed "$PMM_URL" playwright-cli -s="pmm-${TICKET}" run-code "async page => { const base = page.url().match(/^https?:\\/\\/[^/]+/)[0]; await page.setExtraHTTPHeaders({ Authorization: 'Basic ${AUTH}' }); diff --git a/qa-integration/scripts/pmm-ui-login.sh b/qa-integration/scripts/pmm-ui-login.sh index 31df2edb0..b916126ad 100755 --- a/qa-integration/scripts/pmm-ui-login.sh +++ b/qa-integration/scripts/pmm-ui-login.sh @@ -8,6 +8,7 @@ # qa-integration/scripts/pmm-ui-login.sh PMM-14576 # # Session name: pmm- (reuse the same id for follow-up UI commands). +# Opens headed by default (better for screen recordings). Set PMM_UI_HEADED=0 for headless. set -euo pipefail SESSION_ID="${1:?usage: $0 e.g. PMM-14576}" @@ -24,9 +25,9 @@ fi AUTH_TOKEN="$(printf 'admin:%s' "$ADMIN_PASSWORD" | base64 -w0 2>/dev/null || printf 'admin:%s' "$ADMIN_PASSWORD" | base64)" -OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open "$PMM_URL") -if [ "${PMM_UI_HEADED:-0}" = "1" ]; then - OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open --headed "$PMM_URL") +OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open --headed "$PMM_URL") +if [ "${PMM_UI_HEADED:-1}" = "0" ]; then + OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open "$PMM_URL") fi playwright-cli "${OPEN_ARGS[@]}" From fe25f324da413e14569e15fa21d27671ca3612e5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 17:44:42 +0000 Subject: [PATCH 10/83] Harden headed browser default for pmm-ui-login.sh Add repo-level .playwright/cli.config.json (headless: false), pass --config from the login script regardless of cwd, and default DISPLAY to :1 so screen recordings capture a visible browser window. Co-authored-by: davi.travaglia --- .agents/workflows/pmmLogin.md | 2 +- .playwright/cli.config.json | 7 +++++++ qa-integration/MANUAL-QA-MICROVM.md | 2 ++ qa-integration/scripts/pmm-ui-login.sh | 10 ++++++++-- 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .playwright/cli.config.json diff --git a/.agents/workflows/pmmLogin.md b/.agents/workflows/pmmLogin.md index 43e4ba711..4c99de1af 100644 --- a/.agents/workflows/pmmLogin.md +++ b/.agents/workflows/pmmLogin.md @@ -37,7 +37,7 @@ playwright-cli -s=pmm-PMM-14576 snapshot playwright-cli -s=pmm-PMM-14576 click e21 ``` -Headed by default (screen recordings). Opt out: `PMM_UI_HEADED=0 qa-integration/scripts/pmm-ui-login.sh `. +Headed by default (screen recordings). Repo-wide default: `.playwright/cli.config.json` (`headless: false`). Opt out: `PMM_UI_HEADED=0 qa-integration/scripts/pmm-ui-login.sh `. Manual equivalent (if you cannot run the script): diff --git a/.playwright/cli.config.json b/.playwright/cli.config.json new file mode 100644 index 000000000..a165f30ca --- /dev/null +++ b/.playwright/cli.config.json @@ -0,0 +1,7 @@ +{ + "browser": { + "launchOptions": { + "headless": false + } + } +} diff --git a/qa-integration/MANUAL-QA-MICROVM.md b/qa-integration/MANUAL-QA-MICROVM.md index 4ab3c8f65..5758c676d 100644 --- a/qa-integration/MANUAL-QA-MICROVM.md +++ b/qa-integration/MANUAL-QA-MICROVM.md @@ -56,6 +56,8 @@ export ADMIN_PASSWORD='pmm3admin!' # same as step 1 qa-integration/scripts/pmm-ui-login.sh PMM- ``` +Opens a **headed** browser by default (visible on `:1` for screen recordings). Opt out: `PMM_UI_HEADED=0`. Repo config: `.playwright/cli.config.json`. + Reuse session `pmm-PMM-` for snapshots/clicks. Full details: [.agents/workflows/pmmLogin.md](../.agents/workflows/pmmLogin.md). ## 4. Reset diff --git a/qa-integration/scripts/pmm-ui-login.sh b/qa-integration/scripts/pmm-ui-login.sh index b916126ad..eac17dd32 100755 --- a/qa-integration/scripts/pmm-ui-login.sh +++ b/qa-integration/scripts/pmm-ui-login.sh @@ -11,12 +11,17 @@ # Opens headed by default (better for screen recordings). Set PMM_UI_HEADED=0 for headless. set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +PW_CONFIG="${REPO_ROOT}/.playwright/cli.config.json" + SESSION_ID="${1:?usage: $0 e.g. PMM-14576}" PMM_URL="${PMM_URL:-https://127.0.0.1}" ADMIN_PASSWORD="${ADMIN_PASSWORD:-pmm3admin!}" PLAYWRIGHT_SESSION="pmm-${SESSION_ID}" export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS="${PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS:-1}" +export DISPLAY="${DISPLAY:-:1}" if ! command -v playwright-cli >/dev/null 2>&1; then echo "ERROR: playwright-cli not found on PATH" >&2 @@ -25,15 +30,16 @@ fi AUTH_TOKEN="$(printf 'admin:%s' "$ADMIN_PASSWORD" | base64 -w0 2>/dev/null || printf 'admin:%s' "$ADMIN_PASSWORD" | base64)" +PW_CLI=(playwright-cli --config="$PW_CONFIG") OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open --headed "$PMM_URL") if [ "${PMM_UI_HEADED:-1}" = "0" ]; then OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open "$PMM_URL") fi -playwright-cli "${OPEN_ARGS[@]}" +"${PW_CLI[@]}" "${OPEN_ARGS[@]}" # shellcheck disable=SC2016 -playwright-cli -s="$PLAYWRIGHT_SESSION" run-code "async page => { +"${PW_CLI[@]}" -s="$PLAYWRIGHT_SESSION" run-code "async page => { const base = page.url().match(/^https?:\\/\\/[^/]+/)[0]; await page.setExtraHTTPHeaders({ Authorization: 'Basic ${AUTH_TOKEN}' }); await page.route('**/api/user/auth-tokens/rotate', r => r.fulfill({ status: 200, contentType: 'application/json', body: '{}' })); From 295b964b3edfab953402079194300bf10394739a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 17:47:13 +0000 Subject: [PATCH 11/83] Maximize browser window for PMM UI login recordings Use --start-maximized and viewport: null in cli.config.json, and resize to 1920x1200 after open so headed sessions fill the MicroVM display. Co-authored-by: davi.travaglia --- .agents/workflows/pmmLogin.md | 2 +- .playwright/cli.config.json | 6 +++++- qa-integration/MANUAL-QA-MICROVM.md | 2 +- qa-integration/scripts/pmm-ui-login.sh | 6 +++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.agents/workflows/pmmLogin.md b/.agents/workflows/pmmLogin.md index 4c99de1af..6a4d205c3 100644 --- a/.agents/workflows/pmmLogin.md +++ b/.agents/workflows/pmmLogin.md @@ -37,7 +37,7 @@ playwright-cli -s=pmm-PMM-14576 snapshot playwright-cli -s=pmm-PMM-14576 click e21 ``` -Headed by default (screen recordings). Repo-wide default: `.playwright/cli.config.json` (`headless: false`). Opt out: `PMM_UI_HEADED=0 qa-integration/scripts/pmm-ui-login.sh `. +Headed + maximized by default (screen recordings). Repo config: `.playwright/cli.config.json` (`headless: false`, `--start-maximized`, `viewport: null`). Opt out of headed: `PMM_UI_HEADED=0`. Override window size: `PMM_UI_WIDTH` / `PMM_UI_HEIGHT`. Manual equivalent (if you cannot run the script): diff --git a/.playwright/cli.config.json b/.playwright/cli.config.json index a165f30ca..353bdfbf4 100644 --- a/.playwright/cli.config.json +++ b/.playwright/cli.config.json @@ -1,7 +1,11 @@ { "browser": { "launchOptions": { - "headless": false + "headless": false, + "args": ["--start-maximized"] + }, + "contextOptions": { + "viewport": null } } } diff --git a/qa-integration/MANUAL-QA-MICROVM.md b/qa-integration/MANUAL-QA-MICROVM.md index 5758c676d..3a69f2ab8 100644 --- a/qa-integration/MANUAL-QA-MICROVM.md +++ b/qa-integration/MANUAL-QA-MICROVM.md @@ -56,7 +56,7 @@ export ADMIN_PASSWORD='pmm3admin!' # same as step 1 qa-integration/scripts/pmm-ui-login.sh PMM- ``` -Opens a **headed** browser by default (visible on `:1` for screen recordings). Opt out: `PMM_UI_HEADED=0`. Repo config: `.playwright/cli.config.json`. +Opens a **headed, maximized** browser by default (visible on `:1` for screen recordings). Opt out of headed: `PMM_UI_HEADED=0`. Override size: `PMM_UI_WIDTH` / `PMM_UI_HEIGHT` (default 1920×1200). Repo config: `.playwright/cli.config.json`. Reuse session `pmm-PMM-` for snapshots/clicks. Full details: [.agents/workflows/pmmLogin.md](../.agents/workflows/pmmLogin.md). diff --git a/qa-integration/scripts/pmm-ui-login.sh b/qa-integration/scripts/pmm-ui-login.sh index eac17dd32..3f625a01e 100755 --- a/qa-integration/scripts/pmm-ui-login.sh +++ b/qa-integration/scripts/pmm-ui-login.sh @@ -8,7 +8,7 @@ # qa-integration/scripts/pmm-ui-login.sh PMM-14576 # # Session name: pmm- (reuse the same id for follow-up UI commands). -# Opens headed by default (better for screen recordings). Set PMM_UI_HEADED=0 for headless. +# Opens headed + maximized by default (better for screen recordings). Set PMM_UI_HEADED=0 for headless. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -38,6 +38,10 @@ fi "${PW_CLI[@]}" "${OPEN_ARGS[@]}" +PMM_UI_WIDTH="${PMM_UI_WIDTH:-1920}" +PMM_UI_HEIGHT="${PMM_UI_HEIGHT:-1200}" +"${PW_CLI[@]}" -s="$PLAYWRIGHT_SESSION" resize "$PMM_UI_WIDTH" "$PMM_UI_HEIGHT" >/dev/null 2>&1 || true + # shellcheck disable=SC2016 "${PW_CLI[@]}" -s="$PLAYWRIGHT_SESSION" run-code "async page => { const base = page.url().match(/^https?:\\/\\/[^/]+/)[0]; From 9bc8cb368de32cd1357d8efef0586f0c8d6a5509 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 3 Jul 2026 12:12:26 +0000 Subject: [PATCH 12/83] Make pmm-framework setups work on MicroVM (batch 1) - Add SETUP-INVENTORY.md cataloging all 31 pmm-framework.py variants - PSMDB sharding: docker-compose-sharded.microvm.yaml + start-sharded-microvm.sh - PMM_QA_NO_SYSTEMD path: ubuntu base containers for PS/MySQL/haproxy/external - Fix create_minio_container.yml YAML, docker-py pin, ansible interpreter env - Prefer pmm-server container name for pmm-agent registration - Fix wait-pmm-ready.sh missing body file on first curl failure Co-authored-by: davi.travaglia --- qa-integration/SETUP-INVENTORY.md | 76 +++++ .../docker-compose-sharded.microvm.yaml | 87 ++++++ .../scripts/compose-env.sh | 8 + .../start-sharded-microvm.sh | 268 ++++++++++++++++++ qa-integration/pmm_qa/external_setup.yml | 15 +- qa-integration/pmm_qa/haproxy_setup.yml | 30 +- qa-integration/pmm_qa/mysql/mysql-setup.yml | 2 + .../mysql/tasks/prepare_install_mysql.yml | 57 +++- .../percona-server-setup.yml | 2 + .../tasks/prepare_install_ps.yml | 56 +++- qa-integration/pmm_qa/pmm-framework.py | 45 +-- qa-integration/pmm_qa/requirements.txt | 1 + .../pmm_qa/scripts/run_ansible_playbook.py | 7 + .../pmm_qa/tasks/create_minio_container.yml | 4 +- .../pmm_qa/tasks/install_pmm_client.yml | 9 +- .../pmm_qa/tasks/microvm_container_facts.yml | 19 ++ qa-integration/scripts/cleanup-pmm-microvm.sh | 5 +- qa-integration/scripts/lib/wait-pmm-ready.sh | 1 + 18 files changed, 643 insertions(+), 49 deletions(-) create mode 100644 qa-integration/SETUP-INVENTORY.md create mode 100644 qa-integration/pmm_psmdb-pbm_setup/docker-compose-sharded.microvm.yaml create mode 100755 qa-integration/pmm_psmdb-pbm_setup/start-sharded-microvm.sh create mode 100644 qa-integration/pmm_qa/tasks/microvm_container_facts.yml diff --git a/qa-integration/SETUP-INVENTORY.md b/qa-integration/SETUP-INVENTORY.md new file mode 100644 index 000000000..408740aef --- /dev/null +++ b/qa-integration/SETUP-INVENTORY.md @@ -0,0 +1,76 @@ +# pmm-framework.py — setup inventory (MicroVM target) + +Every database type and meaningful variant exposed via `--database`. +Format: `python pmm-framework.py --database [,SETUP_TYPE=][,