From ab78277ba7e8e7a0f89ee1942b68bba7468a187e Mon Sep 17 00:00:00 2001 From: Peter Sirotnak Date: Mon, 27 Jul 2026 10:46:07 +0200 Subject: [PATCH 1/3] PMM-7: Github images POC --- .../build_qa_framework_docker_image.yml | 189 ++++++++++++++++++ .../percona_server_for_mysql/Dockerfile | 56 ++++++ 2 files changed, 245 insertions(+) create mode 100644 .github/workflows/build_qa_framework_docker_image.yml create mode 100644 qa_framework/docker_images/percona_server_for_mysql/Dockerfile diff --git a/.github/workflows/build_qa_framework_docker_image.yml b/.github/workflows/build_qa_framework_docker_image.yml new file mode 100644 index 000000000..7756af38b --- /dev/null +++ b/.github/workflows/build_qa_framework_docker_image.yml @@ -0,0 +1,189 @@ +name: Build and Push Docker Image for PMM QA Framework + +on: + push: + branches: [ main ] + workflow_dispatch: + inputs: + image: + description: 'Which image to build' + required: true + type: choice + options: + - percona-server-mysql + - mysql-community + - percona-distribution-postgresql + - postgresql-community + - percona-server-mongodb + - mongodb + - valkey + version: + description: 'Version to tag (e.g. 1.11.0). Leave empty to auto-bump minor.' + required: false + default: '' + db_version: + description: 'Database version to build/tag (e.g. 8.0, 8.4, 5.7). Applies to any image.' + required: false + default: '' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: percona/pmm-qa + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve image config + id: cfg + run: | + BASE="qa_framework/docker_images" + case "${{ github.event.inputs.image }}" in + percona-server-mysql) + NAME="percona-server-mysql" + DIR="$BASE/percona_server_for_mysql" + ;; + mysql-community) + NAME="mysql-community" + DIR="$BASE/mysql-community" + ;; + percona-distribution-postgresql) + NAME="percona-distribution-postgresql" + DIR="$BASE/percona-distribution-postgresql" + ;; + postgresql-community) + NAME="postgresql-community" + DIR="$BASE/postgresql-community" + ;; + percona-server-mongodb) + NAME="percona-server-mongodb" + DIR="$BASE/percona-server-mongodb" + ;; + mongodb) + NAME="mongodb" + DIR="$BASE/mongodb" + ;; + valkey) + NAME="valkey" + DIR="$BASE/valkey" + ;; + *) + echo "Unknown image: ${{ github.event.inputs.image }}" + exit 1 + ;; + esac + FILE="$DIR/Dockerfile" + CONTEXT="$DIR" + echo "name=$NAME" >> "$GITHUB_OUTPUT" + echo "file=$FILE" >> "$GITHUB_OUTPUT" + echo "context=$CONTEXT" >> "$GITHUB_OUTPUT" + + # Version handling: db_version is free-text and usable by any image. + # Every image receives DB_VERSION as a build arg (Dockerfiles may ignore it); + # percona-server-mysql additionally derives its repo/base/package args. + DBV="${{ github.event.inputs.db_version }}" + FLAVOR="$DBV" + { + echo "build_args<> "$GITHUB_OUTPUT" + echo "flavor=$FLAVOR" >> "$GITHUB_OUTPUT" + echo "Resolved: image=$NAME file=$FILE context=$CONTEXT flavor=${FLAVOR:-none}" + + - name: Determine version + id: version + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + INPUT="${{ github.event.inputs.version }}" + if [ -n "$INPUT" ]; then + VERSION="$INPUT" + echo "Using provided version: $VERSION" + else + ORG="${GITHUB_REPOSITORY%/*}" # percona + PKG="${GITHUB_REPOSITORY#*/}" # pmm-qa + + # Page through all published versions, extract every tag, + # keep only strict X.Y.Z, pick the highest. + page=1 + all_tags="" + while :; do + resp=$(gh api \ + -H "Accept: application/vnd.github+json" \ + "/orgs/${ORG}/packages/container/${PKG}/versions?per_page=100&page=${page}" \ + 2>/dev/null || echo "[]") + # stop on empty page or error/no-package + count=$(echo "$resp" | jq 'if type=="array" then length else 0 end') + [ "$count" -eq 0 ] && break + tags=$(echo "$resp" | jq -r '.[].metadata.container.tags[]?') + all_tags="${all_tags}"$'\n'"${tags}" + page=$((page + 1)) + done + + LATEST=$(echo "$all_tags" \ + | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \ + | sort -V | tail -n1) + LATEST=${LATEST:-0.0.0} + MAJOR=$(echo "$LATEST" | cut -d. -f1) + MINOR=$(echo "$LATEST" | cut -d. -f2) + VERSION="${MAJOR}.$((MINOR + 1)).0" + echo "Highest published: $LATEST -> new: $VERSION" + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Assemble tags + id: tags + run: | + IMG="${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ steps.cfg.outputs.name }}" + VER="${{ steps.version.outputs.version }}" + FLAVOR="${{ steps.cfg.outputs.flavor }}" + { + echo "tags<> "$GITHUB_OUTPUT" + + - name: Set up Docker Build + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: ${{ steps.cfg.outputs.context }} + file: ${{ steps.cfg.outputs.file }} + push: true + build-args: ${{ steps.cfg.outputs.build_args }} + tags: ${{ steps.tags.outputs.tags }} diff --git a/qa_framework/docker_images/percona_server_for_mysql/Dockerfile b/qa_framework/docker_images/percona_server_for_mysql/Dockerfile new file mode 100644 index 000000000..cb70afb44 --- /dev/null +++ b/qa_framework/docker_images/percona_server_for_mysql/Dockerfile @@ -0,0 +1,56 @@ +# Percona Server for MySQL — QA image (systemd), based on qa-integration/pmm_qa +# qa-integration/pmm_qa/percona_server_for_mysql +# +# Reproduces the same container the pmm-qa Ansible framework builds, WITHOUT the +# PMM client. A single image acts as any node in three topologies, selected at +# runtime via the SETUP_TYPE env var: +# - single : standalone instance (my.cnf.j2) +# - replication : async replication (my-async-replication.cnf.j2) +# - gr : group replication (my-group-replication.cnf.j2) +# +# The entrypoint (ps-node-setup.sh, run by a systemd oneshot unit) renders the +# matching my.cnf from env vars, initializes the datadir, sets the root password, +# and wires up replication for the node's role. +# +# ---- Version selection (build args) ---- +# 8.0 (default): docker build -t ps-qa:8.0 . +# 8.4: docker build -t ps-qa:8.4 --build-arg PS_REPO=ps-84-lts . +# 5.7: docker build -t ps-qa:5.7 \ +# --build-arg BASE_IMAGE=antmelekhin/docker-systemd:ubuntu-22.04 \ +# --build-arg PS_REPO=ps-57 \ +# --build-arg PS_PKG_SUFFIX=-5.7 . +ARG BASE_IMAGE=antmelekhin/docker-systemd:ubuntu-24.04 +FROM ${BASE_IMAGE} + +# ps-80 | ps-84-lts | ps-57 (percona-release channel) +ARG PS_REPO=ps-80 +# "" for 8.x, "-5.7" for 5.7 package names +ARG PS_PKG_SUFFIX= + +ENV DEBIAN_FRONTEND=noninteractive + +# Dependencies + Percona repo + Percona Server + RocksDB + sysbench +# (mirrors prepare_install_ps.yml and the sysbench step in percona-server-setup.yml) +RUN apt-get update \ + && apt-get install -y wget gnupg2 lsb-release curl \ + && wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb \ + && dpkg -i percona-release_latest.generic_all.deb \ + && rm -f percona-release_latest.generic_all.deb \ + && percona-release enable ${PS_REPO} release \ + && apt-get update \ + && apt-get install -y \ + percona-server-server${PS_PKG_SUFFIX} \ + percona-server-rocksdb${PS_PKG_SUFFIX} \ + sysbench \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +COPY ps-node-setup.sh /usr/local/bin/ps-node-setup.sh +COPY ps-node-setup.service /etc/systemd/system/ps-node-setup.service + +# Don't let mysql auto-start before the config is rendered; the setup unit starts it. +RUN chmod +x /usr/local/bin/ps-node-setup.sh \ + && systemctl disable mysql 2>/dev/null || true \ + && systemctl enable ps-node-setup.service + +# 3306 = MySQL, 34061 = group replication communication (XCOM) +EXPOSE 3306 34061 From 0a65d39f1ac134f0282e18599f8b4e47a4af7c63 Mon Sep 17 00:00:00 2001 From: Peter Sirotnak Date: Mon, 27 Jul 2026 10:51:25 +0200 Subject: [PATCH 2/3] PMM-7: Github images POC --- .../build_qa_framework_docker_image.yml | 57 +------------------ 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/.github/workflows/build_qa_framework_docker_image.yml b/.github/workflows/build_qa_framework_docker_image.yml index 7756af38b..340236407 100644 --- a/.github/workflows/build_qa_framework_docker_image.yml +++ b/.github/workflows/build_qa_framework_docker_image.yml @@ -17,10 +17,6 @@ on: - percona-server-mongodb - mongodb - valkey - version: - description: 'Version to tag (e.g. 1.11.0). Leave empty to auto-bump minor.' - required: false - default: '' db_version: description: 'Database version to build/tag (e.g. 8.0, 8.4, 5.7). Applies to any image.' required: false @@ -109,63 +105,16 @@ jobs: echo "flavor=$FLAVOR" >> "$GITHUB_OUTPUT" echo "Resolved: image=$NAME file=$FILE context=$CONTEXT flavor=${FLAVOR:-none}" - - name: Determine version - id: version - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - INPUT="${{ github.event.inputs.version }}" - if [ -n "$INPUT" ]; then - VERSION="$INPUT" - echo "Using provided version: $VERSION" - else - ORG="${GITHUB_REPOSITORY%/*}" # percona - PKG="${GITHUB_REPOSITORY#*/}" # pmm-qa - - # Page through all published versions, extract every tag, - # keep only strict X.Y.Z, pick the highest. - page=1 - all_tags="" - while :; do - resp=$(gh api \ - -H "Accept: application/vnd.github+json" \ - "/orgs/${ORG}/packages/container/${PKG}/versions?per_page=100&page=${page}" \ - 2>/dev/null || echo "[]") - # stop on empty page or error/no-package - count=$(echo "$resp" | jq 'if type=="array" then length else 0 end') - [ "$count" -eq 0 ] && break - tags=$(echo "$resp" | jq -r '.[].metadata.container.tags[]?') - all_tags="${all_tags}"$'\n'"${tags}" - page=$((page + 1)) - done - - LATEST=$(echo "$all_tags" \ - | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \ - | sort -V | tail -n1) - LATEST=${LATEST:-0.0.0} - MAJOR=$(echo "$LATEST" | cut -d. -f1) - MINOR=$(echo "$LATEST" | cut -d. -f2) - VERSION="${MAJOR}.$((MINOR + 1)).0" - echo "Highest published: $LATEST -> new: $VERSION" - fi - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - name: Assemble tags id: tags run: | IMG="${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ steps.cfg.outputs.name }}" - VER="${{ steps.version.outputs.version }}" FLAVOR="${{ steps.cfg.outputs.flavor }}" + # Two tags only: latest, plus the db_version (flavor) when set. { echo "tags<> "$GITHUB_OUTPUT" From 116cad82d3895ffd1dafbdee23ff76610ef31845 Mon Sep 17 00:00:00 2001 From: Peter Sirotnak Date: Mon, 27 Jul 2026 11:03:23 +0200 Subject: [PATCH 3/3] PMM-7: Github images POC --- .../build_qa_framework_docker_image.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_qa_framework_docker_image.yml b/.github/workflows/build_qa_framework_docker_image.yml index 340236407..a10fca59d 100644 --- a/.github/workflows/build_qa_framework_docker_image.yml +++ b/.github/workflows/build_qa_framework_docker_image.yml @@ -3,6 +3,11 @@ name: Build and Push Docker Image for PMM QA Framework on: push: branches: [ main ] + pull_request: + branches: [ main ] + paths: + - '.github/workflows/build_qa_framework_docker_image.yml' + - 'qa_framework/docker_images/**' workflow_dispatch: inputs: image: @@ -43,7 +48,10 @@ jobs: id: cfg run: | BASE="qa_framework/docker_images" - case "${{ github.event.inputs.image }}" in + # inputs are absent on push / pull_request events -> default the image + IMAGE="${{ github.event.inputs.image }}" + IMAGE="${IMAGE:-percona-server-mysql}" + case "$IMAGE" in percona-server-mysql) NAME="percona-server-mysql" DIR="$BASE/percona_server_for_mysql" @@ -73,7 +81,7 @@ jobs: DIR="$BASE/valkey" ;; *) - echo "Unknown image: ${{ github.event.inputs.image }}" + echo "Unknown image: $IMAGE" exit 1 ;; esac @@ -122,17 +130,19 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to GHCR + # Pull requests only validate the build; no push, so no login needed. + if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push + - name: Build (and push on non-PR events) uses: docker/build-push-action@v6 with: context: ${{ steps.cfg.outputs.context }} file: ${{ steps.cfg.outputs.file }} - push: true + push: ${{ github.event_name != 'pull_request' }} build-args: ${{ steps.cfg.outputs.build_args }} tags: ${{ steps.tags.outputs.tags }}