Skip to content

Instantly share code, notes, and snippets.

@tzaffi
Created May 25, 2023 19:00
Show Gist options
  • Save tzaffi/b64f8fb891886c8360586374a1ef2b91 to your computer and use it in GitHub Desktop.
Save tzaffi/b64f8fb891886c8360586374a1ef2b91 to your computer and use it in GitHub Desktop.
Python Runner for `runner.go`

Example Scenarios Run using Conduit and Postgres in python via run_runner.py

Let's redo the previous bash example, but this time using run_runner.py.

  1. We assume that the conduit binary is available in the same directory as run_runner.py
  2. You'll need to be in a python virtual env that has the requirements installed:
❯ python -m venv .venv
❯ source .venv/bin/activate
❯ pip install -r requirements.txt
  1. Let's see runner.py's options:
❯ python run_runner.py --help
usage: run_runner.py [-h] [--conduit-binary CONDUIT_BINARY] [--scenario SCENARIO] [--reset-db] [--purge] [--keep-alive] [--pg-container PG_CONTAINER] [--pg-port PG_PORT]
                     [--pg-database PG_DATABASE] [--report-directory REPORT_DIRECTORY] [--build-generator] [--skip-runner] [--test-duration TEST_DURATION]

options:
  -h, --help            show this help message and exit
  --conduit-binary CONDUIT_BINARY
                        Path to conduit binary
  --scenario SCENARIO   Scenario configuration file (default=HOME_DIRECTORY/go-algorand/tools/block-generator/test_config.yml)
  --reset-db            Reset the DB and start at round 0 (default=False)
  --purge               Shutdown container that has been kept alive (default=False)
  --keep-alive          Keep postgres container alive at end of run (default=False)
  --pg-container PG_CONTAINER
                        Name of postgres container (default='generator-test-container')
  --pg-port PG_PORT     Postgres port (default=15432)
  --pg-database PG_DATABASE
                        Postgres database (default='generator_db')
  --report-directory REPORT_DIRECTORY
                        Report directory (default='../../tmp/OUTPUT_RUN_RUNNER_TEST')
  --build-generator     Build the generator binary (default=False)
  --skip-runner         Skip running the generator (default=False)
  --test-duration TEST_DURATION
                        Test duration (default='30s')
  1. Let's run a similar scenario as above, but with:
    • --scenario test_config.yml to explicitly specify the scenario config file
    • --test-duration 30s to explicitly specify the test duration
    • --keep-alive so we can inspect the database after the run
    • --report-directory RUN_REPORT to explicitly specify the report directory
❯ python run_runner.py --scenario test_config.yml --conduit-binary ./conduit --keep-alive --test-duration 30s --report-directory RUN_REPORT
...
Using scenario file: test_config.yml
!!! rm -rf RUN_REPORT !!!
Skipping generator build.
Starting postgres container.
Starting test runner
Keeping postgres container alive: generator-test-container
Also, not removing report directory: RUN_REPORT
  1. Let's inspect the report:
❯ cat RUN_REPORT/test_config.report
scenario:test_config.yml
test_duration_seconds:30
test_duration_actual_seconds:30.024180
...
  1. What is the docker container?
❯ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS                     NAMES
9d000f2d3f3c   postgres   "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes   0.0.0.0:15432->5432/tcp   generator-test-container
  1. Let's enter a psql session insdie the docker container and query the database:
❯ docker exec -it generator-test-container psql -U algorand -d generator_db
psql (14.5 (Debian 14.5-1.pgdg110+1))
Type "help" for help.

generator_db=# \dt
               List of relations
...

generator_db=# SELECT * FROM metastate;
...

generator_db=# SELECT COUNT(*) FROM txn;
 count 
-------
  5380
(1 row)

generator_db=# SELECT COUNT(*) FROM block_header;
 count 
-------
   539
(1 row)
...
  1. Let's shutdown the container:
❯ python run_runner.py --purge
...
Purging postgres container - NO OTHER ACTION TAKEN
Stopping postgres container.

