basebackup_to_shell: Add TAP test.
authorRobert Haas <rhaas@postgresql.org>
Wed, 30 Mar 2022 19:47:02 +0000 (15:47 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 30 Mar 2022 19:47:02 +0000 (15:47 -0400)
Per gripe from Andres Freund. Thanks to Andres Freund, Thomas
Munro, and Andrew Dunstan for advice on how to make this test
work even on Windows.

Discussion: https://postgr.es/m/CA+Tgmoat+zbzzZQJ7poXyUwiqxQxTaUid=auB4FejZ15VvDh4Q@mail.gmail.com

contrib/basebackup_to_shell/Makefile
contrib/basebackup_to_shell/t/001_basic.pl [new file with mode: 0644]

index f31dfaae9cca89305b11a99916ec9a8e3747dd15..58bd26991ae3a9ed83fc80ab18589afe416783f6 100644 (file)
@@ -7,6 +7,11 @@ OBJS = \
 
 PGFILEDESC = "basebackup_to_shell - target basebackup to shell command"
 
+TAP_TESTS = 1
+
+export GZIP_PROGRAM=$(GZIP)
+export TAR
+
 ifdef USE_PGXS
 PG_CONFIG = pg_config
 PGXS := $(shell $(PG_CONFIG) --pgxs)
diff --git a/contrib/basebackup_to_shell/t/001_basic.pl b/contrib/basebackup_to_shell/t/001_basic.pl
new file mode 100644 (file)
index 0000000..57534b6
--- /dev/null
@@ -0,0 +1,129 @@
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# For testing purposes, we just want basebackup_to_shell to write standard
+# input to a file.  However, Windows doesn't have "cat" or any equivalent, so
+# we use "gzip" for this purpose.
+my $gzip = $ENV{'GZIP_PROGRAM'};
+if (!defined $gzip || $gzip eq '')
+{
+   plan skip_all => 'gzip not available';
+}
+
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init('allows_streaming' => 1);
+$node->append_conf('postgresql.conf',
+                  "shared_preload_libraries = 'basebackup_to_shell'");
+$node->start;
+$node->safe_psql('postgres', 'CREATE USER backupuser REPLICATION');
+$node->safe_psql('postgres', 'CREATE ROLE trustworthy');
+
+# For nearly all pg_basebackup invocations some options should be specified,
+# to keep test times reasonable. Using @pg_basebackup_defs as the first
+# element of the array passed to IPC::Run interpolate the array (as it is
+# not a reference to an array)...
+my @pg_basebackup_defs = ('pg_basebackup', '--no-sync', '-cfast');
+
+# This particular test module generally wants to run with -Xfetch, because
+# -Xstream is not supported with a backup target, and with -U backupuser.
+my @pg_basebackup_cmd = (@pg_basebackup_defs, '-U', 'backupuser', '-Xfetch');
+
+# Can't use this module without setting basebackup_to_shell.command.
+$node->command_fails_like(
+    [ @pg_basebackup_cmd, '--target', 'shell' ],
+   qr/shell command for backup is not configured/,
+   'fails if basebackup_to_shell.command is not set');
+
+# Configure basebackup_to_shell.command and reload the configuation file.
+my $backup_path = PostgreSQL::Test::Utils::tempdir;
+my $escaped_backup_path = $backup_path;
+$escaped_backup_path =~ s{\\}{\\\\}g if ($PostgreSQL::Test::Utils::windows_os);
+my $shell_command =
+   $PostgreSQL::Test::Utils::windows_os
+   ? qq{$gzip --fast > "$escaped_backup_path\\\\%f.gz"}
+    : qq{$gzip --fast > "$escaped_backup_path/%f.gz"};
+$node->append_conf('postgresql.conf',
+                  "basebackup_to_shell.command='$shell_command'");
+$node->reload();
+
+# Should work now.
+$node->command_ok(
+    [ @pg_basebackup_cmd, '--target', 'shell' ],
+   'backup with no detail: pg_basebackup');
+verify_backup('', $backup_path, "backup with no detail");
+
+# Should fail with a detail.
+$node->command_fails_like(
+    [ @pg_basebackup_cmd, '--target', 'shell:foo' ],
+   qr/a target detail is not permitted because the configured command does not include %d/,
+   'fails if detail provided without %d');
+
+# Reconfigure to restrict access and require a detail.
+$shell_command =
+   $PostgreSQL::Test::Utils::windows_os
+   ? qq{$gzip --fast > "$escaped_backup_path\\\\%d.%f.gz"}
+    : qq{$gzip --fast > "$escaped_backup_path/%d.%f.gz"};
+$node->append_conf('postgresql.conf',
+                  "basebackup_to_shell.command='$shell_command'");
+$node->append_conf('postgresql.conf',
+                  "basebackup_to_shell.required_role='trustworthy'");
+$node->reload();
+
+# Should fail due to lack of permission.
+$node->command_fails_like(
+    [ @pg_basebackup_cmd, '--target', 'shell' ],
+   qr/permission denied to use basebackup_to_shell/,
+   'fails if required_role not granted');
+
+# Should fail due to lack of a detail.
+$node->safe_psql('postgres', 'GRANT trustworthy TO backupuser');
+$node->command_fails_like(
+    [ @pg_basebackup_cmd, '--target', 'shell' ],
+   qr/a target detail is required because the configured command includes %d/,
+   'fails if %d is present and detail not given');
+
+# Should work.
+$node->command_ok(
+    [ @pg_basebackup_cmd, '--target', 'shell:bar' ],
+   'backup with detail: pg_basebackup');
+verify_backup('bar.', $backup_path, "backup with detail");
+
+done_testing();
+
+sub verify_backup
+{
+   my ($prefix, $backup_dir, $test_name) = @_;
+
+   ok(-f "$backup_dir/${prefix}backup_manifest.gz",
+      "$test_name: backup_manifest.gz was created");
+   ok(-f "$backup_dir/${prefix}base.tar.gz",
+      "$test_name: base.tar.gz was created");
+
+   SKIP: {
+       my $tar = $ENV{TAR};
+       skip "no tar program available", 1 if (!defined $tar || $tar eq '');
+
+       # Decompress.
+       system_or_bail($gzip, '-d',
+                      $backup_dir . '/' . $prefix . 'backup_manifest.gz');
+       system_or_bail($gzip, '-d',
+                      $backup_dir . '/' . $prefix . 'base.tar.gz');
+
+       # Untar.
+       my $extract_path = PostgreSQL::Test::Utils::tempdir;
+       system_or_bail($tar, 'xf', $backup_dir . '/' . $prefix . 'base.tar',
+                      '-C', $extract_path);
+
+       # Verify.
+       $node->command_ok([ 'pg_verifybackup', '-n',
+                         '-m', "${backup_dir}/${prefix}backup_manifest",
+                         '-e', $extract_path ],
+                         "$test_name: backup verifies ok");
+   }
+}