Files
percona-toolkit/t/pt-agent/run_agent.t
2013-01-31 17:00:38 -07:00

322 lines
8.0 KiB
Perl

#!/usr/bin/env perl
BEGIN {
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
};
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More;
use JSON;
use File::Temp qw(tempdir);
use Percona::Test;
use Percona::Test::Mock::UserAgent;
require "$trunk/bin/pt-agent";
Percona::Toolkit->import(qw(Dumper));
Percona::WebAPI::Representation->import(qw(as_hashref));
# Running the agent is going to cause it to schedule the services,
# i.e. write a real crontab. The test box/user shouldn't have a
# crontab, so we'll warn and clobber it if there is one.
my $crontab = `crontab -l 2>/dev/null`;
if ( $crontab ) {
warn "Removing crontab: $crontab\n";
`crontab -r`;
}
# #############################################################################
# Create mock client and Agent
# #############################################################################
# These aren't the real tests yet: to run_agent, first we need
# a client and Agent, so create mock ones.
my $output;
my $json = JSON->new->canonical([1])->pretty;
$json->allow_blessed([]);
$json->convert_blessed([]);
my $ua = Percona::Test::Mock::UserAgent->new(
encode => sub { my $c = shift; return $json->encode($c || {}) },
);
my $client = eval {
Percona::WebAPI::Client->new(
api_key => '123',
ua => $ua,
);
};
is(
$EVAL_ERROR,
'',
'Create mock client'
) or die;
my $agent = Percona::WebAPI::Resource::Agent->new(
id => '123',
hostname => 'host',
links => {
self => '/agents/123',
config => '/agents/123/config',
},
);
my @wait;
my $interval = sub {
my $t = shift;
push @wait, $t;
print "interval=" . (defined $t ? $t : 'undef') . "\n";
};
# #############################################################################
# Test run_agent
# #############################################################################
# The agent does just basically 2 things: check for new config, and
# check for new services. It doesn't do the latter until it has a
# config, because services require info from a config. Config are
# written to $HOME/.pt-agent.conf; this can't be changed because the
# other processes (service runner and spool checker) must share the
# same config.
my $config = Percona::WebAPI::Resource::Config->new(
id => '1',
name => 'Default',
options => {
'check-interval' => "60",
},
links => {
self => '/agents/123/config',
services => '/agents/123/services',
},
);
my $run0 = Percona::WebAPI::Resource::Run->new(
number => '0',
program => 'pt-query-digest',
options => '--output json',
output => 'spool',
);
my $svc0 = Percona::WebAPI::Resource::Service->new(
name => 'query-monitor',
run_schedule => '1 * * * *',
spool_schedule => '2 * * * *',
runs => [ $run0 ],
links => {
send_data => '/query-monitor',
},
);
$ua->{responses}->{get} = [
{
headers => { 'X-Percona-Resource-Type' => 'Config' },
content => as_hashref($config, with_links => 1),
},
{
headers => { 'X-Percona-Resource-Type' => 'Service' },
content => [ as_hashref($svc0, with_links => 1) ],
},
];
# The only thing pt-agent must have is the API key in the config file,
# everything else relies on defaults until the first Config is gotten
# from Percona. -- The tool calls init_config_file() if the file doesn't
# exist, so we do the same. Might as well test it while we're here.
my $config_file = pt_agent::get_config_file();
unlink $config_file if -f $config_file;
pt_agent::init_config_file(file => $config_file, api_key => '123');
is(
`cat $config_file`,
"api-key=123\n",
"init_config_file()"
);
my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
mkdir "$tmpdir/services" or die "Error making $tmpdir/services: $OS_ERROR";
my @ok_code = (); # callbacks
my @oktorun = (1, 0);
my $oktorun = sub {
my $ok = shift @oktorun;
print "oktorun=" . (defined $ok ? $ok : 'undef') . "\n";
my $code = shift @ok_code;
$code->() if $code;
return $ok
};
@wait = ();
$output = output(
sub {
pt_agent::run_agent(
agent => $agent,
client => $client,
interval => $interval,
config_file => $config_file,
lib_dir => $tmpdir,
oktorun => $oktorun, # optional, for testing
json => $json, # optional, for testing
);
},
stderr => 1,
);
is(
`cat $config_file`,
"api-key=123\ncheck-interval=60\n",
"Write Config to config file"
);
is(
scalar @wait,
1,
"Called interval once"
);
is(
$wait[0],
60,
"... used Config->options->check-interval"
);
ok(
-f "$tmpdir/services/query-monitor",
"Created services/query-monitor"
) or diag($output);
chomp(my $n_files = `ls -1 $tmpdir/services| wc -l | awk '{print \$1}'`);
is(
$n_files,
1,
"... only created services/query-monitor"
);
ok(
no_diff(
"cat $tmpdir/services/query-monitor",
"t/pt-agent/samples/service001",
),
"query-monitor service file"
);
$crontab = `crontab -l 2>/dev/null`;
like(
$crontab,
qr/pt-agent --run-service query-monitor$/m,
"Scheduled --run-service with crontab"
);
like(
$crontab,
qr/pt-agent --send-data query-monitor$/m,
"Scheduled --send-data with crontab"
);
# #############################################################################
# Run run_agent again, like the agent had been stopped and restarted.
# #############################################################################
$ua->{responses}->{get} = [
# First check, fail
{
code => 500,
},
# interval
# 2nd check, init with latest Config and Services
{
headers => { 'X-Percona-Resource-Type' => 'Config' },
content => as_hashref($config),
},
{
headers => { 'X-Percona-Resource-Type' => 'Service' },
content => [ as_hashref($svc0) ],
},
# interval
# 3rd check, same Config and Services so nothing to do
{
headers => { 'X-Percona-Resource-Type' => 'Config' },
content => as_hashref($config),
},
{
headers => { 'X-Percona-Resource-Type' => 'Service' },
content => [ as_hashref($svc0) ],
},
# interval, oktorun=0
];
# 0=while check, 1=after first check, 2=after 2nd check, etc.
@oktorun = (1, 1, 1, 0);
# Between the 2nd and 3rd checks, remove the config file (~/.pt-agent.conf)
# and query-monitor service file. When the tool re-GETs these, they'll be
# the same so it won't recreate them. A bug here will cause these files to
# exist again after running.
$ok_code[2] = sub {
unlink "$config_file";
unlink "$tmpdir/services/query-monitor";
Percona::Test::wait_until(sub { ! -f "$config_file" });
Percona::Test::wait_until(sub { ! -f "$tmpdir/services/query-monitor" });
};
@wait = ();
$output = output(
sub {
pt_agent::run_agent(
agent => $agent,
client => $client,
interval => $interval,
config_file => $config_file,
lib_dir => $tmpdir,
oktorun => $oktorun, # optional, for testing
json => $json, # optional, for testing
);
},
stderr => 1,
);
is_deeply(
\@wait,
[ undef, 60, 60 ],
"Got Config after error"
) or diag(Dumper(\@wait));
ok(
! -f "$config_file",
"No Config diff, no config file change"
);
ok(
! -f "$tmpdir/services/query-monitor",
"No Service diff, no service file changes"
);
my $new_crontab = `crontab -l 2>/dev/null`;
is(
$new_crontab,
$crontab,
"Crontab is the same"
);
# #############################################################################
# Done.
# #############################################################################
# This shouldn't cause an error, but if it does, let it show up
# in the results as an error.
`crontab -r`;
if ( -f $config_file ) {
unlink $config_file
or warn "Error removing $config_file: $OS_ERROR";
}
done_testing;