diff --git a/.github/workflows/test-package-installation.yml b/.github/workflows/test-package-installation.yml new file mode 100644 index 00000000..f2f1b298 --- /dev/null +++ b/.github/workflows/test-package-installation.yml @@ -0,0 +1,93 @@ +name: Test Latest Citus Package Installation on Supported PostgreSQL Versions + +on: + workflow_dispatch: + +env: + # Branch whose build-package.yml, postgres-matrix.yml and pkgvars files are the source of truth + SOURCE_BRANCH: "all-citus" + +jobs: + discover: + name: Discover platforms & versions + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.build_matrix.outputs.matrix }} + citus_version: ${{ steps.resolve_versions.outputs.citus_version }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch source branch + run: git fetch origin "${SOURCE_BRANCH}" --depth=1 + + - name: Extract platform list + id: extract_platforms + run: | + git show "origin/${SOURCE_BRANCH}:.github/workflows/build-package.yml" \ + > /tmp/build-package.yml + + platforms=$( + yq -o=json '.jobs.build_package.strategy.matrix.platform' /tmp/build-package.yml \ + | jq -c '.' + ) + + echo "Discovered platforms: ${platforms}" + echo "platforms=${platforms}" >> "$GITHUB_OUTPUT" + + - name: Resolve Citus & PG versions + id: resolve_versions + run: | + git show "origin/${SOURCE_BRANCH}:pkgvars" > /tmp/pkgvars + git show "origin/${SOURCE_BRANCH}:postgres-matrix.yml" > /tmp/postgres-matrix.yml + + # Extract version from pkglatest (e.g. "14.0.0.citus-1" -> "14.0.0") + citus_version=$(grep '^pkglatest=' /tmp/pkgvars | sed 's/^pkglatest=//;s/\.citus-.*//') + + # postgres-matrix.yml keys use major.minor (e.g. "14.0"), strip patch + citus_major_minor=$(echo "${citus_version}" | cut -d. -f1,2) + + pg_versions=$( + yq -o=json \ + ".version_matrix[-1].\"${citus_major_minor}\".postgres_versions" \ + /tmp/postgres-matrix.yml \ + | jq -c '[.[] | tostring]' + ) + + echo "Latest Citus version: ${citus_version}" + echo "Supported PG versions: ${pg_versions}" + echo "citus_version=${citus_version}" >> "$GITHUB_OUTPUT" + echo "pg_versions=${pg_versions}" >> "$GITHUB_OUTPUT" + + - name: Build matrix + id: build_matrix + run: | + matrix=$(jq -cn \ + --argjson platforms '${{ steps.extract_platforms.outputs.platforms }}' \ + --argjson pg_versions '${{ steps.resolve_versions.outputs.pg_versions }}' \ + '{"platform": $platforms, "pg_version": $pg_versions}') + echo "Matrix: ${matrix}" + echo "matrix=${matrix}" >> "$GITHUB_OUTPUT" + + test_package: + name: "PG${{ matrix.pg_version }}/Citus${{ needs.discover.outputs.citus_version }} - ${{ matrix.platform }}" + needs: discover + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ">=3.10" + + - name: Run package installation test + run: | + python3 test_package_installation/run_test.py \ + "${{ matrix.platform }}" \ + "${{ matrix.pg_version }}" \ + "${{ needs.discover.outputs.citus_version }}" diff --git a/.gitignore b/.gitignore index 44cb9171..d6d9d9ad 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ # package output directory /packages + +.vscode diff --git a/test_package_installation/README.md b/test_package_installation/README.md new file mode 100644 index 00000000..417505c8 --- /dev/null +++ b/test_package_installation/README.md @@ -0,0 +1,35 @@ +# Package Installation Tests + +Verifies that Citus packages can be installed from the package repository on all supported platforms. + +Each test: +* spins up a Docker container for the target OS +* installs the PostgreSQL and Citus packages +* sets up a cluster and runs very basic tests +* runs `make check-multi` regression tests from the Citus repository against the installed Citus version + +## Running locally + +Requires **Python 3.10+** and **Docker**. No additional Python packages needed. + +```bash +python3 test_package_installation/run_test.py +``` + +Examples: + +```bash +python3 test_package_installation/run_test.py ubuntu/noble 18 14.0.0 +python3 test_package_installation/run_test.py el/9 17 14.0.0 +python3 test_package_installation/run_test.py debian/bookworm 16 14.0.0 +``` + +## Running via GitHub Actions + +You can manually trigger the workflow using [this GitHub Actions tab](https://github.com/citusdata/packaging/actions/workflows/test-package-installation.yml). + +It automatically discovers the supported platforms, latest Citus version that we published +packages for, and compatible PostgreSQL versions from the `all-citus` branch, using +[pkgvars](https://github.com/citusdata/packaging/blob/all-citus/pkgvars), +[.github/workflows/build-package.yml](https://github.com/citusdata/packaging/blob/all-citus/.github/workflows/build-package.yml) and +[postgres-matrix.yml](https://github.com/citusdata/packaging/blob/all-citus/postgres-matrix.yml) files. diff --git a/test_package_installation/common/run_multi_schedule.sh b/test_package_installation/common/run_multi_schedule.sh new file mode 100644 index 00000000..8f57008a --- /dev/null +++ b/test_package_installation/common/run_multi_schedule.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Runs "make check-multi" (multi_schedule). +# +# Expects the Citus source to be cloned at /citus and pg_regress +# to be available in the PostgreSQL binary path. +# +# Usage: run_multi_schedule.sh +# Example: run_multi_schedule.sh /usr/lib/postgresql/18/bin + +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +PG_BIN="$1" + +## suppress some of the regression test failures + +# On rpm based distros, Postgres package might be linked against a newer ICU +# than the one used to generate expected test output. To avoid test failures +# due to this, patch the expected output to match the newer ICU version. +# Without this, today pg18.sql fails. +echo 's/und-u-kc-true-ks-level1/und-u-kc-ks-level1/g' >> /citus/src/test/regress/bin/normalize.sed + +## run "make check-multi" + +export PATH="${PG_BIN}:${PATH}" + +cd /citus + +# we don't need lz4 and zstd as none of the tests in multi_schedule depend on them +./configure PG_CONFIG="${PG_BIN}/pg_config" --without-libcurl --without-lz4 --without-zstd + +chown -R postgres:postgres /citus + +echo "Running make check-multi ..." + +if ! su - postgres -c "make check-multi -C /citus/src/test/regress"; then + echo "make check-multi failed. Printing regression.diffs ..." + cat /citus/src/test/regress/regression.diffs + exit 1 +fi + +echo "make check-multi completed successfully." diff --git a/test_package_installation/common/test_basics.sh b/test_package_installation/common/test_basics.sh new file mode 100755 index 00000000..d111a155 --- /dev/null +++ b/test_package_installation/common/test_basics.sh @@ -0,0 +1,135 @@ +#!/bin/bash + +# Sets up a multi-node Citus cluster, creates a distributed table, +# rebalances shards, and verifies even distribution. +# +# Note: need to run this as root +# +# Usage: test_basics.sh +# Example: test_basics.sh 18 14.0.0 /usr/lib/postgresql/17/bin + +set -euo pipefail + +if [ "$#" -ne 3 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +EXPECTED_PG_MAJOR_VERSION="$1" +EXPECTED_CITUS_FULL_VERSION="$2" +PG_BIN="$3" + +## constants and helpers + +NODE_COUNT=3 +SHARDS_PER_NODE=5 +FIRST_NODE_PORT=9700 + +run_psql_cmd() { + if [ "$#" -ne 2 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: run_psql_cmd " >&2 + exit 1 + fi + + PORT_NUMBER="$1" + SQL_COMMAND="$2" + + su - postgres -c "${PG_BIN}/psql -X -qAt -p ${PORT_NUMBER} -c \"${SQL_COMMAND}\"" +} + +## initialize all nodes and create citus extension on them + +echo "Initializing nodes and creating citus extension on them ..." + +mkdir -p /test_cluster +chown postgres:postgres /test_cluster +chmod 755 /test_cluster + +for i in $(seq 0 $((NODE_COUNT - 1))); do + node_name="node${i}" + echo "Creating node: $node_name" + + su - postgres -c "mkdir -p /test_cluster/${node_name}" + + port_number=$((FIRST_NODE_PORT + i)) + + su - postgres -c "${PG_BIN}/initdb -D /test_cluster/${node_name}" + su - postgres -c "echo \"shared_preload_libraries = 'citus'\" >> /test_cluster/${node_name}/postgresql.conf" + su - postgres -c "${PG_BIN}/pg_ctl -D /test_cluster/${node_name} -o \"-p $port_number\" -l /test_cluster/${node_name}/logfile start" + run_psql_cmd "$port_number" "CREATE EXTENSION citus;" +done + +## verify PG version + +pg_version=$(run_psql_cmd "$FIRST_NODE_PORT" "SELECT version();") +if [[ "$pg_version" != "PostgreSQL ${EXPECTED_PG_MAJOR_VERSION}."* ]]; then + echo "Error: PostgreSQL version verification failed. Expected version to start with 'PostgreSQL ${EXPECTED_PG_MAJOR_VERSION}.', got: $pg_version" >&2 + exit 1 +else + echo "Verified PostgreSQL version: $pg_version" +fi + +## verify Citus extension version + +citus_version=$(run_psql_cmd "$FIRST_NODE_PORT" "SHOW citus.version;") +if [[ "$citus_version" != "${EXPECTED_CITUS_FULL_VERSION}"* ]]; then + echo "Error: Citus version verification failed. Expected version to start with '${EXPECTED_CITUS_FULL_VERSION}', got: $citus_version" >&2 + exit 1 +else + echo "Verified Citus version: $citus_version" +fi + +## create a distributed table + +echo "Creating a distributed table ..." + +shard_count=$((NODE_COUNT * SHARDS_PER_NODE)) +run_psql_cmd "$FIRST_NODE_PORT" "CREATE TABLE test_table (id INT); SELECT create_distributed_table('test_table', 'id', shard_count => $shard_count);" + +## add worker nodes + +echo "Adding worker nodes ..." + +for i in $(seq 1 $((NODE_COUNT - 1))); do + port_number=$((FIRST_NODE_PORT + i)) + run_psql_cmd "$FIRST_NODE_PORT" "SELECT citus_add_node('localhost', $port_number);" +done + +## run rebalancer + +echo "Running shard rebalancer ..." + +run_psql_cmd "$FIRST_NODE_PORT" "SELECT rebalance_table_shards(shard_transfer_mode =>'block_writes');" + +## verify that shards are distributed evenly across nodes + +echo "Verifying that shards are distributed evenly across nodes ..." + +result=$( + run_psql_cmd "$FIRST_NODE_PORT" " + SELECT bool_and(cnt = $SHARDS_PER_NODE) + FROM ( + SELECT nodeport, + COUNT(shardid) AS cnt + FROM pg_dist_node + LEFT JOIN pg_dist_shard_placement USING (nodeport) + GROUP BY nodeport + ) t; + " +) + +if [ "$result" = "t" ]; then + echo "Verified cluster creation, distributed table creation and shard rebalancing. Shards are distributed evenly across nodes." +else + echo "Error: Failed. Shards are not distributed evenly across nodes, see below for details:" >&2 + run_psql_cmd "$FIRST_NODE_PORT" " + SELECT nodeport, + COUNT(shardid) AS cnt + FROM pg_dist_node + LEFT JOIN pg_dist_shard_placement USING (nodeport) + GROUP BY nodeport; + " + exit 1 +fi diff --git a/test_package_installation/deb/install_package.sh b/test_package_installation/deb/install_package.sh new file mode 100755 index 00000000..2e700dc0 --- /dev/null +++ b/test_package_installation/deb/install_package.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Installs PostgreSQL and Citus packages on a Debian/Ubuntu system. +# +# Note: need to run this as root +# +# Usage: install_package.sh +# Example: install_package.sh 18 14.0.0 + +set -euo pipefail + +if [ "$#" -ne 2 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +PG_MAJOR_VERSION="$1" +CITUS_FULL_VERSION="$2" + +export DEBIAN_FRONTEND=noninteractive + +apt-get -y update + +## install postgres + +echo "Installing PostgreSQL ..." + +apt-get -y install curl ca-certificates + +install -d /usr/share/postgresql-common/pgdg +curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc +. /etc/os-release +sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list" + +apt -y update +apt-get -y install postgresql-${PG_MAJOR_VERSION} + +## install citus + +echo "Installing Citus ..." + +curl --fail https://install.citusdata.com/community/deb.sh > add-citus-repo.sh +bash add-citus-repo.sh + +citus_major_minor_version="${CITUS_FULL_VERSION%.*}" +apt-get -y install postgresql-${PG_MAJOR_VERSION}-citus-${citus_major_minor_version}=${CITUS_FULL_VERSION}.citus-1 + +echo "PostgreSQL and Citus packages installed successfully." diff --git a/test_package_installation/deb/install_regression_test_deps.sh b/test_package_installation/deb/install_regression_test_deps.sh new file mode 100644 index 00000000..03e73b10 --- /dev/null +++ b/test_package_installation/deb/install_regression_test_deps.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Installs dependencies needed to run Citus regression tests. +# +# Note: need to run this as root +# +# Usage: install_regression_test_deps.sh +# Example: install_regression_test_deps.sh 18 14.0.0 + +set -euo pipefail + +if [ "$#" -ne 2 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +PG_MAJOR_VERSION="$1" +CITUS_FULL_VERSION="$2" + +export DEBIAN_FRONTEND=noninteractive + +apt-get -y update + +## install autoreconf + +echo "Installing autoreconf and other build tools ..." + +apt-get -y install autoconf automake libtool + +## install pg_regress + +echo "Installing pg_regress via postgresql-server-dev-${PG_MAJOR_VERSION} ..." + +apt-get -y install postgresql-server-dev-${PG_MAJOR_VERSION} + +## clone citus repository + +echo "Cloning Citus repository (tag v${CITUS_FULL_VERSION}) ..." + +CITUS_TAG="v${CITUS_FULL_VERSION}" +CITUS_REPO_DIR="/citus" + +apt-get -y install git build-essential +git clone --depth 1 --branch "$CITUS_TAG" https://github.com/citusdata/citus "$CITUS_REPO_DIR" + +echo "Regression test dependencies installed successfully." diff --git a/test_package_installation/rpm/install_package.sh b/test_package_installation/rpm/install_package.sh new file mode 100755 index 00000000..d257d144 --- /dev/null +++ b/test_package_installation/rpm/install_package.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Installs PostgreSQL and Citus packages on an EL/OL (RPM-based) system. +# +# Note: need to run this as root +# +# Usage: install_package.sh +# Example: install_package.sh 18 14.0.0 8 + +set -euo pipefail + +if [ "$#" -ne 3 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +PG_MAJOR_VERSION="$1" +CITUS_FULL_VERSION="$2" +OS_VERSION="$3" + +yum -y update + +## install postgres + +echo "Installing PostgreSQL ..." + +yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-${OS_VERSION}-x86_64/pgdg-redhat-repo-latest.noarch.rpm + +# disable the built-in PostgreSQL module +yum -qy module disable postgresql + +yum install -y postgresql${PG_MAJOR_VERSION}-server + +## install citus + +echo "Installing Citus ..." + +curl --fail https://install.citusdata.com/community/rpm.sh > add-citus-repo.sh +bash add-citus-repo.sh + +citus_major_minor_version="${CITUS_FULL_VERSION%.*}" +citus_major_minor_version_without_dot="${citus_major_minor_version//./}" + +# TODO: use --nogpgcheck for now +yum install -y --nogpgcheck citus${citus_major_minor_version_without_dot}_${PG_MAJOR_VERSION}-${CITUS_FULL_VERSION}.citus-1.el${OS_VERSION} + +echo "PostgreSQL and Citus packages installed successfully." diff --git a/test_package_installation/rpm/install_regression_test_deps.sh b/test_package_installation/rpm/install_regression_test_deps.sh new file mode 100644 index 00000000..7c77f2bf --- /dev/null +++ b/test_package_installation/rpm/install_regression_test_deps.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# Installs dependencies needed to run Citus regression tests. +# +# Note: need to run this as root +# +# Usage: install_regression_test_deps.sh +# Example: install_regression_test_deps.sh 18 14.0.0 + +set -euo pipefail + +if [ "$#" -ne 2 ]; then + echo "Error: some arguments are missing." >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +PG_MAJOR_VERSION="$1" +CITUS_FULL_VERSION="$2" + +yum -y update + +## install pg_regress + +echo "Installing pg_regress via postgresql${PG_MAJOR_VERSION}-devel ..." + +# enable PowerTools/CRB/CodeReady repo for dependencies like perl-IPC-Run +if yum repolist --all | grep -qi 'powertools'; then + yum config-manager --set-enabled powertools +elif yum repolist --all | grep -qi 'crb'; then + yum config-manager --set-enabled crb +elif yum repolist --all | grep -qi 'codeready'; then + repo_id=$(yum repolist --all | grep -i 'codeready' | awk '{print $1}') + yum config-manager --set-enabled "$repo_id" +fi + +yum install -y postgresql${PG_MAJOR_VERSION}-devel + +## install contrib extensions needed by regression tests + +echo "Installing postgresql${PG_MAJOR_VERSION}-contrib ..." + +yum install -y postgresql${PG_MAJOR_VERSION}-contrib + +## clone citus repository + +echo "Cloning Citus repository (tag v${CITUS_FULL_VERSION}) ..." + +CITUS_TAG="v${CITUS_FULL_VERSION}" +CITUS_REPO_DIR="/citus" + +yum install -y git +yum groupinstall -y "Development Tools" +git clone --depth 1 --branch "$CITUS_TAG" https://github.com/citusdata/citus "$CITUS_REPO_DIR" + +echo "Regression test dependencies installed successfully." diff --git a/test_package_installation/run_test.py b/test_package_installation/run_test.py new file mode 100644 index 00000000..3f45f60a --- /dev/null +++ b/test_package_installation/run_test.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +""" +Docker-based package installation tester. + +Pulls a Docker image, starts a container, copies the test scripts into it, +and runs the appropriate installation + verification tests for the given OS. + +Usage: + python run_test.py / + +Examples: + python run_test.py ubuntu/noble 18 14.0.0 + python run_test.py el/9 18 14.0.0 +""" + +import os +import subprocess +import sys +from typing import Optional + +# Force line-buffered stdout so print() output appears in order +# with subprocess output (Python fully buffers stdout in non-TTY +# environments like CI). +sys.stdout.reconfigure(line_buffering=True) + + +def pull_docker_image(image_name: str) -> bool: + print(f"Pulling Docker image: {image_name}") + + try: + result = subprocess.run( + ["docker", "pull", image_name], stdout=sys.stdout, stderr=sys.stdout + ) + + if result.returncode == 0: + print(f"Successfully pulled image: {image_name}") + return True + else: + print(f"Error pulling image: {image_name}", file=sys.stderr) + return False + except Exception as e: + print(f"Error during image pull: {e}", file=sys.stderr) + return False + + +def start_container(image_name: str) -> Optional[str]: + print(f"Starting container from image: {image_name}") + + try: + result = subprocess.run( + ["docker", "run", "-d", image_name, "sleep", "infinity"], + capture_output=True, + text=True, + ) + + if result.returncode == 0: + container_id = result.stdout.strip() + print(f"Container started with ID: {container_id}") + return container_id + else: + print(f"Error starting container: {result.stderr}", file=sys.stderr) + return None + except Exception as e: + print(f"Error during container start: {e}", file=sys.stderr) + return None + + +def copy_into_container( + container_id: str, local_path: str, container_path: str +) -> bool: + print(f"Copying {local_path} to container {container_id}:{container_path}") + + try: + result = subprocess.run( + ["docker", "cp", local_path, f"{container_id}:{container_path}"], + stdout=sys.stdout, + stderr=sys.stdout, + ) + + if result.returncode == 0: + print(f"Successfully copied {local_path} to container") + return True + else: + print(f"Error copying file into container.", file=sys.stderr) + return False + except Exception as e: + print(f"Error during file copy: {e}", file=sys.stderr) + return False + + +def run_command_in_container(container_id: str, command: str) -> bool: + print(f"Executing command in container {container_id}") + + try: + result = subprocess.run( + ["docker", "exec", container_id, "bash", "-c", command], + stdout=sys.stdout, + stderr=sys.stdout, + ) + return result.returncode == 0 + except Exception as e: + print(f"Error during command execution: {e}", file=sys.stderr) + return False + + +def main(os_name: str, os_version: str, pg_major_version: str, citus_full_version: str): + if os_name == "ubuntu" or os_name == "debian": + os_specific_dir = "deb" + install_package_command = ( + f"bash /install_package.sh {pg_major_version} {citus_full_version}" + ) + pg_binary_path = f"/usr/lib/postgresql/{pg_major_version}/bin" + image_name = f"{os_name}:{os_version}" + elif os_name == "el" or os_name == "ol": + os_specific_dir = "rpm" + install_package_command = f"bash /install_package.sh {pg_major_version} {citus_full_version} {os_version}" + pg_binary_path = f"/usr/pgsql-{pg_major_version}/bin" + image_name = ( + f"almalinux:{os_version}" + if os_name == "el" + else f"oraclelinux:{os_version}" + ) + else: + print(f"Unsupported OS: {os_name}. Exiting.", file=sys.stderr) + sys.exit(1) + + script_relative_paths = [ + f"{os_specific_dir}/install_package.sh", + "common/test_basics.sh", + f"{os_specific_dir}/install_regression_test_deps.sh", + "common/run_multi_schedule.sh", + ] + + commands = [ + install_package_command, + f"bash /test_basics.sh {pg_major_version} {citus_full_version} {pg_binary_path}", + f"bash /install_regression_test_deps.sh {pg_major_version} {citus_full_version}", + f"bash /run_multi_schedule.sh {pg_binary_path}", + ] + + # Pull the Docker image + if not pull_docker_image(image_name): + print("Failed to pull Docker image. Exiting.", file=sys.stderr) + sys.exit(1) + + container_id = start_container(image_name) + if not container_id: + print("Failed to start Docker container. Exiting.", file=sys.stderr) + sys.exit(1) + + try: + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Copy all necessary scripts into the container first + for p in script_relative_paths: + script_path = os.path.join(script_dir, p) + if not copy_into_container(container_id, script_path, "/"): + print(f"Failed to copy {p} into container. Exiting.", file=sys.stderr) + sys.exit(1) + + # Execute the scripts in order + for c in commands: + print(f"Running {c} in container...") + if not run_command_in_container(container_id, c): + print(f"Failed to execute {c} in container. Exiting.", file=sys.stderr) + sys.exit(1) + finally: + # Cleanup: force-remove the container (stops it if still running) + print(f"Removing container {container_id}") + try: + subprocess.run( + ["docker", "rm", "-f", container_id], + stdout=sys.stdout, + stderr=sys.stdout, + ) + print(f"Container {container_id} removed successfully.") + except Exception as e: + print(f"Error removing container: {e}", file=sys.stderr) + + +if __name__ == "__main__": + if len(sys.argv) != 4: + print( + "Usage: python run_test.py ", + file=sys.stderr, + ) + print("Example: python run_test.py ubuntu/noble 18 14.0.0", file=sys.stderr) + sys.exit(1) + + os_name, os_version = sys.argv[1].split("/") + postgres_version = sys.argv[2] + citus_version = sys.argv[3] + + if not postgres_version.isdigit(): + print( + "PostgreSQL major version must be a number (e.g., 18). Exiting.", + file=sys.stderr, + ) + sys.exit(1) + + if not ( + len(citus_version.split(".")) == 3 + and all(p.isdigit() for p in citus_version.split(".")) + ): + print( + "Citus version must be in the format major.minor.patch (e.g., 14.0.0). Exiting.", + file=sys.stderr, + ) + sys.exit(1) + + main(os_name, os_version, postgres_version, citus_version)