App-Standby

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

Makefile.PL
README
README.md
bin/standby-mgm-cgi.pl
bin/standby-mgm.pl
bin/standby-mgm.psgi
conf/standby-mgm.conf
conf/standby-mgm.conf.dist
dist.ini
examples/complex/README
examples/complex/complex_endpoint.pl
examples/complex/lib/App/Standby/Cmd/Command/complex.pm
examples/complex/lib/App/Standby/Service/Complex.pm
examples/simple/README
examples/simple/lib/App/Standby/Service/Simple.pm
examples/simple/simple_endpoint.pl
lib/App/Standby.pm
lib/App/Standby/Cmd.pm
lib/App/Standby/Cmd/Command.pm
lib/App/Standby/Cmd/Command/bootstrap.pm
lib/App/Standby/DB.pm
lib/App/Standby/Frontend.pm
lib/App/Standby/Group.pm
lib/App/Standby/Service.pm
lib/App/Standby/Service/HTTP.pm
lib/App/Standby/Service/MS.pm

README  view on Meta::CPAN

NAME
    App::Standby - Managing on-call rotation and notification queues

DESCRIPTION
    This distribution provides a small Plack webapp which helps with
    managing on-call rotations and notification queues. It allows you to
    manage several different queues from on place. It is easily extendible
    by plugins which can talk to virtually API endpoint to update a queue or
    a contact.

    Most organizations have at least one big monitoring system (like Nagios
    or Zabbix) and at least one external service level monitoring and other
    means of notification, If you don't want to pass around a shared on-call
    mobile you have to remember to update all those services when the one on
    duty changes. This app will help you with that.

    It allows you to manage several groups with their own queues and update
    each groups external services with just one click.

README  view on Meta::CPAN

    In order for this app to do anything there must be two things in the
    database: users and services. This section will show you how to create
    the later.

    First select services and open up the new service dialog. Enter a short
    name for this service. Remember it must be all lowercase alphanumerics
    since it's going to be used as a prefix for the configuration values
    later. The description can be anything. Select the appropriate class and
    enter the group password you've dedfined when bootstrapping the service.

    Choose class HTTP for a simple endpoint which just gets the whole
    ordered user list as a JSON string. Chosse MS for a Monitoring::Spooler
    endpoint and Pingdom if you have an Pingdom account.

    Add as many services as necessary.

    Next select config from the menu and add new config items. For each
    service at least one. The HTTP service need an endpoint, so if the HTTP
    service was called simple then the config item for the endpoint must be
    called simple_endpoint and contain something like
    http://simple.domain/api/.

    For any pingdom service there must be at least four config items. The
    necessary items are apikey, username, password and contact_id. The last
    one may be given multiple times to update multiple Pingdom contacts in
    one account. If your service is called pingdom the keys would be called
    pingdom_apikey, pingdom_username, pingdom_password and
    pingdom_contact_id.

    The MS service class needs an endpoint and a group id (name_group_id)
    which is used to update the appropriate group in the MS DB. If you call
    your MS service "ms" then the necessary keys would be called
    "ms_endpoint" with a value of e.g.
    "http://localhost/ms/?rm=update_queue" and "ms_group_id" with a value of
    e.g. "1".

    Have a look at the CPAN distribution Monitoring::Spooler for more
    documentation on MS.

  CONTACTS
    To be able to notify someone you must add some contacts. Since the whole
    point of this app is to help with managing changing on call rotations
    you should create more than one contact.

examples/complex/README  view on Meta::CPAN

This example includes a more complex service example.

The service is just a subclass of App::Standby::Service::HTTP and
sends the whole queue. It also provides a cronjob which calls the
service once a day to keep your endpoint up to date.

The endpoint examines the request and updates your complex service.

examples/complex/lib/App/Standby/Cmd/Command/complex.pm  view on Meta::CPAN


__END__

=head1 NAME

App::Standby::Cmd::Command::complex - Example for a command to be run as a cronjob

=head1 DESCRIPTION

This class implements an example for a cronjob that is run once a day to keep some
complex endpoint up-to-date.

=cut

examples/complex/lib/App/Standby/Service/Complex.pm  view on Meta::CPAN

# use MooseX::Params::Validate;
# use Carp;
# use English qw( -no_match_vars );
# use Try::Tiny;

