From c7c79945f9be63cfca6e329bc747c29cd16be8e3 Mon Sep 17 00:00:00 2001 From: Daniel Nichter Date: Thu, 18 Apr 2013 18:41:05 -0600 Subject: [PATCH] Change start_services() to run_services() and make it do triple duty: start, restart, and stop. When a real service is removed, call its stop- meta-service before write_services(). Add run_services_once() for services with the run_once flag. --- bin/pt-agent | 195 +++++++++++++++++++++++++++------------ t/pt-agent/run_service.t | 20 ++-- 2 files changed, 147 insertions(+), 68 deletions(-) diff --git a/bin/pt-agent b/bin/pt-agent index 66003323..b07ac577 100755 --- a/bin/pt-agent +++ b/bin/pt-agent @@ -4756,7 +4756,6 @@ use Percona::WebAPI::Resource::Agent; use Percona::WebAPI::Resource::Config; use Percona::WebAPI::Resource::Service; use Percona::WebAPI::Representation; -use Percona::WebAPI::Util; Percona::Toolkit->import(qw(_d Dumper have_required_args)); Percona::WebAPI::Representation->import(qw(as_json as_config)); @@ -5621,36 +5620,60 @@ sub get_services { curr_services => $curr_services, ); - # First, save each service in --lib/services/. This must - # be done before calling start_services() because that sub - # looks for --lib/services/start-service, etc. + # First, stop and remove services. Do this before write_services() + # because this call looks for --lib/services/stop-service which + # write_services() removes. I.e. use the service's stop- meta + # counterpart (if any) before we remove the service. + run_services( + action => 'stop', + services => $sorted_services->{removed}, + lib_dir => $lib_dir, + bin_dir => $args{bin_dir}, # optional, for testing + ); + + # Second, save each service in --lib/services/. Do this before + # the next calls to run_services() because those calls look for + # --lib/services/start-service which won't exist for new services + # until written by this call. write_services( sorted_services => $sorted_services, lib_dir => $lib_dir, json => $args{json}, # optional, for testing ); - # Start and restart services. This must be done before calling - # schedule_services() so that, for example, start-query-history - # is ran before query-history is scheduled and starts running. + # Start new services and restart existing updated services. + # Do this before calling schedule_services() so that, for example, + # start-query-history is ran before query-history is scheduled + # and starts running. # Start new services. # TODO: this probably can't/won't fail, but if it does, is it # worth setting success=0? - start_services( + run_services( + action => 'start', services => $sorted_services->{added}, lib_dir => $lib_dir, bin_dir => $args{bin_dir}, # optional, for testing ); - # Restart existing, re-configured services. - start_services( - restart => 1, + # Restart existing updated services. + run_services( + action => 'restart', services => $sorted_services->{updated}, lib_dir => $lib_dir, bin_dir => $args{bin_dir}, # optional, for testing ); + # Run services with the run_once flag. Unlike run_services(), + # this call runs the service directly, whether it's meta or not, + # then it removes it from the services hashref so there's no + # chance of running it again unless it's received again. + run_services_once( + services => $sorted_services->{services}, + lib_dir => $lib_dir, + bin_dir => $args{bin_dir}, # optional, for testing + ); + # Schedule any services with a run_schedule or spool_schedule. # This must be called last, after write_services() and # start_services() because, for example, a service schedule @@ -5698,6 +5721,11 @@ sub sort_services { my $name = $service->name; $services->{$name} = $service; + # run_services() only needs real services, from which it can infer + # certain meta-services like "start-foo" for real service "foo", + # but write_services() needs meta-services too so it can know to + # remove their files from --lib/services/. + if ( !exists $prev_services->{$name} ) { push @added, $service; } @@ -5866,7 +5894,87 @@ sub make_new_crontab { # from the real service's name. A service doesn't require meta-services; # there may be nothing to do to start it, in which case the real service # starts running due to its run_schedule and schedule_services(). -sub start_services { +sub run_services { + my (%args) = @_; + have_required_args(\%args, qw( + action + services + lib_dir + )) or die; + my $action = $args{action}; + my $services = $args{services}; + my $lib_dir = $args{lib_dir}; + + # Optional args + my $bin_dir = defined $args{bin_dir} ? $args{bin_dir} + : "$FindBin::Bin/"; + my $exec_cmd = $args{exec_cmd} || sub { return system(@_) }; + + my $env_vars = env_vars(); + my $log = "$lib_dir/logs/start-stop.log"; + my $cmd_fmt = ($env_vars ? "$env_vars " : '') + . $bin_dir . "pt-agent --run-service %s >> $log 2>&1"; + + SERVICE: + foreach my $service ( @$services ) { + next if $service->meta; # only real services + + my $name = $service->name; + + # To restart, one must first stop, then start afterwards. + if ( $action eq 'stop' || $action eq 'restart' ) { + if ( -f "$lib_dir/services/stop-$name" ) { + my $cmd = sprintf $cmd_fmt, "stop-$name"; + _info("Stopping $name: $cmd"); + $exec_cmd->($cmd); + my $cmd_exit_status = $CHILD_ERROR >> 8; + if ( $cmd_exit_status != 0 ) { + _warn("Error stopping $name, check $log and " + . "$lib_dir/logs/$name.run"); + next SERVICE; + } + } + } + + if ( $action eq 'start' || $action eq 'restart' ) { + # Remove old meta files. Meta files are generally temporary + # in any case, persisting info from one interval to the next. + # If the service has changed (e.g., report interval is longer), + # there's no easy way to tranistion from old metadata to new, + # so we just rm the old metadata and start anew. + my $meta_files = "$lib_dir/meta/$name*"; + foreach my $meta_file ( glob $meta_files ) { + if ( unlink $meta_file ) { + _info("Removed $meta_file"); + } + else { + _warn("Cannot remove $meta_file: $OS_ERROR"); + } + } + + # Start the service and wait for it to exit. If it dies + # really early (before it really begins), our log file will + # have the error; else, the service should automatically + # switch to its default log file ending in ".run". + if ( -f "$lib_dir/services/start-$name" ) { + my $cmd = sprintf $cmd_fmt, "start-$name"; + _info("Starting $name: $cmd"); + $exec_cmd->($cmd); + my $cmd_exit_status = $CHILD_ERROR >> 8; + if ( $cmd_exit_status != 0 ) { + _warn("Error starting $name, check $log and " + ."$lib_dir/logs/$name.run"); + next SERVICE; + } + _info("Started $name successfully"); + } + } + } + + return; +} + +sub run_services_once { my (%args) = @_; have_required_args(\%args, qw( services @@ -5882,62 +5990,27 @@ sub start_services { my $exec_cmd = $args{exec_cmd} || sub { return system(@_) }; my $env_vars = env_vars(); - my $log = "$lib_dir/logs/start.log"; + my $log = "$lib_dir/logs/run-once.log"; my $cmd_fmt = ($env_vars ? "$env_vars " : '') . $bin_dir . "pt-agent --run-service %s >> $log 2>&1"; SERVICE: - foreach my $service ( @$services ) { - next if $service->meta; # only real services + foreach my $name ( sort keys %$services ) { + my $service = $services->{$name}; + next unless $service->run_once; - my $name = $service->name; + delete $services->{$name}; - # To restart, one must first stop, then start afterwards. - if ( $restart ) { - if ( -f "$lib_dir/services/stop-$name" ) { - my $cmd = sprintf $cmd_fmt, "stop-$name"; - _info("Stopping $name: $cmd"); - $exec_cmd->($cmd); - my $cmd_exit_status = $CHILD_ERROR >> 8; - if ( $cmd_exit_status != 0 ) { - _warn("Error stopping $name, check $log and " - . "$lib_dir/logs/$name.run"); - next SERVICE; - } - } - } - - # Remove old meta files. Meta files are generally temporary - # in any case, persisting info from one interval to the next. - # If the service has changed (e.g., report interval is longer), - # there's no easy way to tranistion from old metadata to new, - # so we just rm the old metadata and start anew. - my $meta_files = "$lib_dir/meta/$name*"; - foreach my $meta_file ( glob $meta_files ) { - if ( unlink $meta_file ) { - _info("Removed $meta_file"); - } - else { - _warn("Cannot remove $meta_file: $OS_ERROR"); - } - } - - # Start the service and wait for it to exit. If it dies - # really early (before it really begins), our log file will - # have the error; else, the service should automatically - # switch to its default log file ending in ".run". - if ( -f "$lib_dir/services/start-$name" ) { - my $cmd = sprintf $cmd_fmt, "start-$name"; - _info("Starting $name: $cmd"); - $exec_cmd->($cmd); - my $cmd_exit_status = $CHILD_ERROR >> 8; - if ( $cmd_exit_status != 0 ) { - _warn("Error starting $name, check $log and " - ."$lib_dir/logs/$name.run"); - next SERVICE; - } - _info("Started $name successfully"); + my $cmd = sprintf $cmd_fmt, "start-$name"; + _info("Running $name: $cmd"); + $exec_cmd->($cmd); + my $cmd_exit_status = $CHILD_ERROR >> 8; + if ( $cmd_exit_status != 0 ) { + _warn("Error starting $name, check $log and " + ."$lib_dir/logs/$name.run"); + next SERVICE; } + _info("Ran $name successfully"); } return; diff --git a/t/pt-agent/run_service.t b/t/pt-agent/run_service.t index b7fdb2b4..e5a451ac 100644 --- a/t/pt-agent/run_service.t +++ b/t/pt-agent/run_service.t @@ -36,9 +36,10 @@ my $sample = "t/pt-agent/samples"; # Create fake spool and lib dirs. Service-related subs in pt-agent # automatically add "/services" to the lib dir, but the spool dir is # used as-is. -my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1); -mkdir "$tmpdir/spool" or die "Error making $tmpdir/spool: $OS_ERROR"; -mkdir "$tmpdir/services" or die "Error making $tmpdir/services: $OS_ERROR"; +my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 0); +output( + sub { pt_agent::init_lib_dir(lib_dir => $tmpdir) } +); my $spool_dir = "$tmpdir/spool"; sub write_svc_files { @@ -88,9 +89,10 @@ my $output = output( sub { $exit_status = pt_agent::run_service( service => 'query-history', - spool_dir => $spool_dir, lib_dir => $tmpdir, + spool_dir => $spool_dir, Cxn => '', + suffix => '', # optional, for testing ); }, stderr => 1, @@ -98,11 +100,11 @@ my $output = output( ok( no_diff( - "cat $tmpdir/spool/query-history", + "cat $tmpdir/spool/query-history/query-history", "$sample/query-history/data001.json", ), "1 run: spool data (query-history/data001.json)" -); +) or diag(`ls -l $tmpdir/spool/`); chomp(my $n_files = `ls -1 $spool_dir | wc -l | awk '{print \$1}'`); is( @@ -116,7 +118,7 @@ is( 0, "1 run: exit 0" ); - +exit; # ############################################################################# # Service with two task, both using a program. # ############################################################################# @@ -162,6 +164,7 @@ $output = output( spool_dir => $spool_dir, lib_dir => $tmpdir, Cxn => '', + suffix => '', # optional, for testing ); }, stderr => 1, @@ -291,6 +294,7 @@ SKIP: { spool_dir => $spool_dir, lib_dir => $tmpdir, Cxn => $cxn, + suffix => '', # optional, for testing ); }, stderr => 1, @@ -318,6 +322,7 @@ SKIP: { spool_dir => $spool_dir, lib_dir => $tmpdir, Cxn => $cxn, + suffix => '', # optional, for testing ); }, stderr => 1, @@ -341,6 +346,7 @@ SKIP: { spool_dir => $spool_dir, lib_dir => $tmpdir, Cxn => $cxn, + suffix => '', # optional, for testing ); }, stderr => 1,