|
| 1 | +# Copyright (c) 2021-2022, PostgreSQL Global Development Group |
| 2 | + |
| 3 | +use strict; |
| 4 | +use warnings; |
| 5 | + |
| 6 | +use PostgreSQL::Test::Cluster; |
| 7 | +use PostgreSQL::Test::Utils; |
| 8 | +use Test::More; |
| 9 | + |
| 10 | +# For testing purposes, we just want basebackup_to_shell to write standard |
| 11 | +# input to a file. However, Windows doesn't have "cat" or any equivalent, so |
| 12 | +# we use "gzip" for this purpose. |
| 13 | +my $gzip = $ENV{'GZIP_PROGRAM'}; |
| 14 | +if (!defined $gzip || $gzip eq '') |
| 15 | +{ |
| 16 | + plan skip_all => 'gzip not available'; |
| 17 | +} |
| 18 | + |
| 19 | +my $node = PostgreSQL::Test::Cluster->new('primary'); |
| 20 | +$node->init('allows_streaming' => 1); |
| 21 | +$node->append_conf('postgresql.conf', |
| 22 | + "shared_preload_libraries = 'basebackup_to_shell'"); |
| 23 | +$node->start; |
| 24 | +$node->safe_psql('postgres', 'CREATE USER backupuser REPLICATION'); |
| 25 | +$node->safe_psql('postgres', 'CREATE ROLE trustworthy'); |
| 26 | + |
| 27 | +# For nearly all pg_basebackup invocations some options should be specified, |
| 28 | +# to keep test times reasonable. Using @pg_basebackup_defs as the first |
| 29 | +# element of the array passed to IPC::Run interpolate the array (as it is |
| 30 | +# not a reference to an array)... |
| 31 | +my @pg_basebackup_defs = ('pg_basebackup', '--no-sync', '-cfast'); |
| 32 | + |
| 33 | +# This particular test module generally wants to run with -Xfetch, because |
| 34 | +# -Xstream is not supported with a backup target, and with -U backupuser. |
| 35 | +my @pg_basebackup_cmd = (@pg_basebackup_defs, '-U', 'backupuser', '-Xfetch'); |
| 36 | + |
| 37 | +# Can't use this module without setting basebackup_to_shell.command. |
| 38 | +$node->command_fails_like( |
| 39 | + [ @pg_basebackup_cmd, '--target', 'shell' ], |
| 40 | + qr/shell command for backup is not configured/, |
| 41 | + 'fails if basebackup_to_shell.command is not set'); |
| 42 | + |
| 43 | +# Configure basebackup_to_shell.command and reload the configuation file. |
| 44 | +my $backup_path = PostgreSQL::Test::Utils::tempdir; |
| 45 | +my $escaped_backup_path = $backup_path; |
| 46 | +$escaped_backup_path =~ s{\\}{\\\\}g if ($PostgreSQL::Test::Utils::windows_os); |
| 47 | +my $shell_command = |
| 48 | + $PostgreSQL::Test::Utils::windows_os |
| 49 | + ? qq{$gzip --fast > "$escaped_backup_path\\\\%f.gz"} |
| 50 | + : qq{$gzip --fast > "$escaped_backup_path/%f.gz"}; |
| 51 | +$node->append_conf('postgresql.conf', |
| 52 | + "basebackup_to_shell.command='$shell_command'"); |
| 53 | +$node->reload(); |
| 54 | + |
| 55 | +# Should work now. |
| 56 | +$node->command_ok( |
| 57 | + [ @pg_basebackup_cmd, '--target', 'shell' ], |
| 58 | + 'backup with no detail: pg_basebackup'); |
| 59 | +verify_backup('', $backup_path, "backup with no detail"); |
| 60 | + |
| 61 | +# Should fail with a detail. |
| 62 | +$node->command_fails_like( |
| 63 | + [ @pg_basebackup_cmd, '--target', 'shell:foo' ], |
| 64 | + qr/a target detail is not permitted because the configured command does not include %d/, |
| 65 | + 'fails if detail provided without %d'); |
| 66 | + |
| 67 | +# Reconfigure to restrict access and require a detail. |
| 68 | +$shell_command = |
| 69 | + $PostgreSQL::Test::Utils::windows_os |
| 70 | + ? qq{$gzip --fast > "$escaped_backup_path\\\\%d.%f.gz"} |
| 71 | + : qq{$gzip --fast > "$escaped_backup_path/%d.%f.gz"}; |
| 72 | +$node->append_conf('postgresql.conf', |
| 73 | + "basebackup_to_shell.command='$shell_command'"); |
| 74 | +$node->append_conf('postgresql.conf', |
| 75 | + "basebackup_to_shell.required_role='trustworthy'"); |
| 76 | +$node->reload(); |
| 77 | + |
| 78 | +# Should fail due to lack of permission. |
| 79 | +$node->command_fails_like( |
| 80 | + [ @pg_basebackup_cmd, '--target', 'shell' ], |
| 81 | + qr/permission denied to use basebackup_to_shell/, |
| 82 | + 'fails if required_role not granted'); |
| 83 | + |
| 84 | +# Should fail due to lack of a detail. |
| 85 | +$node->safe_psql('postgres', 'GRANT trustworthy TO backupuser'); |
| 86 | +$node->command_fails_like( |
| 87 | + [ @pg_basebackup_cmd, '--target', 'shell' ], |
| 88 | + qr/a target detail is required because the configured command includes %d/, |
| 89 | + 'fails if %d is present and detail not given'); |
| 90 | + |
| 91 | +# Should work. |
| 92 | +$node->command_ok( |
| 93 | + [ @pg_basebackup_cmd, '--target', 'shell:bar' ], |
| 94 | + 'backup with detail: pg_basebackup'); |
| 95 | +verify_backup('bar.', $backup_path, "backup with detail"); |
| 96 | + |
| 97 | +done_testing(); |
| 98 | + |
| 99 | +sub verify_backup |
| 100 | +{ |
| 101 | + my ($prefix, $backup_dir, $test_name) = @_; |
| 102 | + |
| 103 | + ok(-f "$backup_dir/${prefix}backup_manifest.gz", |
| 104 | + "$test_name: backup_manifest.gz was created"); |
| 105 | + ok(-f "$backup_dir/${prefix}base.tar.gz", |
| 106 | + "$test_name: base.tar.gz was created"); |
| 107 | + |
| 108 | + SKIP: { |
| 109 | + my $tar = $ENV{TAR}; |
| 110 | + skip "no tar program available", 1 if (!defined $tar || $tar eq ''); |
| 111 | + |
| 112 | + # Decompress. |
| 113 | + system_or_bail($gzip, '-d', |
| 114 | + $backup_dir . '/' . $prefix . 'backup_manifest.gz'); |
| 115 | + system_or_bail($gzip, '-d', |
| 116 | + $backup_dir . '/' . $prefix . 'base.tar.gz'); |
| 117 | + |
| 118 | + # Untar. |
| 119 | + my $extract_path = PostgreSQL::Test::Utils::tempdir; |
| 120 | + system_or_bail($tar, 'xf', $backup_dir . '/' . $prefix . 'base.tar', |
| 121 | + '-C', $extract_path); |
| 122 | + |
| 123 | + # Verify. |
| 124 | + $node->command_ok([ 'pg_verifybackup', '-n', |
| 125 | + '-m', "${backup_dir}/${prefix}backup_manifest", |
| 126 | + '-e', $extract_path ], |
| 127 | + "$test_name: backup verifies ok"); |
| 128 | + } |
| 129 | +} |
0 commit comments