|
| 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 ||$gzipeq'') |
| 15 | +{ |
| 16 | +planskip_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{\\}{\\\\}gif ($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 | +subverify_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", 1if (!defined$tar ||$tareq''); |
| 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 | +} |