Skip to content

Commit

Permalink
ncm-metaconfig: setup command registry for running arbitrary commands
Browse files Browse the repository at this point in the history
  • Loading branch information
stdweird committed Jun 11, 2020
1 parent 2f715a4 commit f725f72
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 37 deletions.
20 changes: 17 additions & 3 deletions ncm-metaconfig/src/main/pan/components/metaconfig/schema.pan
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type ${project.artifactId}_textrender_convert = {

type caf_service_action = string with match(SELF, '^(restart|reload|stop_sleep_start)$');

type ${project.artifactId}_commands = {
type ${project.artifactId}_actions = {
@{Always run, happens before possible modifications}
'pre' ? string
@{Always run, happens before possible modifications, the file content is passed om stdin.
Expand Down Expand Up @@ -130,12 +130,26 @@ type ${project.artifactId}_config = {
'contents' : ${project.artifactId}_extension
@{Predefined conversions from EDG::WP4::CCM::TextRender}
'convert' ? ${project.artifactId}_textrender_convert
@{Commands to run on pre, validation and/or post step.
@{Actions (i.e. named registered commands) to run on pre, validation and/or post step.
These are independent of daemons and are executed at time of processing each service.}
'commands' ? ${project.artifactId}_commands
'actions' ? ${project.artifactId}_actions
} = dict();

type ${project.artifactId}_component = {
include structure_component
'services' : ${project.artifactId}_config{} with valid_absolute_file_paths(SELF)
@{Command registry for allowed actions, keys should be used as action value}
'commands' ? string{}
} with {
foreach (esc_fn; srv; SELF['services']) {
if (exists(srv['actions'])) {
foreach (action; cmd_ref; srv['actions']) {
if (!(exists(SELF['commands']) && exists(SELF['commands'][cmd_ref]))) {
error('Found %s action %s for %s, but no matching command registered',
action, cmd_ref, unescape(esc_fn));
};
};
};
};
true;
};
82 changes: 57 additions & 25 deletions ncm-metaconfig/src/main/perl/metaconfig.pm
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,16 @@ use EDG::WP4::CCM::Path qw(unescape);
use Readonly;

# Has to correspond to what is allowed in the schema
Readonly::Hash my %ALLOWED_ACTIONS => { restart => 1, reload => 1, stop_sleep_start => 1 };
Readonly::Hash my %ALLOWED_DAEMON_ACTIONS => { restart => 1, reload => 1, stop_sleep_start => 1 };

our $EC = LC::Exception::Context->new->will_store_all;

our $NoActionSupported = 1;

# Given metaconfigservice C<$srv> for C<$file> and hash-reference C<$actions>,
# prepare the actions to be taken for this service/file.
# prepare the srv_actions to be taken for this service/file.
# C<actions> is updated in-place; does not return anything.
sub prepare_action
sub prepare_daemon_action
{
my ($self, $srv, $file, $actions) = @_;

Expand All @@ -190,8 +190,8 @@ sub prepare_action
}

my @acts;
while (my ($daemon,$action) = splice(@daemon_action, 0, 2)) {
if (exists($ALLOWED_ACTIONS{$action})) {
while (my ($daemon, $action) = splice(@daemon_action, 0, 2)) {
if (exists($ALLOWED_DAEMON_ACTIONS{$action})) {
$actions->{$action} ||= {};
$actions->{$action}->{$daemon} = 1;
push(@acts, "$daemon:$action");
Expand All @@ -211,7 +211,7 @@ sub prepare_action

# Take the action for all daemons as defined in hash-reference C<$actions>.
# Does not return anything.
sub process_actions
sub process_daemon_actions
{
my ($self, $actions) = @_;
foreach my $action (sort keys %$actions) {
Expand All @@ -224,13 +224,16 @@ sub process_actions
}

# Run $service shell command of $type (ie a string) (if defined).
# If $commands is undefined, nothing will be run or logged
# $msg is a reporting prefix
# When $input is not undef, pass it on stdin
# Return 1 on success, undef otherwise.
sub run_shell_command
{
my ($self, $commands, $type, $input) = @_;

return 1 if ! defined($commands);

my $command = $commands->{$type};
if ($command) {
$self->debug(1, "Going to run $type command '$command'");
Expand Down Expand Up @@ -269,23 +272,25 @@ sub run_shell_command
# contents C<$contents> (if C<$contents> is not defined,
# C<$srv->{contents}> is used).
# Also tracks the actions that need to be taken via the
# C<$actions> hash-reference.
# C<$daemon_actions> hash-reference.
# C<$commands> is a hashref with pre/test/changed/post action commands.
# (If it is undefined, nothing will be run or logged, see run_shell_command)
# Returns undef in case of rendering or other failure, 1 otherwise.
sub handle_service
{
my ($self, $file, $srv, $contents, $actions) = @_;
my ($self, $file, $srv, $contents, $daemon_actions, $commands) = @_;

my $commands = $srv->{commands} || {};
return if ! $self->run_shell_command($commands, 'pre');

$contents = $srv->{contents} if (! defined($contents));

my $trd = EDG::WP4::CCM::TextRender->new($srv->{module},
$contents,
log => $self,
eol => 0,
element => $srv->{convert},
);
my $trd = EDG::WP4::CCM::TextRender->new(
$srv->{module},
$contents,
log => $self,
eol => 0,
element => $srv->{convert},
);

my %opts = (
log => $self,
Expand Down Expand Up @@ -317,7 +322,7 @@ sub handle_service

if ($fh->close()) {
$self->info("File $file updated");
$self->prepare_action($srv, $file, $actions);
$self->prepare_daemon_action($srv, $file, $daemon_actions);
return if ! $self->run_shell_command($commands, 'changed');
} else {
$self->verbose("File $file up-to-date");
Expand All @@ -326,46 +331,73 @@ sub handle_service
return $self->run_shell_command($commands, 'post');
}

# Lookup actions in command registry, and return hashref with actual commands for each action
sub resolve_command_actions
{
my ($self, $command_registry, $actions) = @_;

my $commands = {}; # this will trigger reporting that nothing is configured is this stays empty
foreach my $type (sort keys %$actions) {
my $action = $actions->{$type};
my $command = $command_registry->{$action};
if ($command) {
$commands->{$type} = $command;
$self->verbose("Resolved $type action $action to command '$command'");
} else {
# Not fatal, should be covered in schema already
$self->error("Unable to resovle $type action $action to command");
}
};
return $commands;
}


sub _configure_files
{
my ($self, $config, $root) = @_;
my ($self, $config, %opts) = @_;

my $root = defined($opts{root}) ? $opts{root} : '';
my $run_commands = defined($opts{run_commands}) ? $opts{run_commands} : 1;

my $t = $config->getElement($self->prefix)->getTree();

my $actions = {};
my $daemon_actions = {};

foreach my $esc_filename (sort keys %{$t->{services}}) {
my $srvc = $t->{services}->{$esc_filename};
my $cont_el = $config->getElement($self->prefix()."/services/$esc_filename/contents");
my $filename = ($root || '') . unescape($esc_filename);
$self->handle_service($filename, $srvc, $cont_el, $actions);
my $filename = $root . unescape($esc_filename);

# Only when run_commands is false, use undef so nothing is even reported
my $commands = $run_commands ? $self->resolve_command_actions($t->{commands}, $srvc->{actions} || {}) : undef;

$self->handle_service($filename, $srvc, $cont_el, $daemon_actions, $commands);
}

return $actions;
return $daemon_actions;
}

sub Configure
{
my ($self, $config) = @_;

my $actions = $self->_configure_files($config);
my $daemon_actions = $self->_configure_files($config);

$self->process_actions($actions);
$self->process_daemon_actions($daemon_actions);

return 1;
}

# Generate the files relative to metaconfig subdirectory
# under the configuration cachemanager cache path.
# No daemons will be restarted.
# No daemons will be restarted, no commands run.
sub aii_command
{
my ($self, $config) = @_;

my $root = $config->{cache_path};
if ($root) {
$self->_configure_files($config, "$root/metaconfig");
$self->_configure_files($config, root => "$root/metaconfig", run_commands => 0);
return 1;
} else {
$self->error("No cache_path found for Configuration instance");
Expand Down
2 changes: 1 addition & 1 deletion ncm-metaconfig/src/test/perl/aii_command.t
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ isa_ok($fh, "CAF::FileWriter");
$fh = get_file("/foo/bar");
ok(!defined($fh), "Nothing created at regular file location");

ok(command_history_ok(undef, ['service foo']), "serivce foo not restarted");
ok(command_history_ok(undef, ['service foo', 'cmd']), "serivce foo not restarted, no cmd run");

done_testing();
2 changes: 1 addition & 1 deletion ncm-metaconfig/src/test/resources/aii.pan
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
object template aii;

include 'simple';
include 'simple_commands';
8 changes: 1 addition & 7 deletions ncm-metaconfig/src/test/resources/commands.pan
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
object template commands;

include 'simple';

prefix "/software/components/metaconfig/services/{/foo/bar}/commands";
"pre" = "cmd pre";
"test" = "cmd test";
"changed" = "cmd changed";
"post" = "cmd post";
include 'simple_commands';

0 comments on commit f725f72

Please sign in to comment.