diff options
Diffstat (limited to 'src/test/perl/PostgresNode.pm')
-rw-r--r-- | src/test/perl/PostgresNode.pm | 184 |
1 files changed, 147 insertions, 37 deletions
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm index 81deed98430..f6570639e00 100644 --- a/src/test/perl/PostgresNode.pm +++ b/src/test/perl/PostgresNode.pm @@ -104,7 +104,8 @@ our @EXPORT = qw( get_new_node ); -our ($test_localhost, $test_pghost, $last_port_assigned, @all_nodes, $died); +our ($use_tcp, $test_localhost, $test_pghost, $last_host_assigned, + $last_port_assigned, @all_nodes, $died); # Windows path to virtual file system root @@ -118,13 +119,14 @@ if ($Config{osname} eq 'msys') INIT { - # PGHOST is set once and for all through a single series of tests when - # this module is loaded. - $test_localhost = "127.0.0.1"; - $test_pghost = - $TestLib::windows_os ? $test_localhost : TestLib::tempdir_short; - $ENV{PGHOST} = $test_pghost; - $ENV{PGDATABASE} = 'postgres'; + # Set PGHOST for backward compatibility. This doesn't work for own_host + # nodes, so prefer to not rely on this when writing new tests. + $use_tcp = $TestLib::windows_os; + $test_localhost = "127.0.0.1"; + $last_host_assigned = 1; + $test_pghost = $use_tcp ? $test_localhost : TestLib::tempdir_short; + $ENV{PGHOST} = $test_pghost; + $ENV{PGDATABASE} = 'postgres'; # Tracking of last port value assigned to accelerate free port lookup. $last_port_assigned = int(rand() * 16384) + 49152; @@ -155,7 +157,9 @@ sub new _host => $pghost, _basedir => "$TestLib::tmp_check/t_${testname}_${name}_data", _name => $name, - _logfile => "$TestLib::log_path/${testname}_${name}.log" + _logfile_generation => 0, + _logfile_base => "$TestLib::log_path/${testname}_${name}", + _logfile => "$TestLib::log_path/${testname}_${name}.log" }; bless $self, $class; @@ -473,8 +477,9 @@ sub init print $conf "max_wal_senders = 0\n"; } - if ($TestLib::windows_os) + if ($use_tcp) { + print $conf "unix_socket_directories = ''\n"; print $conf "listen_addresses = '$host'\n"; } else @@ -536,12 +541,11 @@ sub backup { my ($self, $backup_name) = @_; 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, - '--no-sync'); + TestLib::system_or_bail('pg_basebackup', '-D', $backup_path, '-h', + $self->host, '-p', $self->port, '--no-sync'); print "# Backup finished\n"; return; } @@ -651,6 +655,7 @@ sub init_from_backup { my ($self, $root_node, $backup_name, %params) = @_; my $backup_path = $root_node->backup_dir . '/' . $backup_name; + my $host = $self->host; my $port = $self->port; my $node_name = $self->name; my $root_name = $root_node->name; @@ -677,6 +682,15 @@ sub init_from_backup qq( port = $port )); + if ($use_tcp) + { + $self->append_conf('postgresql.conf', "listen_addresses = '$host'"); + } + else + { + $self->append_conf('postgresql.conf', + "unix_socket_directories = '$host'"); + } $self->enable_streaming($root_node) if $params{has_streaming}; $self->enable_restoring($root_node) if $params{has_restoring}; return; @@ -684,17 +698,45 @@ port = $port =pod -=item $node->start() +=item $node->rotate_logfile() + +Switch to a new PostgreSQL log file. This does not alter any running +PostgreSQL process. Subsequent method calls, including pg_ctl invocations, +will use the new name. Return the new name. + +=cut + +sub rotate_logfile +{ + my ($self) = @_; + $self->{_logfile} = sprintf('%s_%d.log', + $self->{_logfile_base}, + ++$self->{_logfile_generation}); + return $self->{_logfile}; +} + +=pod + +=item $node->start(%params) => success_or_failure Wrapper for pg_ctl start Start the node and wait until it is ready to accept connections. +=over + +=item fail_ok => 1 + +By default, failure terminates the entire F<prove> invocation. If given, +instead return a true or false value to indicate success or failure. + +=back + =cut sub start { - my ($self) = @_; + my ($self, %params) = @_; my $port = $self->port; my $pgdata = $self->data_dir; my $name = $self->name; @@ -721,10 +763,34 @@ sub start { print "# pg_ctl start failed; logfile:\n"; print TestLib::slurp_file($self->logfile); - BAIL_OUT("pg_ctl start failed"); + BAIL_OUT("pg_ctl start failed") unless $params{fail_ok}; + return 0; } $self->_update_pid(1); + return 1; +} + +=pod + +=item $node->kill9() + +Send SIGKILL (signal 9) to the postmaster. + +Note: if the node is already known stopped, this does nothing. +However, if we think it's running and it's not, it's important for +this to fail. Otherwise, tests might fail to detect server crashes. + +=cut + +sub kill9 +{ + my ($self) = @_; + my $name = $self->name; + return unless defined $self->{_pid}; + print "### Killing node \"$name\" using signal 9\n"; + kill(9, $self->{_pid}) or BAIL_OUT("kill(9, $self->{_pid}) failed"); + $self->{_pid} = undef; return; } @@ -965,7 +1031,7 @@ sub _update_pid =pod -=item PostgresNode->get_new_node(node_name) +=item PostgresNode->get_new_node(node_name, %params) Build a new object of class C<PostgresNode> (or of a subclass, if you have one), assigning a free port number. Remembers the node, to prevent its port @@ -974,6 +1040,22 @@ shut down when the test script exits. You should generally use this instead of C<PostgresNode::new(...)>. +=over + +=item port => [1,65535] + +By default, this function assigns a port number to each node. Specify this to +force a particular port number. The caller is responsible for evaluating +potential conflicts and privilege requirements. + +=item own_host => 1 + +By default, all nodes use the same PGHOST value. If specified, generate a +PGHOST specific to this node. This allows multiple nodes to use the same +port. + +=back + For backwards compatibility, it is also exported as a standalone function, which can only create objects of class C<PostgresNode>. @@ -982,10 +1064,11 @@ which can only create objects of class C<PostgresNode>. sub get_new_node { my $class = 'PostgresNode'; - $class = shift if 1 < scalar @_; - my $name = shift; - my $found = 0; - my $port = $last_port_assigned; + $class = shift if scalar(@_) % 2 != 1; + my ($name, %params) = @_; + my $port_is_forced = defined $params{port}; + my $found = $port_is_forced; + my $port = $port_is_forced ? $params{port} : $last_port_assigned; while ($found == 0) { @@ -1002,13 +1085,15 @@ sub get_new_node $found = 0 if ($node->port == $port); } - # Check to see if anything else is listening on this TCP port. - # This is *necessary* on Windows, and seems like a good idea - # on Unixen as well, even though we don't ask the postmaster - # to open a TCP port on Unix. + # Check to see if anything else is listening on this TCP port. Accept + # only ports available for all possible listen_addresses values, so + # the caller can harness this port for the widest range of purposes. + # This is *necessary* on Windows, and seems like a good idea on Unixen + # as well, even though we don't ask the postmaster to open a TCP port + # on Unix. if ($found == 1) { - my $iaddr = inet_aton($test_localhost); + my $iaddr = inet_aton('0.0.0.0'); my $paddr = sockaddr_in($port, $iaddr); my $proto = getprotobyname("tcp"); @@ -1024,16 +1109,35 @@ sub get_new_node } } - print "# Found free port $port\n"; + print "# Found port $port\n"; + + # Select a host. + my $host = $test_pghost; + if ($params{own_host}) + { + if ($use_tcp) + { + # This assumes $use_tcp platforms treat every address in + # 127.0.0.1/24, not just 127.0.0.1, as a usable loopback. + $last_host_assigned++; + $last_host_assigned > 254 and BAIL_OUT("too many own_host nodes"); + $host = '127.0.0.' . $last_host_assigned; + } + else + { + $host = "$test_pghost/$name"; # Assume $name =~ /^[-_a-zA-Z0-9]+$/ + mkdir $host; + } + } # Lock port number found by creating a new node - my $node = $class->new($name, $test_pghost, $port); + my $node = $class->new($name, $host, $port); # Add node to list of nodes push(@all_nodes, $node); # And update port for next time - $last_port_assigned = $port; + $port_is_forced or $last_port_assigned = $port; return $node; } @@ -1424,9 +1528,8 @@ $stderr); =item $node->command_ok(...) -Runs a shell command like TestLib::command_ok, but with PGPORT -set so that the command will default to connecting to this -PostgresNode. +Runs a shell command like TestLib::command_ok, but with PGHOST and PGPORT set +so that the command will default to connecting to this PostgresNode. =cut @@ -1436,6 +1539,7 @@ sub command_ok my $self = shift; + local $ENV{PGHOST} = $self->host; local $ENV{PGPORT} = $self->port; TestLib::command_ok(@_); @@ -1446,7 +1550,7 @@ sub command_ok =item $node->command_fails(...) -TestLib::command_fails with our PGPORT. See command_ok(...) +TestLib::command_fails with our connection parameters. See command_ok(...) =cut @@ -1456,6 +1560,7 @@ sub command_fails my $self = shift; + local $ENV{PGHOST} = $self->host; local $ENV{PGPORT} = $self->port; TestLib::command_fails(@_); @@ -1466,7 +1571,7 @@ sub command_fails =item $node->command_like(...) -TestLib::command_like with our PGPORT. See command_ok(...) +TestLib::command_like with our connection parameters. See command_ok(...) =cut @@ -1476,6 +1581,7 @@ sub command_like my $self = shift; + local $ENV{PGHOST} = $self->host; local $ENV{PGPORT} = $self->port; TestLib::command_like(@_); @@ -1486,7 +1592,8 @@ sub command_like =item $node->command_checks_all(...) -TestLib::command_checks_all with our PGPORT. See command_ok(...) +TestLib::command_checks_all with our connection parameters. See +command_ok(...) =cut @@ -1496,6 +1603,7 @@ sub command_checks_all my $self = shift; + local $ENV{PGHOST} = $self->host; local $ENV{PGPORT} = $self->port; TestLib::command_checks_all(@_); @@ -1520,6 +1628,7 @@ sub issues_sql_like my ($self, $cmd, $expected_sql, $test_name) = @_; + local $ENV{PGHOST} = $self->host; local $ENV{PGPORT} = $self->port; truncate $self->logfile, 0; @@ -1534,8 +1643,8 @@ sub issues_sql_like =item $node->run_log(...) -Runs a shell command like TestLib::run_log, but with PGPORT set so -that the command will default to connecting to this PostgresNode. +Runs a shell command like TestLib::run_log, but with connection parameters set +so that the command will default to connecting to this PostgresNode. =cut @@ -1543,6 +1652,7 @@ sub run_log { my $self = shift; + local $ENV{PGHOST} = $self->host; local $ENV{PGPORT} = $self->port; TestLib::run_log(@_); |