The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Ubic::Service::MongoDB;
BEGIN {
  $Ubic::Service::MongoDB::VERSION = '0.01';
}

use strict;
use warnings;

# ABSTRACT: running MongoDB as Ubic service


use parent qw(Ubic::Service::Common);

use File::Copy qw(move);
use File::Spec::Functions qw(catfile);
use MongoDB;
use Params::Validate qw(:all);
use Ubic::Daemon qw(:all);
use Ubic::Result qw(:all);



sub new {
    my $class = shift;

    my $opt_str     = { type => SCALAR, optional => 1 };

    my $params = validate(@_, {
        config => { type => HASHREF },
        daemon => { type => SCALAR,
                    regex => qr/^mongo(d|s)$/,
                    default => 'mongod' },

        status   => { type => CODEREF, optional => 1 },
        user     => $opt_str,
        ubic_log => $opt_str,
        stdout   => $opt_str,
        stderr   => $opt_str,
        pidfile  => $opt_str,

        gen_cfg => $opt_str,
    });

    my $self = $params;
    bless $self, $class;

    $self->{port} = $self->{config}->{port} || 27017;

    if (!$params->{pidfile}) {
        $params->{pidfile} = "/tmp/$self->{daemon}." . $self->{port} .  '.pid';
    }
    if (!$params->{gen_cfg}) {
        $params->{gen_cfg} = "/tmp/$self->{daemon}." . $self->{port} . '.conf';
    }


    return bless $params => $class;
}

sub bin {
    my $self = shift;

    my @cmd = ($self->{daemon}, '--config', $self->{gen_cfg});

    return \@cmd;
}

sub create_cfg_file {
    my $self = shift;

    my $fname = $self->{gen_cfg};
    my $tmp_fname = $fname . ".tmp";

    open(my $tmp_fh, '>', $tmp_fname) or die "Can't open file [$tmp_fname]: $!";

    foreach my $k (keys %{$self->{config}}) {
        my $v = $self->{config}->{$k};
        print $tmp_fh "$k=$v\n";
    }

    close($tmp_fh) or die "Can't close file [$tmp_fname]: $!";
    move($tmp_fname, $fname) or die "Can't mova file [${tmp_fname}] to [$fname]: $!";
}

sub pidfile {
    my $self = shift;

    return $self->{pidfile};
}

sub user {
    my $self = shift;

    return $self->{user} if defined $self->{user};
    return $self->SUPER::user;
}

sub timeout_options {
    return {
        start => { trials => 15, step => 0.1 },
        stop  => { trials => 15, step => 0.1 }
    };
}

sub start_impl {
    my $self = shift;

    $self->create_cfg_file;

    my $daemon_opts = {
        bin          => $self->bin,
        term_timeout => 5
    };

    for (qw/ubic_log stdout stderr pidfile/) {
        $daemon_opts->{$_} = $self->{$_} if defined $self->{$_};
    }
    start_daemon($daemon_opts);
}

sub stop_impl {
    my $self = shift;

    return stop_daemon($self->pidfile, { timeout => 7 });
}

sub status_impl {
    my $self = shift;

    my $running = check_daemon($self->pidfile);
    return result('not running') unless ($running);

    my $status;
    eval {
        my $conn = MongoDB::Connection->new(
            host => "mongodb://localhost:$self->{port}",
            timeout => 2,
            query_timeout => 2,
        );
        my $db = $conn->admin;
        $status = $db->run_command({ serverStatus => 1 });
    };

    if ($@) {
        return result('broken', $@);
    } elsif (!$status || !$status->{ok} || $status->{ok} != 1) {
        return result('broken');
    } else {
        return result('running');
    }
}


1;

__END__
=pod

=head1 NAME

Ubic::Service::MongoDB - running MongoDB as Ubic service

=head1 VERSION

version 0.01

=head1 SYNOPSIS

# in your ubic service (/etc/ubic/service/mymongo, for example)
use Ubic::Service::MongoDB;
return Ubic::Service::MongoDB->new({
    config => {
        dbpath    => '/var/lib/mongodb',
        logpath   => "/var/log/mongodb/mongodb.log",
        logappend => "true",
    },
    daemon => 'mongod',
    user   => 'mongodb',
    ubic_log => '/var/log/mongodb/ubic.log',
    stdout   => '/var/log/mongodb/stdout.log',
    stderr   => '/var/log/mongodb/stderr.log',
});

=head1 DESCRIPTION

This is a L<Ubic> service for MongoDB. You can start/stop C<mongod> and C<mongos> using this module.

=head1 METHODS

=over

=item C<new($params)>

Creates new MongoDB service. C<$params> is a hashref with the following parameters:

=over

=item I<config>

Hashref with keys and values for MongoDB .conf file. This conf file regenerates every time at start.

=item I<daemon> (optional)

What you want to start: C<mongod> or C<mongos>. Default is C<mongod>.

=item I<user> (optional)

User name that will be used as real and effective user identifier during exec of MongoDB.

=item I<status> (optional)

Coderef for checking MongoDB status. Takes current instance of C<Ubic::Service::MongoDB> as a first param.

Default implemetation uses C<serverStatus()> MongoDB command.

=item I<ubic_log> (optional)

Path to ubic log.

=item I<stdout> (optional)

Path to stdout log.

=item I<stderr> (optional)

Path to stderr log.

=item I<pidfile> (optional)

Pidfile for C<Ubic::Daemon> module.

If not specified it is a /tmp/mongo(d|s).<port>.pid.

=item I<gen_cfg> (optional)

Generated MongoDB config file name.

If not specified it is a /tmp/mongo(d|s).<port>.conf.

=back

=back

=head1 SEE ALSO

L<http://www.mongodb.org/display/DOCS/File+Based+Configuration>

L<Ubic>

=head1 AUTHOR

Yury Zavarin <yury.zavarin@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Yury Zavarin.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut