From 43172d6e025af48a7ea31a8700d25455b20453c5 Mon Sep 17 00:00:00 2001
From: Shaoqi Bai <sbai@pivotal.io>
Date: Fri, 8 Mar 2019 18:04:20 +0800
Subject: [PATCH] Add tablespace tap test for pg_rewind

---
 src/bin/pg_rewind/t/006_tablespace.pl | 83 +++++++++++++++++++++++++++
 src/bin/pg_rewind/t/RewindTest.pm     | 26 +++++++++
 src/test/perl/PostgresNode.pm         | 26 +++++++++
 src/test/perl/RecursiveCopy.pm        | 18 +++++-
 4 files changed, 150 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/pg_rewind/t/006_tablespace.pl

diff --git a/src/bin/pg_rewind/t/006_tablespace.pl b/src/bin/pg_rewind/t/006_tablespace.pl
new file mode 100644
index 0000000000..19b3403c90
--- /dev/null
+++ b/src/bin/pg_rewind/t/006_tablespace.pl
@@ -0,0 +1,83 @@
+use strict;
+use warnings;
+use File::Path qw(rmtree);
+use TestLib;
+use Test::More tests => 6;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use RewindTest;
+
+my $tempdir = TestLib::tempdir;
+
+sub run_test
+{
+	my $test_mode = shift;
+
+	RewindTest::setup_cluster($test_mode, ['-g']);
+	RewindTest::start_master();
+
+	rmtree("$tempdir/inmaster");
+	rmtree("$tempdir/instandby");
+	rmtree("$tempdir/master_beforepromotion");
+	rmtree("$tempdir/master_afterpromotion");
+	rmtree("$tempdir/standby_afterpromotion");
+
+	mkdir "$tempdir/inmaster";
+	mkdir "$tempdir/instandby";
+
+	# Create a tablespace in master.
+	master_psql("CREATE TABLESPACE inmaster LOCATION '$tempdir/inmaster'");
+
+	RewindTest::create_standby_tbl_mapping($test_mode, "$tempdir/inmaster", "$tempdir/instandby");
+
+	mkdir "$tempdir/master_beforepromotion";
+
+	# Create a tablespace, it has to be droped before doing pg_rewind, or else pg_rewind will fail
+	master_psql("CREATE TABLESPACE master_beforepromotion LOCATION '$tempdir/master_beforepromotion'");
+
+	RewindTest::promote_standby();
+
+	mkdir "$tempdir/master_afterpromotion";
+	mkdir "$tempdir/standby_afterpromotion";
+
+	# Create tablespaces in the old master and the new promoted standby.
+	master_psql("CREATE TABLESPACE master_afterpromotion LOCATION '$tempdir/master_afterpromotion'");
+	standby_psql("CREATE TABLESPACE standby_afterpromotion LOCATION '$tempdir/standby_afterpromotion'");
+	# Drop tablespace in the new promoted standby, because pg_rewind can not handle this case.
+	standby_psql("DROP TABLESPACE standby_afterpromotion");
+
+	# The clusters are now diverged.
+
+	RewindTest::run_pg_rewind($test_mode);
+
+	# Check that the correct databases are present after pg_rewind.
+	check_query(
+		'SELECT spcname FROM pg_tablespace ORDER BY spcname',
+		qq(inmaster
+master_beforepromotion
+pg_default
+pg_global
+),
+		'tablespace names');
+
+	# Permissions on PGDATA should have group permissions
+  SKIP:
+	{
+		skip "unix-style permissions not supported on Windows", 1
+		  if ($windows_os);
+
+		ok(check_mode_recursive($node_master->data_dir(), 0750, 0640),
+			'check PGDATA permissions');
+	}
+
+	RewindTest::clean_rewind_test();
+	return;
+}
+
+# Run the test in both modes.
+run_test('local');
+run_test('remote');
+
+exit(0);
diff --git a/src/bin/pg_rewind/t/RewindTest.pm b/src/bin/pg_rewind/t/RewindTest.pm
index 85cae7e47b..81bf4d0e47 100644
--- a/src/bin/pg_rewind/t/RewindTest.pm
+++ b/src/bin/pg_rewind/t/RewindTest.pm
@@ -174,6 +174,32 @@ primary_conninfo='$connstr_master application_name=rewind_standby'
 	return;
 }
 