# extends ...
extends 'App::Standby::Service::HTTP';
# has ...
# with ...
# initializers ...
sub _init_endpoints {
    my $self = shift;

    return $self->_config_values($self->name().'_endpoint');
}


# your code here ...

no Moose;
__PACKAGE__->meta->make_immutable;

1;

examples/simple/README  view on Meta::CPAN

This example includes a very simple service example.

The service is just a subclass of App::Standby::Service::HTTP and
sends the whole queue while the endpoint is implemented as
a very simple CGI script that decodes the queue and inserts it
into a queue in a MySQL database.

examples/simple/lib/App/Standby/Service/Simple.pm  view on Meta::CPAN

# use MooseX::Params::Validate;
# use Carp;
# use English qw( -no_match_vars );
# use Try::Tiny;

# extends ...
extends 'App::Standby::Service::HTTP';
# has ...
# with ...
# initializers ...
sub _init_endpoints {
    my $self = shift;

    return $self->_config_values($self->name().'_endpoint');
}

# your code here ...

no Moose;
__PACKAGE__->meta->make_immutable;

1;

__END__

lib/App/Standby.pm  view on Meta::CPAN


=head1 NAME

App::Standby - Managing on-call rotation and notification queues

=head1 DESCRIPTION

This distribution provides a small Plack webapp which helps with managing on-call rotations
and notification queues. It allows you to manage several different queues from
on place. It is easily extendible by plugins which can talk to virtually API
endpoint to update a queue or a contact.

Most organizations have at least one big monitoring system (like Nagios or Zabbix) and
at least one external service level monitoring and other means of notification,
If you don't want to pass around a shared on-call mobile you have to remember to update all
those services when the one on duty changes. This app will help you with that.

It allows you to manage several groups with their own queues and update each
groups external services with just one click.

=head1 METHODS

lib/App/Standby.pm  view on Meta::CPAN

=head2 SERVICES

In order for this app to do anything there must be two things in the database: users and services.
This section will show you how to create the later.

First select services and open up the new service dialog. Enter a short name for this service.
Remember it must be all lowercase alphanumerics since it's going to be used as a prefix for the
configuration values later. The description can be anything. Select the appropriate class and
enter the group password you've dedfined when bootstrapping the service.

Choose class HTTP for a simple endpoint which just gets the whole ordered user list as a JSON
string. Chosse MS for a Monitoring::Spooler endpoint and Pingdom if you have an Pingdom account.

Add as many services as necessary.

Next select config from the menu and add new config items. For each service at least one. The
HTTP service need an endpoint, so if the HTTP service was called simple then the config item
for the endpoint must be called simple_endpoint and contain something like http://simple.domain/api/.

For any pingdom service there must be at least four config items. The necessary items are apikey,
username, password and contact_id. The last one may be given multiple times to update
multiple Pingdom contacts in one account. If your service is called pingdom the keys would be
called pingdom_apikey, pingdom_username, pingdom_password and pingdom_contact_id.

The MS service class needs an endpoint and a group id (name_group_id) which is used
to update the appropriate group in the MS DB. If you call your MS service "ms" then the necessary
keys would be called "ms_endpoint" with a value of e.g. "http://localhost/ms/?rm=update_queue" and
"ms_group_id" with a value of e.g. "1".

Have a look at the CPAN distribution Monitoring::Spooler for more documentation on MS.

=head2 CONTACTS

To be able to notify someone you must add some contacts. Since the whole point of this app
is to help with managing changing on call rotations you should create more than one contact.

Create at least two contacts. The name may be anything but you should know who it is refering to.

lib/App/Standby/Service.pm  view on Meta::CPAN

=head2 update

This method is called with a array_ref containing the new ordering on any changed.

=head1 NAME

App::Standby::Service - Service Plugin baseclass

=head1 ADDING A NEW SERVICE

First of all there are two kinds of services: Simple HTTP endpoints and complex plugins.

The simple HTTP plugins just receive the whole queue in as JSON encoded array.
Those only need to subclass App::Standby::Service::HTTP and provide an implementation
for _init_endpoints. Have a look at the simple example.

All other services will need to subclass App::Standby::Service and implement an
update() method. Have a look at App::Standby::Service::Pingom for an example.

The method _config_values helps with getting values to known keys from
the config table. A service plugin MUST always prepend its name to
the key to allow for multiple instances of one plugin registered at
the same time.

=head1 AUTHOR

lib/App/Standby/Service/HTTP.pm  view on Meta::CPAN

    'isa'   => 'Str',
    'required' => 0,
);

