diff --git a/bin/pt-agent b/bin/pt-agent index d96e9eba..e3f98793 100755 --- a/bin/pt-agent +++ b/bin/pt-agent @@ -4647,6 +4647,7 @@ use Time::HiRes qw(sleep time); use JSON qw(decode_json); use File::Temp qw(tempfile); use File::Path; +use FindBin; use Percona::Toolkit; use Percona::WebAPI::Client; @@ -5089,8 +5090,8 @@ sub run_agent { lib_dir => $lib_dir, config => $config, services => $services, - json => $args{json}, # optional, for testing - + json => $args{json}, # optional, for testing + bin_dir => $args{bin_dir}, # optional, for testing ); } @@ -5214,6 +5215,14 @@ sub get_services { schedule_services( services => $new_services, lib_dir => $lib_dir, + bin_dir => $args{bin_dir}, # optional, for testing + ); + + # TODO: this probably can't/won't fail, but if it does, is it + # worth setting success=0? + start_services( + services => $new_services, + bin_dir => $args{bin_dir}, # optional, for testing ); $services = $new_services; @@ -5268,8 +5277,9 @@ sub write_config { return; } -# Check and init the --lib dir. This dir is used to save the Agent resource (/agent), -# Service resources (/services/), and crontab for services (/conrtab, /crontab.err). +# Check and init the --lib dir. This dir is used to save the Agent resource +# (/agent), Service resources (/services/), and crontab for services(/conrtab, +# /crontab.err). sub init_lib_dir { my (%args) = @_; @@ -5442,6 +5452,8 @@ sub make_new_crontab { # Optional args my $crontab_list = defined $args{crontab_list} ? $args{crontab_list} : `crontab -l 2>/dev/null`; + my $bin_dir = defined $args{bin_dir} ? $args{bin_dir} + : "$FindBin::Bin/"; my @other_lines = grep { $_ !~ m/pt-agent (?:--run-service|--send-data)/ } @@ -5452,12 +5464,12 @@ sub make_new_crontab { foreach my $service ( @$services ) { push @pt_agent_lines, $service->run_schedule - . " pt-agent --run-service " + . " ${bin_dir}pt-agent --run-service " . $service->name; if ( $service->spool_schedule ) { push @pt_agent_lines, $service->spool_schedule - . " pt-agent --send-data " + . " ${bin_dir}pt-agent --send-data " . $service->name; } } @@ -5468,10 +5480,39 @@ sub make_new_crontab { return $new_crontab; } +# Start all services that have the run_once_on_start flag enabled. This is +# used for "setup services" so the user doesn't have to wait until the next +# the service is actually scheduled to run. +sub start_services { + my (%args) = @_; + have_required_args(\%args, qw( + services + )) or die; + my $services = $args{services}; + + # Optional args + my $bin_dir = defined $args{bin_dir} ? $args{bin_dir} + : "$FindBin::Bin/"; + + foreach my $service ( @$services ) { + next unless $service->run_once_on_start; + my $cmd = "${bin_dir}pt-agent --run-service " . $service->name; + _info('Starting ' . $service->name . ' service: ' . $cmd); + # TODO: need service-specific log files, else where is this output + # supposed to go? + system("$cmd /dev/null 2>&1 &"); + } + + return; +} + # ########################## # # --run-service process subs # # ########################## # +# TODO: need service-specific PID files so two "--run-service enable-slow-log" +# can't run at the same time. + sub run_service { my (%args) = @_; @@ -5560,7 +5601,7 @@ sub run_service { # special vars like __RUN_N_OUTPUT__, __TMPDIR__, etc. my $cmd = join(' ', $task->program, - $task->options, + ($task->options || ''), '>', $output_file, ); diff --git a/t/pt-agent/make_new_crontab.t b/t/pt-agent/make_new_crontab.t index c086f4e7..699409a1 100644 --- a/t/pt-agent/make_new_crontab.t +++ b/t/pt-agent/make_new_crontab.t @@ -34,6 +34,7 @@ sub test_make_new_crontab { my $new_crontab = pt_agent::make_new_crontab( services => $services, crontab_list => $crontab_list, + bin_dir => '', ); ok( @@ -122,6 +123,7 @@ SKIP: { my $new_crontab = pt_agent::make_new_crontab( services => [ $svc0 ], + bin_dir => '', ); is( diff --git a/t/pt-agent/run_agent.t b/t/pt-agent/run_agent.t index efde0c2b..2eeebd24 100644 --- a/t/pt-agent/run_agent.t +++ b/t/pt-agent/run_agent.t @@ -141,7 +141,7 @@ is( "init_config_file()" ); -my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1); +my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 0); mkdir "$tmpdir/services" or die "Error making $tmpdir/services: $OS_ERROR"; my @ok_code = (); # callbacks @@ -308,6 +308,121 @@ is( "Crontab is the same" ); +# ############################################################################# +# Test a run_once_on_start service +# ############################################################################# + +diag(`rm -f $tmpdir/* >/dev/null 2>&1`); +diag(`rm -rf $tmpdir/services/*`); +mkdir "$tmpdir/spool" or die $OS_ERROR; + +$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'); + +$config = Percona::WebAPI::Resource::Config->new( + ts => 1363720060, + name => 'Test run_once_on_start', + options => { + 'check-interval' => "60", + 'lib' => $tmpdir, + 'spool' => "$tmpdir/spool", + 'pid' => "$tmpdir/pid", + 'log' => "$tmpdir/log" + }, + links => { + self => '/agents/123/config', + services => '/agents/123/services', + }, +); + +$run0 = Percona::WebAPI::Resource::Task->new( + name => 'run-at-start', + number => '0', + program => 'date', + output => 'spool', +); + +$svc0 = Percona::WebAPI::Resource::Service->new( + name => 'test-run-at-start', + run_schedule => '0 0 1 1 *', + spool_schedule => '0 0 1 1 *', + run_once_on_start => 1, # here's the magic + tasks => [ $run0 ], + links => { + self => '/query-history', + data => '/query-history/data', + }, +); + +$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) ], + }, + { + 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) ], + }, +]; + +@ok_code = (); # callbacks +@oktorun = (1, 1, 0); +@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 + bin_dir => "$trunk/bin/", # optional, for testing + ); + }, + stderr => 1, +); + +Percona::Test::wait_for_files("$tmpdir/spool/test-run-at-start"); + +like( + $output, + qr/Starting test-run-at-start service/, + "Ran service on start" +); + +my @runs = $output =~ m/Starting test-run-at-start service/g; + +is( + scalar @runs, + 1, + "... only ran it once" +); + +chomp($output = `cat $tmpdir/spool/test-run-at-start 2>/dev/null`); +ok( + $output, + "... service ran at start" +) or diag($output); + +chomp($output = `crontab -l`); +like( + $output, + qr/--run-service test-run-at-start/, + "... service was scheduled" +); + # ############################################################################# # Done. # ############################################################################# @@ -320,4 +435,5 @@ if ( -f $config_file ) { unlink $config_file or warn "Error removing $config_file: $OS_ERROR"; } + done_testing; diff --git a/t/pt-agent/schedule_services.t b/t/pt-agent/schedule_services.t index ea57c716..d70f7489 100644 --- a/t/pt-agent/schedule_services.t +++ b/t/pt-agent/schedule_services.t @@ -81,6 +81,15 @@ is( ) or diag($output); $crontab = `crontab -l 2>/dev/null`; + +# pt-agent uses $FindBin::Bin/pt-agent for the path to pt-agent, +# which in testing will be $trunk/t/pt-agent/ because that's where +# this file is located. However, if $FindBin::Bin resovles sym +# links where as $trunk does not, so to make things simple we just +# cut out the full path. +if ( $crontab ) { + $crontab =~ s! /.+?/pt-agent --! pt-agent --!g; +} is( $crontab, "* 0 * * * date > /dev/null