New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ncm-metaconfig: support daemons with actions #328
Changes from 8 commits
9f91fb5
5530a92
6089770
c9d7330
ee18da8
f363c6a
88176d2
37a0f3b
06274da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,17 +18,83 @@ use Readonly; | |
|
||
Readonly::Scalar my $PATH => '/software/components/${project.artifactId}'; | ||
|
||
# Has to correspond to what is allowed in the schema | ||
Readonly::Hash my %ALLOWED_ACTIONS => { restart => 1, reload => 1, stop_sleep_start => 1 }; | ||
|
||
our $EC=LC::Exception::Context->new->will_store_all; | ||
|
||
our $NoActionSupported = 1; | ||
|
||
# Keep track of all actions to be taken in a hash | ||
# key = action; value = array ref to with all actions | ||
my %actions = (); | ||
|
||
# Convenience method to retrieve actions (mainly for unittests) | ||
# Returns a reference to the actions hash | ||
sub get_actions | ||
{ | ||
my $self = shift; | ||
return \%actions; | ||
} | ||
|
||
# Convenience method to reset actions (mainly for unittests) | ||
sub reset_actions | ||
{ | ||
my $self = shift; | ||
%actions = (); | ||
} | ||
|
||
# Add action for daemon | ||
sub add_action | ||
{ | ||
my ($self, $daemon, $action) = @_; | ||
|
||
$actions{$action} = () if (! $actions{$action}); | ||
if (! grep {$_ eq $daemon} @{$actions{$action}}) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just use a hash here: $actions{$action} ||= {};
$actions{$action}->{$daemon} = 1; I'd move the logging to the end of the caller method. There's little value in logging each insertion. You can simply log the final |
||
$self->debug(1, "Adding daemon $daemon action with action $action"); | ||
push(@{$actions{$action}}, $daemon); | ||
} | ||
} | ||
|
||
# Given metaconfigservice C<$srv> and C<CAF::FileWriter> instance C<$fh>, | ||
# check if a daemon needs to be restarted. | ||
sub needs_restarting | ||
# prepare the actions to be taken for the service service | ||
sub prepare_action | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return a hash of actions. Do not rely on |
||
{ | ||
my ($self, $fh, $srv) = @_; | ||
my ($self, $srv) = @_; | ||
|
||
return $fh->close() && $srv->{daemon} && scalar(@{$srv->{daemon}}); | ||
$self->verbose('File changed, looking for daemons and actions'); | ||
if ($srv->{daemons}) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... and if this was my code, I did it wrong. |
||
while (my ($daemon, $action) = each %{$srv->{daemons}}) { | ||
$self->add_action($daemon, $action); | ||
} | ||
} | ||
|
||
if ($srv->{daemon}) { | ||
$self->verbose("Deprecated daemon(s) restart via daemon field."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this warrant a |
||
foreach my $daemon (@{$srv->{daemon}}) { | ||
if ($srv->{daemons}->{$daemon}) { | ||
$self->verbose('Daemon $daemon also defined in daemons field. Adding restart action anyway.'); | ||
} | ||
$self->add_action($daemon, 'restart'); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even better: my ($self, $srv, $actions) = @_;
...
# Do we need the `if` here? Will it warn on SL5?
if ($srv->{daemons}) {
while (my ($d, $a) = each(%{$srv->{daemons}})) {
$actions->{$d} = $a;
# or $actions->{$a}->{$d} = 1;
}
}
if ($srv->{daemon}) {
foreach my $d (@{$srv->{daemon}}) {
$srv.>{$d} = "restart";
# or $srv->{restart}->{$d} = 1;
}
} this saves you from the logic of merging two return values together if you were to return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd like to keep the current code using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am fine with an |
||
} | ||
|
||
# Restart any daemons whose configurations we have changed. | ||
sub process_actions | ||
{ | ||
my $self = shift; | ||
while (my ($action, $ds) = each %actions) { | ||
my $msg = "action $action for daemons ".join(',', @$ds); | ||
my $srv = CAF::Service->new($ds, log => $self); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you receive a hash of hashes, this turns into while (my ($action, $ds) = each(%actions)) {
my $srv = CAF::Service->new([keys(%$ds)], log => $self); and you save yourself quite some code and complexity above. |
||
if(exists($ALLOWED_ACTIONS{$action})) { | ||
$self->verbose("Taking $msg"); | ||
$srv->$action(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In any case, the |
||
} else { | ||
$self->error("No CAF::Service allowed action $action; no $msg"); | ||
} | ||
} | ||
|
||
} | ||
|
||
# Generate $file, configuring $srv using CAF::TextRender. | ||
|
@@ -61,11 +127,8 @@ sub handle_service | |
return; | ||
} | ||
|
||
if ($self->needs_restarting($fh, $srv)) { | ||
foreach my $d (@{$srv->{daemon}}) { | ||
$self->{daemons}->{$d} = 1; | ||
} | ||
} | ||
$self->prepare_action($srv) if ($fh->close()); | ||
|
||
return 1; | ||
} | ||
|
||
|
@@ -76,15 +139,14 @@ sub Configure | |
|
||
my $t = $config->getElement($PATH)->getTree(); | ||
|
||
$self->reset_actions(); | ||
|
||
while (my ($f, $c) = each(%{$t->{services}})) { | ||
$self->handle_service(unescape($f), $c); | ||
} | ||
|
||
# Restart any daemons whose configurations we have changed. | ||
if ($self->{daemons}) { | ||
my $srv = CAF::Service->new([keys(%{$self->{daemons}})], log => $self); | ||
$srv->restart(); | ||
} | ||
$self->process_actions(); | ||
|
||
return 1; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#!/usr/bin/perl | ||
# -*- mode: cperl -*- | ||
use strict; | ||
use warnings; | ||
use Test::More; | ||
use Test::Quattor qw(actions_daemons actions_nodaemons); | ||
use Test::MockModule; | ||
use NCM::Component::metaconfig; | ||
use CAF::Object; | ||
use CAF::FileWriter; | ||
|
||
$CAF::Object::NoAction = 1; | ||
|
||
my $mock = Test::MockModule->new('CAF::Service'); | ||
our ($restart, $reload); | ||
$mock->mock('restart', sub { | ||
my $self = shift; | ||
$restart += scalar @{$self->{services}}; | ||
}); | ||
$mock->mock('reload', sub { | ||
my $self = shift; | ||
$reload += scalar @{$self->{services}}; | ||
}); | ||
|
||
my $pretend_changed; | ||
|
||
no warnings 'redefine'; | ||
*CAF::FileWriter::close = sub { | ||
return $pretend_changed; | ||
}; | ||
use warnings 'redefine'; | ||
|
||
=pod | ||
|
||
=head1 DESCRIPTION | ||
|
||
Test how the need for restarting a service is handled | ||
|
||
=cut | ||
|
||
|
||
my $cmp = NCM::Component::metaconfig->new('metaconfig'); | ||
|
||
$cmp->add_action("daemon1", "action1"); | ||
$cmp->add_action("daemon2", "action1"); | ||
$cmp->add_action("daemon1", "action2"); | ||
|
||
is_deeply($cmp->get_actions, { "action1" => ["daemon1", "daemon2"], "action2" => ["daemon1"]}, | ||
"Actions added"); | ||
|
||
$cmp->reset_actions; | ||
$cmp->prepare_action({'daemon' => ['d1', 'd2']}); | ||
is_deeply($cmp->get_actions, {"restart" => ["d1", "d2"]}, | ||
"Daemon restart actions added"); | ||
|
||
$cmp->reset_actions; | ||
$cmp->prepare_action({'daemon' => ['d1', 'd2'], | ||
'daemons' => {'d1' => 'reload', | ||
'd2' => 'restart', | ||
'd3' => 'doesnotexist' | ||
}}); | ||
# d2 only once in restart | ||
# d1 in reload and restart | ||
is_deeply($cmp->get_actions, { "restart" => ["d2", "d1"], # restart from daemons is processed first | ||
'reload' => ['d1'], | ||
'doesnotexist' => ['d3']}, | ||
"Daemon restart and daemons actions added"); | ||
|
||
$cmp->process_actions(); | ||
is($restart, 2, '2 restarts triggered'); | ||
is($reload, 1, '1 reload triggered'); | ||
is($cmp->{ERROR}, 1, '1 error logged due to unsupported action'); | ||
|
||
my $cfg_d = get_config_for_profile('actions_daemons'); | ||
my $cfg_nd = get_config_for_profile('actions_nodaemons'); | ||
|
||
$restart = $reload = 0; | ||
$cmp->reset_actions; | ||
is($cmp->Configure($cfg_d), 1, 'Configure actions_daemons returned 1'); | ||
is($restart, 0, '0 restarts triggered (daemons configured, no file changes)'); | ||
is($reload, 0, '0 reload triggered (daemons configured, no file changes)'); | ||
|
||
$restart = $reload = 0; | ||
$cmp->reset_actions; | ||
is($cmp->Configure($cfg_nd), 1, 'Configure actions_nodaemons returned 1'); | ||
is($restart, 0, '0 restarts triggered (no daemons configured, no file changes)'); | ||
is($reload, 0, '0 reload triggered (no daemons configured, no file changes)'); | ||
|
||
# all files are changed files | ||
$pretend_changed=1; | ||
|
||
$restart = $reload = 0; | ||
$cmp->reset_actions; | ||
is($cmp->Configure($cfg_d), 1, 'Configure actions_daemons returned 1'); | ||
is($restart, 1, '1 restarts triggered (daemons configured, file changes)'); | ||
is($reload, 1, '1 reload triggered (daemons configured, file changes)'); | ||
|
||
$restart = $reload = 0; | ||
$cmp->reset_actions; | ||
is($cmp->Configure($cfg_nd), 1, 'Configure actions_nodaemons returned 1'); | ||
is($restart, 0, '0 restarts triggered (no daemons configured, file changes)'); | ||
is($reload, 0, '0 reload triggered (no daemons configured, file changes)'); | ||
|
||
done_testing(); |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't turn this into a global. It's difficult to follow where the actions come from.