v10-0004-Regression-tests-for-pg_validatebackup.patch

application/octet-stream

Filename: v10-0004-Regression-tests-for-pg_validatebackup.patch
Type: application/octet-stream
Part: 0
Message: Re: backup manifests

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v10-0004
Subject: Regression tests for pg_validatebackup.
File+
src/bin/pg_validatebackup/.gitignore 1 0
src/bin/pg_validatebackup/Makefile 6 0
src/bin/pg_validatebackup/t/001_basic.pl 27 0
src/bin/pg_validatebackup/t/002_algorithm.pl 55 0
src/bin/pg_validatebackup/t/003_corruption.pl 178 0
From 0fcfaa3691f681a5470399f97ab674873567bf48 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Tue, 10 Mar 2020 13:44:46 -0400
Subject: [PATCH v10 4/4] Regression tests for pg_validatebackup.

Patch by me, based in part on ideas from Mark Dilger and
Rajkumar Raghuwanshi.
---
 src/bin/pg_validatebackup/.gitignore          |   1 +
 src/bin/pg_validatebackup/Makefile            |   6 +
 src/bin/pg_validatebackup/t/001_basic.pl      |  27 +++
 src/bin/pg_validatebackup/t/002_algorithm.pl  |  55 ++++++
 src/bin/pg_validatebackup/t/003_corruption.pl | 178 ++++++++++++++++++
 5 files changed, 267 insertions(+)
 create mode 100644 src/bin/pg_validatebackup/t/001_basic.pl
 create mode 100644 src/bin/pg_validatebackup/t/002_algorithm.pl
 create mode 100644 src/bin/pg_validatebackup/t/003_corruption.pl

diff --git a/src/bin/pg_validatebackup/.gitignore b/src/bin/pg_validatebackup/.gitignore
index 3ae1c1f03a..21e0a92429 100644
--- a/src/bin/pg_validatebackup/.gitignore
+++ b/src/bin/pg_validatebackup/.gitignore
@@ -1 +1,2 @@
 /pg_validatebackup
+/tmp_check/
diff --git a/src/bin/pg_validatebackup/Makefile b/src/bin/pg_validatebackup/Makefile
index dde7eb3c02..04ef7d3051 100644
--- a/src/bin/pg_validatebackup/Makefile
+++ b/src/bin/pg_validatebackup/Makefile
@@ -31,3 +31,9 @@ uninstall:
 
 clean distclean maintainer-clean:
 	rm -f pg_validatebackup$(X) $(OBJS)
+
+check:
+	$(prove_check)
+
+installcheck:
+	$(prove_installcheck)
diff --git a/src/bin/pg_validatebackup/t/001_basic.pl b/src/bin/pg_validatebackup/t/001_basic.pl
new file mode 100644
index 0000000000..49153f89ac
--- /dev/null
+++ b/src/bin/pg_validatebackup/t/001_basic.pl
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 14;
+
+my $tempdir = TestLib::tempdir;
+
+program_help_ok('pg_validatebackup');
+program_version_ok('pg_validatebackup');
+program_options_handling_ok('pg_validatebackup');
+
+command_fails_like(['pg_validatebackup'],
+				   qr/no backup directory specified/,
+				   'pg_validatebackup needs target directory specified');
+command_fails_like(['pg_validatebackup', $tempdir],
+				   qr/could not open file.*\/backup_manifest\"/,
+				   'pg_validatebackup requires a manifest');
+
+# create fake manifest file
+open(my $fh, '>', "$tempdir/backup_manifest") || die "open: $!";
+close($fh);
+
+# but then try to use an alternate, nonexisting manifest
+command_fails_like(['pg_validatebackup', '-m', "$tempdir/not_the_manifest",
+						$tempdir],
+				   qr/could not open file.*\/not_the_manifest\"/,
+				   'pg_validatebackup respects -m flag');
diff --git a/src/bin/pg_validatebackup/t/002_algorithm.pl b/src/bin/pg_validatebackup/t/002_algorithm.pl
new file mode 100644
index 0000000000..9bb89d3855
--- /dev/null
+++ b/src/bin/pg_validatebackup/t/002_algorithm.pl
@@ -0,0 +1,55 @@
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use File::Path qw(rmtree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 19;
+
+my $master = get_new_node('master');
+$master->init(allows_streaming => 1);
+$master->start;
+
+for my $algorithm (qw(bogus none crc32c sha224 sha256 sha384 sha512))
+{
+	my $backup_path = $master->backup_dir . '/' . $algorithm;
+	my @backup = ('pg_basebackup', '-D', $backup_path, '-m', $algorithm,
+				  '--no-sync');
+	my @validate = ('pg_validatebackup', '-e', $backup_path);
+
+	# A backup with a bogus algorithm should fail.
+	if ($algorithm eq 'bogus')
+	{
+		$master->command_fails(\@backup,
+							   "backup fails with algorithm \"$algorithm\"");
+		next;
+	}
+
+	# A backup with a valid algorithm should work.
+	$master->command_ok(\@backup, "backup ok with algorithm \"$algorithm\"");
+
+	# We expect each real checksum algorithm to be mentioned on every line of
+	# the backup manifest file except the first and last; for simplicity, we
+	# just check that it shows up lots of times. When the checksum algorithm
+	# is none, we just check that the manifest exists.
+	if ($algorithm eq 'none')
+	{
+		ok(-f "$backup_path/backup_manifest", "backup manifest exists");
+	}
+	else
+	{
+		my $manifest = slurp_file("$backup_path/backup_manifest");
+		my $count_of_algorithm_in_manifest =
+			(() = $manifest =~ /$algorithm/mig);
+		cmp_ok($count_of_algorithm_in_manifest, '>', 100,
+			   "$algorithm is mentioned many times in the manifest");
+	}
+
+	# Make sure that it validates OK.
+	$master->command_ok(\@validate,
+						"validate backup with algorithm \"$algorithm\"");
+
+	# Remove backup immediately to save disk space.
+	rmtree($backup_path);
+}
diff --git a/src/bin/pg_validatebackup/t/003_corruption.pl b/src/bin/pg_validatebackup/t/003_corruption.pl
new file mode 100644
index 0000000000..07ed7b6657
--- /dev/null
+++ b/src/bin/pg_validatebackup/t/003_corruption.pl
@@ -0,0 +1,178 @@
+use strict;
+use warnings;
+use Cwd;
+use Config;
+use File::Path qw(rmtree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 32;
+
+my $master = get_new_node('master');
+$master->init(allows_streaming => 1);
+$master->start;
+
+# Include a user-defined tablespace in the hopes of detecting problems in that
+# area.
+my $source_ts_path = TestLib::tempdir;
+$master->safe_psql('postgres', <<EOM);
+CREATE TABLE x1 (a int);
+INSERT INTO x1 VALUES (111);
+CREATE TABLESPACE ts1 LOCATION '$source_ts_path';
+CREATE TABLE x2 (a int) TABLESPACE ts1;
+INSERT INTO x1 VALUES (222);
+EOM
+
+my @scenario = (
+	{
+		'name' => 'extra_file',
+		'mutilate' => \&mutilate_extra_file,
+		'fails_like' =>
+			qr/extra_file.*present on disk but not in the manifest/
+	},
+	{
+		'name' => 'extra_tablespace_file',
+		'mutilate' => \&mutilate_extra_tablespace_file,
+		'fails_like' =>
+			qr/extra_ts_file.*present on disk but not in the manifest/
+	},
+	{
+		'name' => 'missing_file',
+		'mutilate' => \&mutilate_missing_file,
+		'fails_like' =>
+			qr/pg_xact\/0000.*present in the manifest but not on disk/
+	},
+	{
+		'name' => 'missing_tablespace',
+		'mutilate' => \&mutilate_missing_tablespace,
+		'fails_like' =>
+			qr/pg_tblspc.*present in the manifest but not on disk/
+	},
+	{
+		'name' => 'append_to_file',
+		'mutilate' => \&mutilate_append_to_file,
+		'fails_like' =>
+			qr/has size \d+ on disk but size \d+ in the manifest/
+	},
+	{
+		'name' => 'truncate_file',
+		'mutilate' => \&mutilate_truncate_file,
+		'fails_like' =>
+			qr/has size 0 on disk but size \d+ in the manifest/
+	},
+	{
+		'name' => 'replace_file',
+		'mutilate' => \&mutilate_replace_file,
+		'fails_like' => qr/checksum mismatch for file/
+	},
+	{
+		'name' => 'bad_manifest',
+		'mutilate' => \&mutilate_bad_manifest,
+		'fails_like' => qr/manifest checksum mismatch/
+	}
+);
+
+for my $scenario (@scenario)
+{
+	my $name = $scenario->{'name'};
+
+	# Take a backup and check that it validates OK.
+	my $backup_path = $master->backup_dir . '/' . $name;
+	my $backup_ts_path = TestLib::tempdir;
+	$master->command_ok(['pg_basebackup', '-D', $backup_path, '--no-sync',
+						'-T', "${source_ts_path}=${backup_ts_path}"],
+						"base backup ok");
+	command_ok(['pg_validatebackup', $backup_path ],
+			   "intact backup validated");
+
+	# Mutilate the backup in some way.
+	$scenario->{'mutilate'}->($backup_path);
+
+	# Now check that the backup no longer validates.
+	command_fails_like(['pg_validatebackup', $backup_path ],
+					   $scenario->{'fails_like'},
+					   "corrupt backup fails validation: $name");
+}
+
+sub create_extra_file
+{
+	my ($backup_path, $relative_path) = @_;
+	my $pathname = "$backup_path/$relative_path";
+	open(my $fh, '>', $pathname) || die "open $pathname: $!";
+	print $fh "This is an extra file.\n";
+	close($fh);
+}
+
+# Add a file into the root directory of the backup.
+sub mutilate_extra_file
+{
+	my ($backup_path) = @_;
+	create_extra_file($backup_path, "extra_file");
+}
+
+# Add a file inside the user-defined tablespace.
+sub mutilate_extra_tablespace_file
+{
+	my ($backup_path) = @_;
+	my ($tsoid) = grep { $_ ne '.' && $_ ne '..' }
+		 slurp_dir("$backup_path/pg_tblspc");
+	my ($catvdir) = grep { $_ ne '.' && $_ ne '..' }
+		 slurp_dir("$backup_path/pg_tblspc/$tsoid");
+	my ($tsdboid) = grep { $_ ne '.' && $_ ne '..' }
+		 slurp_dir("$backup_path/pg_tblspc/$tsoid/$catvdir");
+	create_extra_file($backup_path,
+					  "pg_tblspc/$tsoid/$catvdir/$tsdboid/extra_ts_file");
+}
+
+# Remove a file.
+sub mutilate_missing_file
+{
+	my ($backup_path) = @_;
+	my $pathname = "$backup_path/pg_xact/0000";
+	unlink($pathname) || die "$pathname: $!";
+}
+
+# Remove the symlink to the user-defined tablespace.
+sub mutilate_missing_tablespace
+{
+	my ($backup_path) = @_;
+	my ($tsoid) = grep { $_ ne '.' && $_ ne '..' }
+		 slurp_dir("$backup_path/pg_tblspc");
+	my $pathname = "$backup_path/pg_tblspc/$tsoid";
+	unlink($pathname) || die "$pathname: $!";
+}
+
+# Append an additional bytes to a file.
+sub mutilate_append_to_file
+{
+	my ($backup_path) = @_;
+	append_to_file "$backup_path/global/pg_control", 'x';
+}
+
+# Truncate a file to zero length.
+sub mutilate_truncate_file
+{
+	my ($backup_path) = @_;
+	my $pathname = "$backup_path/global/pg_control";
+	open(my $fh, '>', $pathname) || die "open $pathname: $!";
+	close($fh);
+}
+
+# Replace a file's contents without changing the length of the file. This is
+# not a particularly efficient way to do this, so we pick a file that's
+# expected to be short.
+sub mutilate_replace_file
+{
+	my ($backup_path) = @_;
+	my $pathname = "$backup_path/PG_VERSION";
+	my $contents = slurp_file($pathname);
+	open(my $fh, '>', $pathname) || die "open $pathname: $!";
+	print $fh 'q' x length($contents);
+	close($fh);
+}
+
+# Corrupt the backup manifest.
+sub mutilate_bad_manifest
+{
+	my ($backup_path) = @_;
+	append_to_file "$backup_path/backup_manifest", "\n";
+}
-- 
2.17.2 (Apple Git-113)