Test-Postgresql58

 view release on metacpan or  search on metacpan

lib/Test/Postgresql58.pm  view on Meta::CPAN

# Note that these are used only if the program isn't already in the path.
our @SEARCH_PATHS = (
    split(/:/, $ENV{PATH}),
    # popular installation dir?
    qw(/usr/local/pgsql),
    # ubuntu (maybe debian as well, find the newest version)
    (sort { $b cmp $a } grep { -d $_ } glob "/usr/lib/postgresql/*"),
    # macport
    (sort { $b cmp $a } grep { -d $_ } glob "/opt/local/lib/postgresql*"),
    # Postgresapp.com
    (sort { $b cmp $a } grep { -d $_ } glob "/Applications/Postgres.app/Contents/Versions/*"),
    # BSDs end up with it in /usr/local/bin which doesn't appear to be in the path sometimes:
    "/usr/local",
);

# This environment variable is used to override the default, so it gets
# prefixed to the start of the search paths.
if (defined $ENV{POSTGRES_HOME} and -d $ENV{POSTGRES_HOME}) {
    unshift @SEARCH_PATHS, $ENV{POSTGRES_HOME};
}

our $errstr;
our $BASE_PORT = 15432;

our %Defaults = (
    auto_start      => 2,
    base_dir        => undef,
    initdb          => undef,
    initdb_args     => '-U postgres -A trust',
    pid             => undef,
    port            => undef,
    postmaster      => undef,
    postmaster_args => '-h 127.0.0.1 -F',
    uid             => undef,
    _owner_pid      => undef,
);

Class::Accessor::Lite->mk_accessors(keys %Defaults);

sub new {
    my $klass = shift;
    my $self = bless {
        %Defaults,
        @_ == 1 ? %{$_[0]} : @_,
        _owner_pid => $$,
    }, $klass;
    if (! defined $self->uid && $ENV{USER} eq 'root') {
        my @a = getpwnam('nobody')
            or die "user nobody does not exist, use uid() to specify user:$!";
        $self->uid($a[2]);
    }
    if (defined $self->base_dir) {
        $self->base_dir(cwd . '/' . $self->base_dir)
            if $self->base_dir !~ m|^/|;
    } else {
        $self->base_dir(
            tempdir(
                CLEANUP => $ENV{TEST_POSTGRESQL_PRESERVE} ? undef : 1,
            ),
        );
        chown $self->uid, -1, $self->base_dir
            if defined $self->uid;
    }
    if (! defined $self->initdb) {
        my $prog = _find_program('initdb')
            or return;
        $self->initdb($prog);
    }
    if (! defined $self->postmaster) {
        my $prog = _find_program('postmaster')
            or return;
        $self->postmaster($prog);
    }
    if ($self->auto_start) {
        $self->setup
            if $self->auto_start >= 2;
        $self->start;
    }
    $self;
}

sub DESTROY {
    local $?;
    my $self = shift;
    $self->stop
        if defined $self->pid && $$ == $self->_owner_pid;
    return;
}

sub dsn {
    my $self = shift;
    my %args = $self->_default_args(@_);

    return 'DBI:Pg:' . join(';', map { "$_=$args{$_}" } sort keys %args);
}

sub _default_args {
    my ($self, %args) = @_;
    $args{host} ||= '127.0.0.1';
    $args{port} ||= $self->port;
    $args{user} ||= 'postgres';
    $args{dbname} ||= 'test';
    return %args;
}

sub uri {
    my $self = shift;
    my %args = $self->_default_args(@_);

    return sprintf('postgresql://%s@%s:%d/%s', @args{qw/user host port dbname/});
}

