Skip to content

Commit ea4ed53

Browse files
committed
test(refactor): add withToxiproxy* in withTools
This commit introduces toxiproxy in withTools adding the following helpers: * withToxiproxyServer * withToxiproxyProxy * withToxiproxyLatency * withToxiproxyPgProxy
1 parent af4c415 commit ea4ed53

4 files changed

Lines changed: 156 additions & 10 deletions

File tree

nix/tools/withTools.nix

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
, python3Packages
99
, writeText
1010
, writers
11+
, toxiproxy
1112
}:
1213
let
1314
withTmpDb =
@@ -54,16 +55,25 @@ let
5455
5556
export PGDATA="$tmpdir/db"
5657
export PGHOST="$tmpdir/socket"
58+
PGPORT=$(${randomPort})
59+
export PGPORT
5760
export PGUSER
5861
export PGDATABASE
5962
export PGRST_DB_SCHEMAS
6063
export PGTZ
6164
export PGOPTIONS
6265
6366
HBA_FILE="$tmpdir/pg_hba.conf"
64-
echo "local $PGDATABASE some_protected_user password" > "$HBA_FILE"
65-
echo "local $PGDATABASE all trust" >> "$HBA_FILE"
66-
echo "local replication all trust" >> "$HBA_FILE"
67+
{
68+
echo "local $PGDATABASE some_protected_user password"
69+
echo "local $PGDATABASE all trust"
70+
echo "local replication all trust"
71+
echo "host $PGDATABASE some_protected_user localhost password"
72+
echo "host $PGDATABASE all localhost trust"
73+
} >> "$HBA_FILE"
74+
75+
UNIX_PGHOST="$PGHOST"
76+
export TCP_PGHOST="localhost"
6777
6878
log "Initializing database cluster..."
6979
# We try to make the database cluster as independent as possible from the host
@@ -80,7 +90,7 @@ let
8090
# On MacOS, it's 104 chars
8191
# See: https://serverfault.com/questions/641347/check-if-a-path-exceeds-maximum-for-unix-domain-socket
8292
83-
pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $PGHOST -c log_statement=\"all\" " \
93+
pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c listen_addresses=\"$TCP_PGHOST\" -c hba_file=$HBA_FILE -k $UNIX_PGHOST -c log_statement=\"all\" " \
8494
>> "$setuplog"
8595
8696
log "Creating a minimally privileged $PGUSER connection role..."
@@ -93,6 +103,7 @@ let
93103
replica_slot="replica_$RANDOM"
94104
replica_dir="$tmpdir/$replica_slot"
95105
replica_host="$tmpdir/socket_$replica_slot"
106+
replica_port=$(${randomPort})
96107
97108
mkdir -p "$replica_host"
98109
@@ -105,15 +116,16 @@ let
105116
106117
log "Starting replica on $replica_host"
107118
108-
pg_ctl -D "$replica_dir" -l "$replica_dblog" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $replica_host -c log_statement=\"all\" " \
119+
pg_ctl -D "$replica_dir" -l "$replica_dblog" -w start -o "-F -c listen_addresses=\"$TCP_PGHOST\" -c port=$replica_port -c hba_file=$HBA_FILE -k $replica_host -c log_statement=\"all\" " \
109120
>> "$setuplog"
110121
111122
>&2 echo "${commandName}: Replica enabled. You can connect to it with: psql 'postgres:///$PGDATABASE?host=$replica_host' -U postgres"
112123
>&2 echo "${commandName}: You can tail the replica logs with: tail -f $replica_dblog"
113124
114125
export PGREPLICAHOST="$replica_host"
126+
export PGREPLICAPORT="$replica_port"
115127
export PGREPLICASLOT="$replica_slot"
116-
export PGRST_DB_URI="postgres:///$PGDATABASE?host=$PGREPLICAHOST,$PGHOST"
128+
export PGRST_DB_URI="postgres:///$PGDATABASE?host=$PGREPLICAHOST,$PGHOST&port=$replica_port,$PGPORT"
117129
fi
118130
119131
# shellcheck disable=SC2317
@@ -371,6 +383,136 @@ let
371383
libraries = [ python3Packages.pandas python3Packages.tabulate python3Packages.psutil ];
372384
}
373385
(builtins.readFile ./monitor_pid.py);
386+
387+
randomPort =
388+
writers.writePython3 "postgrest-random-port"
389+
{
390+
# Quick one-liner: ignore linting errors
391+
flakeIgnore = [ "E702" "W292" "E501" ];
392+
}
393+
''import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()'';
394+
395+
withToxiproxyProxy =
396+
checkedShellScript
397+
{
398+
name = "postgrest-with-toxiproxy-proxy";
399+
docs = "Run <command> with Toxiproxy proxy created. Proxy name passed as TOXI_PROXY_NAME env variable.";
400+
args =
401+
[
402+
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
403+
"ARG_LEFTOVERS([command arguments])"
404+
"ARG_OPTIONAL_SINGLE([listen], [l], [Proxy will listen on this address])"
405+
"ARG_OPTIONAL_SINGLE([upstream], [u], [Proxy will forward to this address])"
406+
];
407+
positionalCompletion = "_command";
408+
workingDir = "/";
409+
withPath = [ toxiproxy ];
410+
}
411+
''
412+
proxyname="tp$RANDOM"
413+
toxiproxy-cli create -l "$_arg_listen" -u "$_arg_upstream" "$proxyname"
414+
415+
# shellcheck disable=SC2317
416+
stop () {
417+
toxiproxy-cli delete "$proxyname" || true
418+
}
419+
trap stop EXIT
420+
421+
(TOXI_PROXY_NAME="$proxyname" "$_arg_command" "''${_arg_leftovers[@]}")
422+
'';
423+
424+
withToxiproxyPgProxy =
425+
checkedShellScript
426+
{
427+
name = "postgrest-with-toxiproxy-pg-proxy";
428+
docs = "Run <command> with a Toxiproxy proxy to PosgreSQL.";
429+
args =
430+
[
431+
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
432+
"ARG_LEFTOVERS([command arguments])"
433+
"ARG_USE_ENV([TCP_PGHOST], [], [PG host name])"
434+
"ARG_USE_ENV([PGPORT], [], [PG port])"
435+
];
436+
positionalCompletion = "_command";
437+
workingDir = "/";
438+
}
439+
''
440+
proxy_port=''$(${randomPort})
441+
442+
${withToxiproxyServer} ${withToxiproxyProxy} -l "$TCP_PGHOST:$proxy_port" -u "$TCP_PGHOST:$PGPORT" \
443+
env "TOXI_PGPORT=$proxy_port" "$_arg_command" "''${_arg_leftovers[@]}"
444+
'';
445+
446+
withToxiproxyLatency =
447+
checkedShellScript
448+
{
449+
name = "postgrest-with-toxiproxy-latency";
450+
docs = "Run <command> with Toxiproxy latency toxic for both upstream and downstream";
451+
args =
452+
[
453+
"ARG_POSITIONAL_SINGLE([latency], [Latency])"
454+
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
455+
"ARG_LEFTOVERS([command arguments])"
456+
"ARG_USE_ENV([TOXI_PROXY_NAME], [], [Toxiproxy proxy name to create toxic in])"
457+
];
458+
positionalCompletion = "_command";
459+
workingDir = "/";
460+
withPath = [ toxiproxy ];
461+
}
462+
''
463+
proxyname="$TOXI_PROXY_NAME"
464+
upstream_toxicname="toxic$RANDOM"
465+
downstream_toxicname="toxic$RANDOM"
466+
# calculate delay in milliseconds
467+
# version accepting only milliseconds
468+
# TODO implement delay in seconds/minutes/hours
469+
#read -r delay unit <<<''$(echo "$_arg_latency" | sed -r 's/([0-9]+)(.*)/\1 \2/g')
470+
delay=''$(echo "$_arg_latency" | sed -r 's/([0-9]+)(.*)/\1/g')
471+
472+
toxiproxy-cli toxic add -t latency --upstream -n "$upstream_toxicname" -a latency="$delay" "$proxyname"
473+
toxiproxy-cli toxic add -t latency --downstream -n "$downstream_toxicname" -a latency="$delay" "$proxyname"
474+
475+
# shellcheck disable=SC2317
476+
stop () {
477+
toxiproxy-cli toxic delete -n "$downstream_toxicname" "$proxyname" || true
478+
toxiproxy-cli toxic delete -n "$upstream_toxicname" "$proxyname" || true
479+
}
480+
trap stop EXIT
481+
482+
("$_arg_command" "''${_arg_leftovers[@]}")
483+
'';
484+
485+
withToxiproxyServer =
486+
checkedShellScript
487+
{
488+
name = "postgrest-with-toxiproxy-server";
489+
docs = "Run <command> with toxiproxy-server";
490+
args =
491+
[
492+
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
493+
"ARG_LEFTOVERS([command arguments])"
494+
];
495+
positionalCompletion = "_command";
496+
workingDir = "/";
497+
withPath = [ toxiproxy ];
498+
}
499+
''
500+
if ! test -v TOXI_PROXY; then
501+
export TOXI_PROXY=""
502+
LOG_LEVEL=error toxiproxy-server&
503+
TOXIPROXY_PID=$!
504+
sleep 1 # give the server a moment to start
505+
506+
# shellcheck disable=SC2317
507+
stop () {
508+
kill "$TOXIPROXY_PID" || true
509+
wait "$TOXIPROXY_PID" || true
510+
}
511+
trap stop EXIT
512+
fi
513+
("$_arg_command" "''${_arg_leftovers[@]}")
514+
'';
515+
374516
in
375517
buildToolbox
376518
{
@@ -385,5 +527,5 @@ buildToolbox
385527
builtins.map (pg: { inherit (pg) name; value = withTmpDb pg; }) postgresqlVersions
386528
);
387529
# make latest withPg available for other nix files
388-
extra = { inherit withPg; };
530+
extra = { inherit withPg withToxiproxyPgProxy; };
389531
}