has 'password' => (
    'is'    => 'rw',
    'isa'   => 'Str',
    'required' => 0,
);

has 'endpoints' => (
    'is'    => 'rw',
    'isa'   => 'ArrayRef',
    'lazy'  => 1,
    'builder'   => '_init_endpoints',
);
# with ...
# initializers ...
sub _init_json {
    my $self = shift;

    my $JSON = JSON::->new->utf8();

    return $JSON;
}

lib/App/Standby/Service/HTTP.pm  view on Meta::CPAN


    return $content;
}

sub _update {
    my $self = shift;
    my $user_ref = shift;

    my $count = 0;

    foreach my $endpoint (@{$self->endpoints()}) {
        $self->logger()->log( message => "Updating endpoint: ".$endpoint, level => 'debug', );
        my $req = HTTP::Request::->new( POST => $endpoint );
        $req->content_type('application/x-www-form-urlencoded');
        my $payload = $self->_build_payload($user_ref);
        $req->content($payload);
        $self->logger()->log( message => "Payload: ".$payload, level => 'debug', );

        if($self->username() && $self->password()) {
            $req->authorization_basic( $self->username(), $self->password() );
        }

        my $content;
        my $response;
        my $prev_alarm = 0;

        my $success = try {
            local $SIG{ALRM} = sub { die "alarm-standby-service\n"; };
            $prev_alarm = alarm 10;
            $response   = $self->_ua()->request($req);
            if ( !$response->is_success ) {
                my $msg = "ERROR Request to $endpoint failed: " . $response->code() . ' - ' . $response->message();
                $self->logger()->log( message => $msg, level => 'error', );
                die( $msg );
            }
            $content = $response->content;
            if ( !$content ) {
                my $msg = "ERROR No content at $endpoint : " . $response->code() . ' - ' . $response->message();
                $self->logger()->log( message => $msg, level => 'error', );
                die( $msg );
            }
            1;    # make sure $success has a true value ...
        }
        catch {
            $self->logger()->log( message => "Request failed: ".$_, level => 'debug', );
        }
        finally {

lib/App/Standby/Service/MS.pm  view on Meta::CPAN

# use MooseX::Params::Validate;
# use Carp;
# use English qw( -no_match_vars );
# use Try::Tiny;

# extends ...
extends 'App::Standby::Service::HTTP';
# has ...
# with ...
# initializers ...
sub _init_endpoints {
    my $self = shift;

    return $self->_config_values($self->name().'_endpoint');
}

# your code here ...

no Moose;
__PACKAGE__->meta->make_immutable;

1;

__END__

t/frontend.t  view on Meta::CPAN

    unlike($res->header('Location'), qr/Invalid...Key/, 'No error due to invalid key');

    # contacts page for group 1 lists the new contact
    $res = $cb->(GET '/?rm=list_contacts&group_id=1',);
    like($res->content, qr/Testcontact/, 'User Testcontact exists in group 1');

    # adding config item succeeds w/ correct key
    $res = $cb->(POST '/', [
        rm          => 'insert_config',
        'group_id'  => 1,
        'key'      => 'ms_endpoint',
        'value' => 'http://localhost/ms/',
        'group_key' => 'test',
    ]);
    ok($res->is_redirect, 'Redirect after create');
    unlike($res->header('Location'), qr/Invalid...Key/, 'No error due to invalid key');

    # config page for group 1 lists the new item
    $res = $cb->(GET '/?rm=list_config&group_id=1',);
    like($res->content, qr/ms_endpoint/, 'Config item ms_endpoint exists in group 1');

    # adding service succeeds w/ correct key
    $res = $cb->(POST '/', [
        rm          => 'insert_service',
        'group_id'  => 1,
        'name'      => 'ms',
        'description' => 'Monitoring::Spooler',
        'class' => 'MS',
        'group_key' => 'test',
    ]);
    ok($res->is_redirect, 'Redirect after create');
    unlike($res->header('Location'), qr/Invalid...Key/, 'No error due to invalid key');

    # config page for group 1 lists the new item
    $res = $cb->(GET '/?rm=list_services&group_id=1',);
    like($res->content, qr/App::Standby::Service::MS/, 'Config item ms_endpoint exists in group 1');
};

done_testing();



( run in 0.342 second using v1.01-cache-2.11-cpan-27979f6cc8f )