+sub create_standby_tbl_mapping
+{
+	my $extra_name = shift;
+
+	$node_standby =
+	  get_new_node('standby' . ($extra_name ? "_${extra_name}" : ''));
+	my ($olddir, $newdir) = @_;
+	$node_master->backup_tbl_mapping('my_backup', $olddir, $newdir);
+	$node_standby->init_from_backup($node_master, 'my_backup');
+	my $connstr_master = $node_master->connstr();
+	$node_standby->append_conf(
+		"postgresql.conf", qq(
+primary_conninfo='$connstr_master application_name=rewind_standby'
+));
+
+	$node_standby->set_standby_mode();
+
+	# Start standby
+	$node_standby->start;
+
+	# The standby may have WAL to apply before it matches the primary.  That
+	# is fine, because no test examines the standby before promotion.
+
+	return;
+}
+
 sub promote_standby
 {
 	#### Now run the test-specific parts to run after standby has been started
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 0634aefd20..8d2f0c044d 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -546,6 +546,32 @@ sub backup
 	return;
 }
 
+=item $node->backup_tblspc_mapping(backup_name, olddir, newdir)
+
+Create a hot backup with B<pg_basebackup> in subdirectory B<backup_name> of
+B<< $node->backup_dir >>, with configuration --tablespace-mapping=B<olddir>=B<newdir>
+, including the WAL. WAL files
+fetched at the end of the backup, not streamed.
+
+You'll have to configure a suitable B<max_wal_senders> on the
+target server since it isn't done by default.
+
+=cut
+
+sub backup_tbl_mapping
+{
+	my ($self, $backup_name, $olddir, $newdir) = @_;
+	my $backup_path = $self->backup_dir . '/' . $backup_name;
+	my $port        = $self->port;
+	my $name        = $self->name;
+
+	print "# Taking pg_basebackup $backup_name from node \"$name\"\n";
+	TestLib::system_or_bail('pg_basebackup', '-D', $backup_path, '-p', $port,
+		'-T', "$olddir=$newdir", '--no-sync');
+	print "# Backup finished\n";
+	return;
+}
+
 =item $node->backup_fs_hot(backup_name)
 
 Create a backup with a filesystem level copy in subdirectory B<backup_name> of
diff --git a/src/test/perl/RecursiveCopy.pm b/src/test/perl/RecursiveCopy.pm
index baf5d0ac63..1840de8c8e 100644
--- a/src/test/perl/RecursiveCopy.pm
+++ b/src/test/perl/RecursiveCopy.pm
@@ -99,7 +99,7 @@ sub _copypath_recurse
 
 	# Check for symlink -- needed only on source dir
 	# (note: this will fall through quietly if file is already gone)
-	croak "Cannot operate on symlink \"$srcpath\"" if -l $srcpath;
+	# croak "Cannot operate on symlink \"$srcpath\"" if -l $srcpath;
 
 	# Abort if destination path already exists.  Should we allow directories
 	# to exist already?
@@ -107,7 +107,7 @@ sub _copypath_recurse
 
 	# If this source path is a file, simply copy it to destination with the
 	# same name and we're done.
-	if (-f $srcpath)
+	if (-f $srcpath && !(-l $srcpath))
 	{
 		my $fh;
 		unless (open($fh, '<', $srcpath))
@@ -122,7 +122,7 @@ sub _copypath_recurse
 	}
 
 	# If it's a directory, create it on dest and recurse into it.
-	if (-d $srcpath)
+	if (-d $srcpath && !(-l $srcpath))
 	{
 		my $directory;
 		unless (opendir($directory, $srcpath))
@@ -145,6 +145,18 @@ sub _copypath_recurse
 		return 1;
 	}
 
+	if (-l $srcpath)
+	{
+         my $old_link = $srcpath;
+         my $new_link = $destpath;
+
+         my $dst = readlink($old_link);
+         my @stat = lstat($old_link);
+
+         symlink $dst, $new_link;
+         return 1;
+	}
+
 	# If it disappeared from sight, that's OK.
 	return 1 if !-e $srcpath;
 
-- 
2.19.1