test/io/conftest.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ def dburi():
99
"Postgres database connection URI."
1010
dbname = os.environ["PGDATABASE"]
1111
host = os.environ["PGHOST"]
12+
port = os.environ["PGPORT"]
1213
user = os.environ["PGUSER"]
13-
return f"postgresql://?dbname={dbname}&host={host}&user={user}".encode()
14+
return f"postgresql://?dbname={dbname}&host={host}&port={port}&user={user}".encode()
1415

1516

1617
@pytest.fixture
@@ -19,6 +20,7 @@ def baseenv():
1920
return {
2021
"PGDATABASE": os.environ["PGDATABASE"],
2122
"PGHOST": os.environ["PGHOST"],
23+
"PGPORT": os.environ["PGPORT"],
2224
"PGUSER": os.environ["PGUSER"],
2325
}
2426

@@ -51,6 +53,7 @@ def replicaenv(defaultenv):
5153
**defaultenv,
5254
**conf,
5355
"PGHOST": os.environ["PGREPLICAHOST"] + "," + os.environ["PGHOST"],
56+
"PGPORT": os.environ["PGREPLICAPORT"] + "," + os.environ["PGPORT"],
5457
"PGREPLICASLOT": os.environ["PGREPLICASLOT"],
5558
},
5659
}
@@ -76,6 +79,7 @@ def metapostgrest():
7679
env = {
7780
"PGDATABASE": os.environ["PGDATABASE"],
7881
"PGHOST": os.environ["PGHOST"],
82+
"PGPORT": os.environ["PGPORT"],
7983
"PGUSER": role,
8084
"PGRST_DB_ANON_ROLE": role,
8185
"PGRST_DB_CONFIG": "true",

test/io/test_auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def relativeSeconds(sec):
165165

166166
def test_fail_with_invalid_password(defaultenv):
167167
"Connecting with an invalid password should fail without retries."
168-
uri = f'postgresql://?dbname={defaultenv["PGDATABASE"]}&host={defaultenv["PGHOST"]}&user=some_protected_user&password=invalid_pass'
168+
uri = f'postgresql://?dbname={defaultenv["PGDATABASE"]}&host={defaultenv["PGHOST"]}&port={defaultenv["PGPORT"]}&user=some_protected_user&password=invalid_pass'
169169
env = {**defaultenv, "PGRST_DB_URI": uri}
170170
with run(env=env, wait_for_readiness=False) as postgrest:
171171
exitCode = wait_until_exit(postgrest)

test/io/test_io.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1675,7 +1675,7 @@ def test_log_listener_connection_start(defaultenv):
16751675
# Check for the listener start message containing host and port
16761676
# Do not check if pg version is displayed properly as it is tricky to test it
16771677
assert any(
1678-
f'"{defaultenv["PGHOST"]}:5432" and listening for database notifications on the "pgrst" channel'
1678+
f'"{defaultenv["PGHOST"]}:{defaultenv["PGPORT"]}" and listening for database notifications on the "pgrst" channel'
16791679
in line
16801680
for line in output
16811681
)

0 commit comments

Comments
 (0)