❯ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
SCENARIO = test_config.yml
SKIP = --skip-runner
RESETDB = --reset-db
debug-blockgen:
python run_runner.py \
--conduit-binary ./conduit \
--scenario $(SCENARIO) \
--keep-alive $(SKIP) \
--test-duration 30s \
$(RESETDB)
cleanup:
python run_runner.py --purge
enter-pg:
docker exec -it generator-test-container psql -U algorand -d generator_db
import argparse
import os
from pathlib import Path
import shlex
import subprocess
import sys
import time
POSTGRES_CONTAINER = "generator-test-container"
POSTGRES_PORT = 15432
POSTGRES_DATABASE = "generator_db"
REPORT_DIRECTORY = "../../tmp/OUTPUT_RUN_RUNNER_TEST"
CWD = Path.cwd()
def run_cmd(cmd):
process = subprocess.Popen(
shlex.split(cmd.replace("\\\n", " ")),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, stderr = process.communicate()
if (rcode := process.returncode) != 0:
print(f"Error executing command: {cmd}")
print(stderr.decode())
sys.exit(rcode)
return stdout.decode()
def up(args):
run_cmd(f"docker rm -f {args.pg_container}")
run_cmd(
f"docker run -d --name {args.pg_container} -e POSTGRES_USER=algorand -e POSTGRES_PASSWORD=algorand -p {args.pg_port}:5432 postgres"
)
time.sleep(5)
run_cmd(
f'docker exec -it {args.pg_container} psql -Ualgorand -c "create database {args.pg_database}"'
)
def down(args):
run_cmd(f"docker rm -f {args.pg_container}")
def launch_json_args(cmd: str):
def wrap(x):
return x if x.startswith('"') else f'"{x}"'
newlines = []
lines = cmd.splitlines()
for i, line in enumerate(lines):
if i == 0:
continue
if not line.startswith("--"):
newline = wrap(line)
else:
newline = ",".join(map(wrap, line.split(" ", maxsplit=1)))
if i < len(lines) - 1:
newline += ","
newlines.append(newline)
NL = "\n"
BS = "\\"
DC = ",,"
return f"[{(NL.join(newlines)).replace(BS, '').replace(DC, ',').replace(' ', '')}]"
def parse_ars():
parser = argparse.ArgumentParser()
parser.add_argument("--conduit-binary", help="Path to conduit binary")
parser.add_argument(
"--scenario",
default=(default := CWD / "test_config.yml"),
help=f"Scenario configuration file ({default=!s})",
)
parser.add_argument(
"--reset-db",
action="store_true",
default=False,
help="Reset the DB and start at round 0 (default=False)",
)
parser.add_argument(
"--purge",
action="store_true",
default=False,
help="Shutdown container that has been kept alive (default=False)",
)
parser.add_argument(
"--keep-alive",
action="store_true",
default=False,
help="Keep postgres container alive at end of run (default=False)",
)
parser.add_argument(
"--pg-container",
default=(default := POSTGRES_CONTAINER),
help=f"Name of postgres container ({default=})",
)
parser.add_argument(
"--pg-port",
default=(default := POSTGRES_PORT),
help=f"Postgres port ({default=})",
)
parser.add_argument(
"--pg-database",
default=(default := POSTGRES_DATABASE),
help=f"Postgres database ({default=})",
)
parser.add_argument(
"--report-directory",
default=(default := REPORT_DIRECTORY),
help=f"Report directory ({default=})",
)
parser.add_argument(
"--build-generator",
action="store_true",
default=False,
help="Build the generator binary (default=False)",
)
parser.add_argument(
"--skip-runner",
action="store_true",
default=False,
help="Skip running the generator (default=False)",
)
parser.add_argument(
"--test-duration",
default=(default := "30s"),
help=f"Test duration ({default=})",
)
args = parser.parse_args()
print(args)
return args
def main():
args = parse_ars()
try:
if not args.purge:
print(f"Using scenario file: {args.scenario}")
print(f"!!! rm -rf {args.report_directory} !!!")
run_cmd(f"rm -rf {args.report_directory}")
if args.build_generator:
print("Building generator.")
os.chdir(CWD)
run_cmd("go build")
os.chdir("..")
else:
print("Skipping generator build.")
print("Starting postgres container.")
up(args)
SLNL = "\\\n"
generator_cmd = f"""{CWD}/block-generator \\
runner \\
--conduit-binary "{args.conduit_binary}" \\
--report-directory {args.report_directory} \\
--test-duration {args.test_duration} \\
--log-level trace \\
--postgres-connection-string "host=localhost user=algorand password=algorand dbname={args.pg_database} port={args.pg_port} sslmode=disable" \\
--scenario {args.scenario} {SLNL+'--reset-db' if args.reset_db else ''}"""
if args.skip_runner:
print("Skipping test runner.")
print(f"Run it yourself:\n{generator_cmd}")
print(
f"""`launch.json` args:
{launch_json_args(generator_cmd)}"""
)
else:
print("Starting test runner")
run_cmd(generator_cmd)
else:
print("Purging postgres container - NO OTHER ACTION TAKEN")
down(args)
finally:
if not args.keep_alive:
print("Stopping postgres container.")
down(args)
else:
print(f"Keeping postgres container alive: {args.pg_container}")
print(f"Also, not removing report directory: {args.report_directory}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment