From d7f49dee8a02c661ee0debfc57b324bab3de7f09 Mon Sep 17 00:00:00 2001 From: Daniel Nichter Date: Mon, 7 Jan 2013 17:52:37 -0700 Subject: [PATCH] Start writing and testing schedule_services(). --- bin/pt-agent | 95 +++++++++++++++++++++++++------ t/pt-agent/make_new_crontab.t | 78 +++++++++++++++++++++++++ t/pt-agent/samples/crontab001.in | 0 t/pt-agent/samples/crontab001.out | 2 + 4 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 t/pt-agent/make_new_crontab.t create mode 100644 t/pt-agent/samples/crontab001.in create mode 100644 t/pt-agent/samples/crontab001.out diff --git a/bin/pt-agent b/bin/pt-agent index 770ec26e..e1cd6ad8 100755 --- a/bin/pt-agent +++ b/bin/pt-agent @@ -4359,8 +4359,10 @@ use warnings FATAL => 'all'; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG} || 0; -use POSIX qw(signal_h); +use POSIX qw(signal_h); use Time::HiRes qw(sleep time); +use JSON qw(decode_json); +use File::Temp qw(tempfile); use Percona::Toolkit; use Percona::WebAPI::Client; @@ -4520,7 +4522,6 @@ sub main { config_file => $config_file, lib_dir => $o->get('lib'), ); - _info('Agent ' . $agent->id . ' has stopped'); } _info("pt-agent exit $exit_status, oktorun $oktorun"); @@ -4663,23 +4664,34 @@ sub run_agent { my $config; my $services; + AGENT_LOOP: while ( $oktorun->() ) { eval { _info('Getting config'); + + # Get the agent's config from Percona. my $new_config = $client->get( url => $client->links->{config}, ); + + # If the current and new configs are different, + # write the new one to disk, then apply it. if ( resource_diff($config, $new_config) ) { _info('New config'); write_config( config => $new_config, file => $config_file, ); + + # Whatever --lib dir the new config has, use it. + # Services are written to --lib/services if ( my $new_lib_dir = $new_config->options->{lib} ) { # TODO: what if new lib dir doesn't have /services? $lib_dir = $new_lib_dir; _info("New --lib direcotry: $lib_dir"); } + + # Apply new config, i.e. update the current, running config. $config = $new_config; } else { @@ -4690,12 +4702,19 @@ sub run_agent { _warn($EVAL_ERROR); } + # Get services only if there's a current, running config. + # Without one, we won't know how to implement services. if ( $config ) { eval { _info('Getting services'); + + # Get services from Percona. my $new_services = $client->get( url => $client->links->{services}, ); + + # If the current and new services are different, + # write the new ones to disk, then schedule them. if ( resource_diff($services, $new_services) ) { _info('New services'); write_services( @@ -4722,8 +4741,11 @@ sub run_agent { # If no config yet, the tool's built-in default for # --check-interval is used instead. $interval->($config ? $config->options->{'check-interval'} : ()); - } + } # AGENT_LOOP + + # This shouldn't happen until the service is stopped/killed. + _info('Agent ' . $agent->id . ' has stopped'); return; } @@ -4748,6 +4770,7 @@ sub write_config { close $fh; my ($api_key) = $contents =~ m/^(api-key=\S+)$/m; + # Re-write the api-key, if any, then write the config. open $fh, '>', $file or die "Error opening $file: $OS_ERROR"; if ( $api_key ) { @@ -4762,21 +4785,8 @@ sub write_config { return; } -sub update_config { - my (%args) = @_; - - have_required_args(\%args, qw( - old - new - )) or die; - my $old = $args{old}; - my $new = $args{new}; - - my @options = qw( - ); - -} - +# Write each service to its own file in --lib/. Remove services +# that are not longer implemented (i.e. not in the services array). sub write_services { my (%args) = @_; @@ -4791,6 +4801,7 @@ sub write_services { _info("Writing services to $lib_dir"); + # Write every current service. my %have_service; foreach my $service ( @$services ) { my $file = $lib_dir . '/' . $service->name; @@ -4805,6 +4816,8 @@ sub write_services { $have_service{$service->name} = $service; } + # Remove old services: one's that still exisit but weren't + # writen ^, so they're no longer implemented. opendir my $dh, $lib_dir or die "Error opening $lib_dir: $OS_ERROR"; while ( my $file = readdir($dh) ) { @@ -4821,9 +4834,55 @@ sub write_services { } sub schedule_services { + my (%args) = @_; + + have_required_args(\%args, qw( + services + )) or die; + my $services = $args{services}; + + _info("Scheduling services"); + + my $new_crontab = make_new_crontab(%args); + _info("New crontab:\n", $new_crontab); + + my ($fh, $file) = tempfile(); + print { $fh } $new_crontab; + close $fh; + + system("crontab $file"); + + unlink $file; + return; } +sub make_new_crontab { + my (%args) = @_; + + have_required_args(\%args, qw( + services + )) or die; + my $services = $args{services}; + + # Optional args + my $crontab_list = $args{crontab_list} || `crontab -l`; + + my @other_lines + = grep { $_ !~ m/pt-agent --run-service/ } + split("\n", $crontab_list); + + my @pt_agent_lines; + foreach my $service ( @$services ) { + push @pt_agent_lines, + $service->schedule . " pt-agent --run-service " . $service->name; + } + + my $new_crontab = join("\n", @other_lines, @pt_agent_lines, "\n"); + + return $new_crontab; +} + # #################### # # Service process subs # # #################### # diff --git a/t/pt-agent/make_new_crontab.t b/t/pt-agent/make_new_crontab.t new file mode 100644 index 00000000..10145ee0 --- /dev/null +++ b/t/pt-agent/make_new_crontab.t @@ -0,0 +1,78 @@ +#!/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; +require "$trunk/bin/pt-agent"; + +Percona::Toolkit->import(qw(have_required_args Dumper)); + +my $sample = "t/pt-agent/samples"; + +sub test_make_new_crontab { + my (%args) = @_; + have_required_args(\%args, qw( + file + name + services + )) or die; + my $file = $args{file}; + my $name = $args{name}; + my $services = $args{services}; + + my $crontab_list = slurp_file("$trunk/$sample/$file.in"); + + my $new_crontab = pt_agent::make_new_crontab( + services => $services, + crontab_list => $crontab_list, + ); + + ok( + no_diff( + $new_crontab, + "$sample/$file.out", + cmd_output => 1, + ), + "$name" + ); +} + +# ############################################################################# +# Empty crontab, new service. +# ############################################################################# + +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', + alias => 'Query Monitor', + schedule => '* 8 * * 1,2,3,4,5', + runs => [ $run0 ], +); + +test_make_new_crontab( + name => "crontab001", + file => "crontab001", + services => [ $svc0 ], +); + +# ############################################################################# +# Done. +# ############################################################################# +done_testing; diff --git a/t/pt-agent/samples/crontab001.in b/t/pt-agent/samples/crontab001.in new file mode 100644 index 00000000..e69de29b diff --git a/t/pt-agent/samples/crontab001.out b/t/pt-agent/samples/crontab001.out new file mode 100644 index 00000000..4435ec65 --- /dev/null +++ b/t/pt-agent/samples/crontab001.out @@ -0,0 +1,2 @@ +* 8 * * 1,2,3,4,5 pt-agent --run-service query-monitor +