sub start {
    my $self = shift;
    return
        if defined $self->pid;
    # start (or die)
    sub {
        my $err;
        if ($self->port) {
            $err = $self->_try_start($self->port)

lib/Test/Postgresql58.pm  view on Meta::CPAN

        exec($cmd);
        die "failed to launch postmaster:$?";
    }
    close $logfh;
    # wait until server becomes ready (or dies)
    for (my $i = 0; $i < 100; $i++) {
        open $logfh, '<', $self->base_dir . '/postgres.log'
            or die 'failed to open log file:' . $self->base_dir
                . "/postgres.log:$!";
        my $lines = do { join '', <$logfh> };
        close $logfh;
        last
            if $lines =~ /is ready to accept connections/;
        if (waitpid($pid, WNOHANG) > 0) {
            # failed
            return $lines;
        }
        sleep 1;
    }
    # PostgreSQL is ready
    $self->pid($pid);
    $self->port($port);
    return;
}

sub stop {
    my ($self, $sig) = @_;
    return unless defined $self->pid;

    $sig ||= SIGTERM;

    kill $sig, $self->pid;
    my $timeout = 10;
    while ($timeout > 0 and waitpid($self->pid, WNOHANG) <= 0) {
        $timeout -= sleep(1);
    }

    if ($timeout <= 0) {
        warn "Pg refused to die gracefully; killing it violently.\n";
        kill SIGKILL, $self->pid;
        $timeout = 5;
        while ($timeout > 0 and waitpid($self->pid, WNOHANG) <= 0) {
            $timeout -= sleep(1);
        }
        if ($timeout <= 0) {
            warn "Pg really didn't die.. WTF?\n";
        }
    }

    $self->pid(undef);
    return;
}

sub setup {
    my $self = shift;
    # (re)create directory structure
    mkdir $self->base_dir;
    chmod 0755, $self->base_dir
        or die "failed to chmod 0755 dir:" . $self->base_dir . ":$!";
    if ($ENV{USER} eq 'root') {
        chown $self->uid, -1, $self->base_dir
            or die "failed to chown dir:" . $self->base_dir . ":$!";
    }
    if (mkdir $self->base_dir . '/tmp') {
        if ($self->uid) {
            chown $self->uid, -1, $self->base_dir . '/tmp'
                or die "failed to chown dir:" . $self->base_dir . "/tmp:$!";
        }
    }
    # initdb
    if (! -d $self->base_dir . '/data') {
        pipe my $rfh, my $wfh
            or die "failed to create pipe:$!";
        my $pid = fork;
        die "fork failed:$!"
            unless defined $pid;
        if ($pid == 0) {
            close $rfh;
            open STDOUT, '>&', $wfh
                or die "dup(2) failed:$!";
            open STDERR, '>&', $wfh
                or die "dup(2) failed:$!";
            chdir $self->base_dir
                or die "failed to chdir to:" . $self->base_dir . ":$!";
            if (defined $self->uid) {
                setuid($self->uid)
                    or die "setuid failed:$!";
            }
            my $cmd = join(
                ' ',
                $self->initdb,
                $self->initdb_args,
                '-D', $self->base_dir . '/data',
            );
            exec($cmd);
            die "failed to exec:$cmd:$!";
        }
        close $wfh;
        my $output = '';
        while (my $l = <$rfh>) {
            $output .= $l;
        }
        close $rfh;
        while (waitpid($pid, 0) <= 0) {
        }
        die "*** initdb failed ***\n$output\n"
            if $? != 0;

        # use postgres hard-coded configuration as some packagers mess
        # around with postgresql.conf.sample too much:
        truncate $self->base_dir . '/data/postgresql.conf', 0;
    }
}

sub _find_program {
    my $prog = shift;
    undef $errstr;
    for my $sp (@SEARCH_PATHS) {
        return "$sp/bin/$prog" if -x "$sp/bin/$prog";
        return "$sp/$prog" if -x "$sp/$prog";
    }
    $errstr = "could not find $prog, please set appropriate PATH or POSTGRES_HOME";
    return;
}

1;
__END__



( run in 1.750 second using v1.01-cache-2.11-cpan-5511b514fd6 )