From 342122cecf95c743c067821e97ee46f512285012 Mon Sep 17 00:00:00 2001 From: Artem Gavrilov Date: Thu, 24 Jul 2025 17:37:12 +0200 Subject: [PATCH] PG-1411 Add pg_resetwal tap tests for TDE setup These tests are copy of original pg_resetwal tests with enalbed WAL encryption and removed flags validation as we interested here only in proper enrypted WAL handling. --- contrib/pg_tde/t/pg_resetwal_basic.pl | 150 ++++++++++++++++++++++ contrib/pg_tde/t/pg_resetwal_corrupted.pl | 92 +++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 contrib/pg_tde/t/pg_resetwal_basic.pl create mode 100644 contrib/pg_tde/t/pg_resetwal_corrupted.pl diff --git a/contrib/pg_tde/t/pg_resetwal_basic.pl b/contrib/pg_tde/t/pg_resetwal_basic.pl new file mode 100644 index 00000000000..78890f633cf --- /dev/null +++ b/contrib/pg_tde/t/pg_resetwal_basic.pl @@ -0,0 +1,150 @@ + +# Copyright (c) 2021-2024, PostgreSQL Global Development Group + +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +unlink('/tmp/pg_resetwal_basic.per'); + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; +$node->append_conf( + 'postgresql.conf', q{ +track_commit_timestamp = on + +# WAL Encryption +shared_preload_libraries = 'pg_tde' +}); + +$node->start; +$node->safe_psql('postgres', "CREATE EXTENSION pg_tde;"); +$node->safe_psql('postgres', + "SELECT pg_tde_add_global_key_provider_file('file-keyring-wal', '/tmp/pg_resetwal_basic.per');" +); +$node->safe_psql('postgres', + "SELECT pg_tde_create_key_using_global_key_provider('server-key', 'file-keyring-wal');" +); +$node->safe_psql('postgres', + "SELECT pg_tde_set_server_key_using_global_key_provider('server-key', 'file-keyring-wal');" +); + +$node->append_conf( + 'postgresql.conf', q{ +pg_tde.wal_encrypt = on +}); +$node->stop; + + +command_like([ 'pg_resetwal', '-n', $node->data_dir ], + qr/checkpoint/, 'pg_resetwal -n produces output'); + + +# Permissions on PGDATA should be default +SKIP: +{ + skip "unix-style permissions not supported on Windows", 1 + if ($windows_os); + + ok(check_mode_recursive($node->data_dir, 0700, 0600), + 'check PGDATA permissions'); +} + +command_ok([ 'pg_resetwal', '-D', $node->data_dir ], 'pg_resetwal runs'); +$node->start; +is($node->safe_psql("postgres", "SELECT 1;"), + 1, 'server running and working after reset'); + +command_fails_like( + [ 'pg_resetwal', $node->data_dir ], + qr/lock file .* exists/, + 'fails if server running'); + +$node->stop('immediate'); +command_fails_like( + [ 'pg_resetwal', $node->data_dir ], + qr/database server was not shut down cleanly/, + 'does not run after immediate shutdown'); +command_ok( + [ 'pg_resetwal', '-f', $node->data_dir ], + 'runs after immediate shutdown with force'); +$node->start; +is($node->safe_psql("postgres", "SELECT 1;"), + 1, 'server running and working after forced reset'); + +$node->stop; + +# check various command-line handling + +# Note: This test intends to check that a nonexistent data directory +# gives a reasonable error message. Because of the way the code is +# currently structured, you get an error about readings permissions, +# which is perhaps suboptimal, so feel free to update this test if +# this gets improved. + + +# run with control override options + +my $out = (run_command([ 'pg_resetwal', '-n', $node->data_dir ]))[0]; +$out =~ /^Database block size: *(\d+)$/m or die; +my $blcksz = $1; + +my @cmd = ('pg_resetwal', '-D', $node->data_dir); + +# some not-so-critical hardcoded values +push @cmd, '-e', 1; +push @cmd, '-l', '00000001000000320000004B'; +push @cmd, '-o', 100_000; +push @cmd, '--wal-segsize', 1; + +# these use the guidance from the documentation + +sub get_slru_files +{ + opendir(my $dh, $node->data_dir . '/' . $_[0]) or die $!; + my @files = sort grep { /[0-9A-F]+/ } readdir $dh; + closedir $dh; + return @files; +} + +my (@files, $mult); + +@files = get_slru_files('pg_commit_ts'); +# XXX: Should there be a multiplier, similar to the other options? +# -c argument is "old,new" +push @cmd, + '-c', + sprintf("%d,%d", hex($files[0]) == 0 ? 3 : hex($files[0]), hex($files[-1])); + +@files = get_slru_files('pg_multixact/offsets'); +$mult = 32 * $blcksz / 4; +# -m argument is "new,old" +push @cmd, '-m', + sprintf("%d,%d", + (hex($files[-1]) + 1) * $mult, + hex($files[0]) == 0 ? 1 : hex($files[0] * $mult)); + +@files = get_slru_files('pg_multixact/members'); +$mult = 32 * int($blcksz / 20) * 4; +push @cmd, '-O', (hex($files[-1]) + 1) * $mult; + +@files = get_slru_files('pg_xact'); +$mult = 32 * $blcksz * 4; +push @cmd, + '-u', (hex($files[0]) == 0 ? 3 : hex($files[0]) * $mult), + '-x', ((hex($files[-1]) + 1) * $mult); + +command_ok([ @cmd, '-n' ], 'runs with control override options, dry run'); +command_ok(\@cmd, 'runs with control override options'); +command_like( + [ 'pg_resetwal', '-n', $node->data_dir ], + qr/^Latest checkpoint's NextOID: *100000$/m, + 'spot check that control changes were applied'); + +$node->start; +ok(1, 'server started after reset'); + +done_testing(); diff --git a/contrib/pg_tde/t/pg_resetwal_corrupted.pl b/contrib/pg_tde/t/pg_resetwal_corrupted.pl new file mode 100644 index 00000000000..74cc66a2c80 --- /dev/null +++ b/contrib/pg_tde/t/pg_resetwal_corrupted.pl @@ -0,0 +1,92 @@ + +# Copyright (c) 2021-2024, PostgreSQL Global Development Group + +# Tests for handling a corrupted pg_control + +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +unlink('/tmp/pg_resetwal_corrupted.per'); + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; +$node->append_conf( + 'postgresql.conf', q{ + +# WAL Encryption +shared_preload_libraries = 'pg_tde' +}); + +$node->start; +$node->safe_psql('postgres', "CREATE EXTENSION pg_tde;"); +$node->safe_psql('postgres', + "SELECT pg_tde_add_global_key_provider_file('file-keyring-wal', '/tmp/pg_waldump_corrupted.per');" +); +$node->safe_psql('postgres', + "SELECT pg_tde_create_key_using_global_key_provider('server-key', 'file-keyring-wal');" +); +$node->safe_psql('postgres', + "SELECT pg_tde_set_server_key_using_global_key_provider('server-key', 'file-keyring-wal');" +); + +$node->append_conf( + 'postgresql.conf', q{ +pg_tde.wal_encrypt = on +}); +$node->stop; + +my $pg_control = $node->data_dir . '/global/pg_control'; +my $size = -s $pg_control; + +# Read out the head of the file to get PG_CONTROL_VERSION in +# particular. +my $data; +open my $fh, '<', $pg_control or BAIL_OUT($!); +binmode $fh; +read $fh, $data, 16 or die $!; +close $fh; + +# Fill pg_control with zeros +open $fh, '>', $pg_control or BAIL_OUT($!); +binmode $fh; +print $fh pack("x[$size]"); +close $fh; + +command_checks_all( + [ 'pg_resetwal', '-n', $node->data_dir ], + 0, + [qr/pg_control version number/], + [ + qr/pg_resetwal: warning: pg_control exists but is broken or wrong version; ignoring it/ + ], + 'processes corrupted pg_control all zeroes'); + +# Put in the previously saved header data. This uses a different code +# path internally, allowing us to process a zero WAL segment size. +open $fh, '>', $pg_control or BAIL_OUT($!); +binmode $fh; +print $fh $data, pack("x[" . ($size - 16) . "]"); +close $fh; + +command_checks_all( + [ 'pg_resetwal', '-n', $node->data_dir ], + 0, + [qr/pg_control version number/], + [ + qr/\Qpg_resetwal: warning: pg_control specifies invalid WAL segment size (0 bytes); proceed with caution\E/ + ], + 'processes zero WAL segment size'); + +# now try to run it +command_fails_like( + [ 'pg_resetwal', $node->data_dir ], + qr/not proceeding because control file values were guessed/, + 'does not run when control file values were guessed'); +command_ok([ 'pg_resetwal', '-f', $node->data_dir ], + 'runs with force when control file values were guessed'); + +done_testing();