summaryrefslogtreecommitdiffstats
path: root/t/030_errors.t
diff options
context:
space:
mode:
Diffstat (limited to 't/030_errors.t')
-rw-r--r--t/030_errors.t336
1 files changed, 336 insertions, 0 deletions
diff --git a/t/030_errors.t b/t/030_errors.t
new file mode 100644
index 0000000..88d3700
--- /dev/null
+++ b/t/030_errors.t
@@ -0,0 +1,336 @@
+# Check all kinds of error conditions.
+
+use strict;
+
+require File::Temp;
+
+use lib 't';
+use TestLib;
+use Test::More tests => 156;
+use PgCommon;
+
+my $version = $MAJORS[-1];
+
+my $socketdir = '/tmp/postgresql-testsuite/';
+my ($pg_uid, $pg_gid) = (getpwnam 'postgres')[2,3];
+
+# create a pid file with content $1 and return its path
+sub create_pidfile {
+ my $fname = "/var/lib/postgresql/$version/main/postmaster.pid";
+ open F, ">$fname" or die "open: $!";
+ print F $_[0];
+ close F;
+ chown $pg_uid, $pg_gid, $fname or die "chown: $!";
+ chmod 0700, $fname or die "chmod: $!";
+ return $fname;
+}
+
+sub check_nonexisting_cluster_error {
+ my $outref;
+ my $result = exec_as 0, $_[0], $outref;
+ is $result, 1, "'$_[0]' fails";
+ like $$outref, qr/(invalid version|does not exist)/i, "$_[0] gives error message about nonexisting cluster";
+ unlike $$outref, qr/invalid symbolic link/i, "$_[0] does not print 'invalid symbolic link' gibberish";
+}
+
+# check if pg_lsclusters shows a cluster without binaries
+mkdir "/etc/postgresql/6.3";
+mkdir "/etc/postgresql/6.3/main";
+open F, ">/etc/postgresql/6.3/main/postgresql.conf";
+close F;
+is `pg_lsclusters -h`, "6.3 main <unknown> down,binaries_missing <unknown> <unknown> <unknown>\n",
+ 'pg_lscluster reports cluster without binaries';
+program_ok 0, "pg_dropcluster 6.3 main";
+
+# create cluster
+ok ((system "pg_createcluster --socketdir '$socketdir' $version main >/dev/null") == 0,
+ "pg_createcluster --socketdir");
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/$version\s*main.*5432.*down/, 'cluster was created';
+
+is ((get_cluster_port $version, 'main'), 5432, 'Port of created cluster is 5432');
+
+# creating cluster with the same name should fail
+like_program_out 'root', "pg_createcluster --socketdir '$socketdir' $version main", 1, qr/already exists/,
+ "pg_createcluster on existing cluster";
+# and the original one still exists
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/$version\s*main.*5432.*down/, 'original cluster still exists';
+
+# attempt to create clusters with an invalid port
+like_program_out 0, "pg_createcluster $version test -p foo", 1,
+ qr/invalid.*number expected/,
+ 'pg_createcluster -p checks that port option is numeric';
+like_program_out 0, "pg_createcluster $version test -p 42", 1,
+ qr/must be a positive integer between/,
+ 'pg_createcluster -p checks valid port range';
+
+# chown cluster to an invalid user to test error
+(system "chown -R 0 /var/lib/postgresql/$version/main") == 0 or die "chown failed: $!";
+like_program_out 0, "pg_ctlcluster $version main start", 1, qr/must not be owned by root/,
+ "pg_ctlcluster refuses to start root-owned cluster";
+my $badid = 98;
+(system "chown -R $badid /var/lib/postgresql/$version/main") == 0 or die "chown failed: $!";
+like_program_out 0, "pg_ctlcluster $version main start", 1, qr/owned by user id 98 which does not exist/,
+ 'pg_ctlcluster fails on invalid cluster owner uid';
+(system "chown -R postgres:$badid /var/lib/postgresql/$version/main") == 0 or die "chown failed: $!";
+like_program_out 0, "pg_ctlcluster $version main start", 1, qr/owned by group id 98 which does not exist/,
+ 'pg_ctlcluster as root fails on invalid cluster owner gid';
+like_program_out 'postgres', "pg_ctlcluster $version main start", 1, qr/owned by group id 98 which does not exist/,
+ 'pg_ctlcluster as postgres fails on invalid cluster owner gid';
+(system "chown -R postgres:postgres /var/lib/postgresql/$version/main") == 0 or die "chown failed: $!";
+program_ok 0, "pg_ctlcluster $version main start", 0,
+ 'pg_ctlcluster succeeds on valid cluster owner uid/gid';
+
+# check socket
+my @contents = ('.s.PGSQL.5432', '.s.PGSQL.5432.lock', "$version-main.pid", "$version-main.pg_stat_tmp");
+pop @contents if ($version < 8.4 or $version >= 15); # remove pg_stat_tmp
+ok_dir '/var/run/postgresql', [grep {/main/} @contents], 'No sockets in /var/run/postgresql';
+ok_dir $socketdir, ['.s.PGSQL.5432', '.s.PGSQL.5432.lock'], "Socket is in $socketdir";
+
+# stop cluster, check sockets
+ok ((system "pg_ctlcluster $version main stop") == 0,
+ 'cluster stops with custom unix_socket_dir');
+ok_dir $socketdir, [], "No sockets in $socketdir after stopping cluster";
+
+# remove default socket dir and check that the socket defaults to
+# /var/run/postgresql
+open F, "+</etc/postgresql/$version/main/postgresql.conf" or
+ die "could not open postgresql.conf for r/w: $!";
+my @lines = <F>;
+seek F, 0, 0 or die "seek: $!";
+truncate F, 0;
+@lines = grep !/^unix_socket_dir/, @lines; # <= 9.2: "_directory", >= 9.3: "_directories"
+print F @lines;
+close F;
+
+ok ((system "pg_ctlcluster $version main start") == 0,
+ 'cluster starts after removing unix_socket_dir');
+if ($PgCommon::rpm) {
+ ok ((grep { $_ eq '.s.PGSQL.5432' } @{TestLib::dircontent('/tmp')}) == 1, 'Socket is in /tmp');
+} else {
+ ok_dir '/var/run/postgresql', [@contents],
+ 'Socket is in default dir /var/run/postgresql';
+}
+ok_dir $socketdir, [], "No sockets in $socketdir";
+
+# server should not stop with corrupt file
+rename "/var/lib/postgresql/$version/main/postmaster.pid",
+ "/var/lib/postgresql/$version/main/postmaster.pid.orig" or die "rename: $!";
+create_pidfile 'foo';
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 1,
+ "Error: pid file is invalid, please manually kill the stale server process.\n",
+ 'pg_ctlcluster fails with corrupted PID file';
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/online/, 'cluster is still online';
+
+# restore PID file
+(system "cp /var/lib/postgresql/$version/main/postmaster.pid.orig /var/lib/postgresql/$version/main/postmaster.pid") == 0 or die "cp: $!";
+is ((exec_as 'postgres', "pg_ctlcluster $version main stop"), 0,
+ 'pg_ctlcluster succeeds with restored PID file');
+mkdir $PgCommon::binroot . "foo"; # #940220: infinite recursion in get_program_path
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/down/, 'cluster is down';
+rmdir $PgCommon::binroot . "foo";
+
+# stop stopped server
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 2,
+ "Cluster is not running.\n", 'pg_ctlcluster stop fails on stopped cluster';
+
+# simulate crashed server
+rename "/var/lib/postgresql/$version/main/postmaster.pid.orig",
+ "/var/lib/postgresql/$version/main/postmaster.pid" or die "rename: $!";
+is_program_out 'postgres', "pg_ctlcluster $version main start", 0,
+ "Removed stale pid file.\n", 'pg_ctlcluster succeeds with already existing PID file';
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/online/, 'cluster is online';
+is ((exec_as 'postgres', "pg_ctlcluster $version main stop"), 0,
+ 'pg_ctlcluster stop succeeds');
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/down/, 'cluster is down';
+ok (! -e "/var/lib/postgresql/$version/main/postmaster.pid", 'no pid file left');
+
+# trying to stop a stopped server cleans up corrupt and stale pid files
+my $pf = create_pidfile 'foo';
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 2,
+ "Removed stale pid file.\nCluster is not running.\n",
+ 'pg_ctlcluster stop succeeds with corrupted PID file';
+ok (! -e $pf, 'pid file was cleaned up');
+
+create_pidfile 'foo';
+is_program_out 'postgres', "pg_ctlcluster --force $version main stop", 2,
+ "Removed stale pid file.\nCluster is not running.\n",
+ 'pg_ctlcluster --force stop succeeds with corrupted PID file';
+ok (! -e $pf, 'pid file was cleaned up');
+
+create_pidfile '99998';
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 2,
+ "Removed stale pid file.\nCluster is not running.\n",
+ 'pg_ctlcluster stop succeeds with stale PID file';
+ok (! -e $pf, 'pid file was cleaned up');
+
+create_pidfile '99998';
+is_program_out 'postgres', "pg_ctlcluster --force $version main stop", 2,
+ "Removed stale pid file.\nCluster is not running.\n",
+ 'pg_ctlcluster --force stop succeeds with stale PID file';
+ok (! -e $pf, 'pid file was cleaned up');
+
+create_pidfile '';
+is_program_out 'postgres', "pg_ctlcluster --force $version main stop", 2,
+ "Removed stale pid file.\nCluster is not running.\n",
+ 'pg_ctlcluster stop succeeds with empty PID file';
+ok (! -e $pf, 'pid file was cleaned up');
+
+# corrupt PID file while server is down
+create_pidfile 'foo';
+is_program_out 'postgres', "pg_ctlcluster $version main start", 0,
+ "Removed stale pid file.\n", 'pg_ctlcluster succeeds with corrupted PID file';
+like_program_out 'postgres', 'pg_lsclusters -h', 0, qr/online/, 'cluster is online';
+
+# start running server
+is_program_out 'postgres', "pg_ctlcluster $version main start", 2,
+ "Cluster is already running.\n", 'pg_ctlcluster start fails on running cluster';
+is ((exec_as 'postgres', "pg_ctlcluster $version main stop"), 0, 'pg_ctlcluster stop');
+
+# backup pg_hba.conf
+rename "/etc/postgresql/$version/main/pg_hba.conf",
+ "/etc/postgresql/$version/main/pg_hba.conf.orig" or die "rename: $!";
+
+# test check for invalid pg_hba.conf
+open F, ">/etc/postgresql/$version/main/pg_hba.conf" or die "could not create pg_hba.conf: $!";
+print F "foo\n";
+close F;
+chmod 0644, "/etc/postgresql/$version/main/pg_hba.conf" or die "chmod: $!";
+
+if ($version < '8.4') {
+ like_program_out 'postgres', "pg_ctlcluster $version main start", 0,
+ qr/WARNING.*connection to the database failed.*pg_hba.conf/is,
+ 'pg_ctlcluster start warns about invalid pg_hba.conf';
+ is_program_out 'postgres', "pg_ctlcluster $version main stop", 0, '', 'stopping cluster';
+} else {
+ like_program_out 'postgres', "pg_ctlcluster $version main start", 1,
+ qr/FATAL.*pg_hba.conf/is,
+ 'pg_ctlcluster start fails on invalid pg_hba.conf';
+ is_program_out 'postgres', "pg_ctlcluster $version main stop", 2,
+ "Cluster is not running.\n", 'stopping cluster';
+}
+
+# test check for pg_hba.conf with removed passwordless local superuser access
+open F, ">/etc/postgresql/$version/main/pg_hba.conf" or die "could not create pg_hba.conf: $!";
+print F "local all all md5\n";
+close F;
+chmod 0644, "/etc/postgresql/$version/main/pg_hba.conf" or die "chmod: $!";
+
+like_program_out 'postgres', "pg_ctlcluster $version main start", 0,
+ qr/Warning.*connection to the database failed.*(no password supplied|password authentication failed)/is,
+ 'pg_ctlcluster start warns about absence of passwordless superuser connection';
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 0, '', 'stopping cluster';
+
+# restore pg_hba.conf
+unlink "/etc/postgresql/$version/main/pg_hba.conf";
+rename "/etc/postgresql/$version/main/pg_hba.conf.orig",
+ "/etc/postgresql/$version/main/pg_hba.conf" or die "rename: $!";
+
+# leftover files must not create confusion
+open F, '>/etc/postgresql/postgresql.conf';
+print F "data_directory = '/nonexisting'\n";
+close F;
+my @c = get_version_clusters $version;
+is_deeply (\@c, ['main'],
+ 'leftover /etc/postgresql/postgresql.conf is not regarded as a cluster');
+unlink '/etc/postgresql/postgresql.conf';
+
+# fails by default due to access restrictions
+# remove cluster and directory; this should work as user "postgres"
+is_program_out 'postgres', "pg_dropcluster $version main", 0, '',
+ , "pg_dropcluster works as user postgres";
+
+# graceful handling of absent data dir (might not be mounted)
+ok ((system "pg_createcluster $version main >/dev/null") == 0,
+ "pg_createcluster succeeds");
+rename "/var/lib/postgresql/$version", "/var/lib/postgresql/$version.orig" or die "rename: $!";
+my $outref;
+is ((exec_as 0, "pg_ctlcluster $version main start", $outref, 1), 1,
+ 'pg_ctlcluster fails on nonexisting /var/lib/postgresql');
+like $$outref, qr/^Error:.*\/var\/lib\/postgresql.*not accessible.*$/, 'proper error message for nonexisting /var/lib/postgresql';
+
+rename "/var/lib/postgresql/$version.orig", "/var/lib/postgresql/$version" or die "rename: $!";
+is_program_out 'postgres', "pg_ctlcluster $version main start", 0, '',
+ 'pg_ctlcluster start succeeds again with reappeared /var/lib/postgresql';
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 0, '', 'stopping cluster';
+
+# pg_ctlcluster checks colliding ports
+ok ((system "pg_createcluster $version other >/dev/null") == 0,
+ "pg_createcluster other");
+set_cluster_port $version, 'other', '5432';
+is ((exec_as 'postgres', "pg_ctlcluster $version main start"), 0,
+ 'pg_ctlcluster: main cluster on conflicting port starts');
+
+# clusters can run side by side on different socket directories
+set_cluster_socketdir $version, 'other', $socketdir;
+PgCommon::set_conf_value $version, 'other', 'postgresql.conf',
+ 'listen_addresses', ''; # otherwise they will conflict on TCP socket
+is ((exec_as 'postgres', "pg_ctlcluster $version other start"), 0,
+ 'pg_ctlcluster: other cluster starts on conflicting port, but different socket dirs');
+is ((exec_as 'postgres', "pg_ctlcluster $version other stop"), 0);
+
+# ... but will give an error when running on the same port
+set_cluster_socketdir $version, 'other', ($PgCommon::rpm and $version < 9.4) ? '/tmp' : '/var/run/postgresql';
+like_program_out 'postgres', "pg_ctlcluster $version other start", 1,
+ qr/Port conflict:.*port 5432/,
+ 'pg_ctlcluster other cluster fails on conflicting port and same socket dir';
+is_program_out 'postgres', "pg_ctlcluster $version main stop", 0, '',
+ 'stopping main cluster';
+is ((exec_as 'postgres', "pg_ctlcluster $version other start"), 0,
+ 'pg_ctlcluster: other cluster on conflicting port starts after main is down');
+ok ((system "pg_dropcluster $version other --stop") == 0,
+ 'pg_dropcluster other');
+
+# clean up
+ok ((system "pg_dropcluster $version main") == 0,
+ 'pg_dropcluster');
+ok_dir $socketdir, [], 'No sockets any more';
+rmdir $socketdir or die "rmdir: $!";
+
+# ensure sane error messages for nonexisting clusters
+check_nonexisting_cluster_error 'pg_lsclusters 4.5 foo';
+check_nonexisting_cluster_error 'psql --cluster 4.5/foo';
+check_nonexisting_cluster_error "psql --cluster $MAJORS[0]/foo";
+check_nonexisting_cluster_error "pg_dropcluster 4.5 foo";
+check_nonexisting_cluster_error "pg_dropcluster $MAJORS[0] foo";
+check_nonexisting_cluster_error "pg_upgradecluster 4.5 foo";
+check_nonexisting_cluster_error "pg_upgradecluster $MAJORS[0] foo";
+check_nonexisting_cluster_error "pg_ctlcluster 4.5 foo stop";
+check_nonexisting_cluster_error "pg_ctlcluster $MAJORS[0] foo stop";
+
+check_clean;
+
+# check that pg_dropcluster copes with partially existing cluster
+# configurations (which can happen if the disk becomes full)
+
+mkdir '/etc/postgresql/';
+mkdir "/etc/postgresql/$MAJORS[-1]";
+mkdir "/etc/postgresql/$MAJORS[-1]/broken" or die "mkdir: $!";
+symlink "/var/lib/postgresql/$MAJORS[-1]/broken", "/etc/postgresql/$MAJORS[-1]/broken/pgdata" or die "symlink: $!";
+
+unlike_program_out 0, "pg_dropcluster $MAJORS[-1] broken", 0, qr/error/i,
+ 'pg_dropcluster cleans up broken cluster configuration (only /etc with pgdata)';
+
+check_clean;
+
+mkdir '/etc/postgresql/';
+mkdir '/var/lib/postgresql/';
+mkdir "/etc/postgresql/$MAJORS[-1]" and
+mkdir "/etc/postgresql/$MAJORS[-1]/broken";
+mkdir "/var/lib/postgresql/$MAJORS[-1]";
+mkdir "/var/lib/postgresql/$MAJORS[-1]/broken";
+chown $pg_uid, $pg_gid, "/var/lib/postgresql/$MAJORS[-1]/broken";
+mkdir "/var/lib/postgresql/$MAJORS[-1]/broken/base" or die "mkdir: $!";
+open F, ">/etc/postgresql/$MAJORS[-1]/broken/postgresql.conf" or die "open: $!";
+print F "data_directory = '/var/lib/postgresql/$MAJORS[-1]/broken'\n";
+close F;
+open F, ">/var/lib/postgresql/$MAJORS[-1]/broken/PG_VERSION" or die "open: $!";
+close F;
+
+unlike_program_out 0, "pg_dropcluster $MAJORS[-1] broken", 0, qr/error/i,
+ 'pg_dropcluster cleans up broken cluster configuration (/etc with pgdata and postgresql.conf and partial /var)';
+is -d "/etc/postgresql/$MAJORS[-1]", undef, "/etc/postgresql/$MAJORS[-1] was removed";
+is -d "/var/lib/postgresql/$MAJORS[-1]", undef, "/var/lib/postgresql/$MAJORS[-1] was removed";
+
+check_clean;
+
+# vim: filetype=perl