Skip to content

Commit

Permalink
Fixes #7279: Add generic methods to create conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
peckpeck committed Oct 22, 2015
1 parent 48481dc commit 3e44962
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 0 deletions.
63 changes: 63 additions & 0 deletions tests/acceptance/30_generic_methods/condition_from_command.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#######################################################
#
# Launch the /bin/date command
#
#######################################################

bundle common acc_path
{
vars:
"root" string => getenv("NCF_TESTS_ACCEPTANCE", 1024);
}

body common control
{
inputs => { "${acc_path.root}/default.cf.sub", "${acc_path.root}/default_ncf.cf.sub", "@{ncf_inputs.default_files}" };
bundlesequence => { configuration, default("${this.promise_filename}") };
version => "1.0";
}

#######################################################

bundle agent init
{
vars:
"command_0" string => "exit 0";
"canonified_command_0" string => canonify("${command_name}");
}

#######################################################

bundle agent test
{
vars:
"true_codes" string => "0,1, 2";
"false_codes" string => "3, 4";

methods:
"phA" usebundle => condition_from_command("classA", "exit 0", "${true_codes}", "${false_codes}"); # true
"phB" usebundle => condition_from_command("classB", "exit 2", "${true_codes}", "${false_codes}"); # true
"phC" usebundle => condition_from_command("classC", "exit 3", "${true_codes}", "${false_codes}"); # false
"phD" usebundle => condition_from_command("classD", "exit 4", "${true_codes}", "${false_codes}"); # false
"phE" usebundle => condition_from_command("classE", "exit 5", "${true_codes}", "${false_codes}"); # error
}

#######################################################

bundle agent check
{
classes:
"ok_A" expression => "condition_from_command_exit_0_kept.!condition_from_command_exit_0_repaired.!condition_from_command_exit_0_error.classA_true.!classA_false";
"ok_B" expression => "condition_from_command_exit_2_kept.!condition_from_command_exit_2_repaired.!condition_from_command_exit_2_error.classB_true.!classB_false";
"ok_C" expression => "condition_from_command_exit_3_kept.!condition_from_command_exit_3_repaired.!condition_from_command_exit_3_error.!classC_true.classC_false";
"ok_D" expression => "condition_from_command_exit_4_kept.!condition_from_command_exit_4_repaired.!condition_from_command_exit_4_error.!classD_true.classD_false";
"ok_E" expression => "!condition_from_command_exit_5_kept.!condition_from_command_exit_5_repaired.condition_from_command_exit_5_error.!classE_true.!classE_false";

"ok" expression => "ok_A.ok_B.ok_C.ok_D.ok_E";

reports:
ok::
"$(this.promise_filename) Pass";
!ok::
"$(this.promise_filename) FAIL";
}
53 changes: 53 additions & 0 deletions tests/acceptance/30_generic_methods/condition_from_expression.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#######################################################
#
# Test if only success classes are created
#
#######################################################

bundle common acc_path
{
vars:
"root" string => getenv("NCF_TESTS_ACCEPTANCE", 1024);
}

body common control
{
inputs => { "${acc_path.root}/default.cf.sub", "${acc_path.root}/default_ncf.cf.sub", "@{ncf_inputs.default_files}" };
bundlesequence => { configuration, default("$(this.promise_filename)") };
version => "1.0";
}

#######################################################

bundle agent init
{
vars:
"tmp" string => getenv("TEMP", 1024);
}

#######################################################

bundle agent test
{
methods:
"ph1" usebundle => condition_from_expression("class1", "any");
"ph2" usebundle => condition_from_expression("class2", "!any");

}

#######################################################

bundle agent check
{
classes:
"ok_class1" expression => "condition_from_expression_class1_ok.class1_true";
"ok_class2" expression => "condition_from_expression_class2_ok.class2_false";

"ok" expression => "ok_class1.ok_class2";

reports:
ok::
"$(this.promise_filename) Pass";
!ok::
"$(this.promise_filename) FAIL";
}
60 changes: 60 additions & 0 deletions tree/30_generic_methods/condition_from_command.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#####################################################################################
# Copyright 2015 Normation SAS
#####################################################################################
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

# @name Condition from command
# @description Execute a command and create outcome classes depending on its exit code
#
# @parameter condition_prefix The condition name, use ${class_prefix} to create a local condition
# @parameter command The command to run
# @parameter true_codes List of codes that produce a true status separated with commas (ex: 1,2,5)
# @parameter false_codes List of codes that produce a false status separated with commas (ex: 3,4,6)
#
# @class_prefix condition_from_command
# @class_parameter condition_name
#
# This bundle will define a class condition_from_command_${command_name}_{kept,not_ok,ok,reached}
# depending on the exit codes given in parameters.
#
# If the exit code is in the list, this will always produce a kept outcome class.
# If the exit code is not in the list, this will produce an error outcome class.
# If the exit code is in the list, this will additionnaly produce a ${condition_prefix}_true or a ${condition_prefix}_false condition.
#
# The created condition (class in cfengine speaking) is global to the agent.
# To make the condition specific to current technique instance (or directive), add ${class_prefix} within the condition prefix,
# this variable is automatically defined in your technique and is available if you use Rudder agent >= 4.0 or cfengine >= 3.8

bundle agent condition_from_command(condition_prefix, command, true_codes, false_codes)
{
vars:
"old_class_prefix" string => canonify("condition_from_command_${command}");
"class_prefix" string => canonify(join("_", "this.callers_promisers"));
"args" slist => { "${condition_prefix}", "${command}", "${true_codes}", "${false_codes}" };

"true_list" slist => string_split("${true_codes}", "\s*,\s*", "256");
"false_list" slist => string_split("${false_codes}", "\s*,\s*", "256");

methods:
"report"
usebundle => _logger("Execute the test command ${command} to create ${condition_prefix}_{true,false}", "${old_class_prefix}", "${class_prefix}", @{args}),
ifvarclass => "(!has_promiser_stack.${old_class_prefix}_reached)|(has_promiser_stack.${class_prefix}_reached)";

commands:
"${command}"
contain => in_shell,
classes => classes_generic_return_boolean_list_two("${old_class_prefix}", "${class_prefix}", "${condition_prefix}", @{true_list}, @{false_list});
}
57 changes: 57 additions & 0 deletions tree/30_generic_methods/condition_from_expression.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#####################################################################################
# Copyright 2015 Normation SAS
#####################################################################################
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

# @name Condition from expression
# @description Create a new condition class
#
# @parameter condition_prefix The condition prefix, use ${class_prefix}_name to create a local condition
# @parameter condition_expression The expression evaluated to create the condition (use 'any' to alwaus evaluate to true)
#
# @class_prefix condition_from_expression
# @class_parameter condition_prefix
#
# This bundle will define a class condition_from_expression_${condition_name}_{kept,ok,reached}
#
# This bundle will additionnaly produce a ${condition_prefix}_true or a ${condition_prefix}_false condition depending on the result on the expression
#
# Calling this method with a condition expression transforms a complex expression into a single class condition.
#
# The created condition (class in cfengine speaking) is global to the agent.
# To make the condition specific to current technique instance (or directive), add ${class_prefix} within the condition prefix,
# this variable is automatically defined in your technique and is available if you use Rudder agent >= 4.0 or cfengine >= 3.8
#
bundle agent condition_from_expression(condition_prefix, condition_expression)
{
vars:
"old_class_prefix" string => canonify("condition_from_expression_${condition_prefix}");
"class_prefix" string => canonify(join("_", "this.callers_promisers"));
"args" slist => { "${condition_prefix}", "${condition_expression}" };

classes:
"${condition_prefix}_true" expression => "${condition_expression}",
scope => "namespace";

"${condition_prefix}_false" expression => "!(${condition_expression})",
scope => "namespace";

methods:
"success" usebundle => _classes_success("${old_class_prefix}");
"success" usebundle => _classes_success("${class_prefix}");

"report" usebundle => _logger("Create the condition ${condition_prefix}_{true,false} with ${condition_expression}", "${old_class_prefix}", "${class_prefix}", @{args});
}
59 changes: 59 additions & 0 deletions tree/30_generic_methods/condition_from_expression_persistent.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#####################################################################################
# Copyright 2015 Normation SAS
#####################################################################################
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

# @name Condition from expression persistent
# @description Create a new condition class that persists accross runs
#
# @parameter condition_prefix The condition prefix, use ${class_prefix}_name to create a local condition
# @parameter condition_expression The expression evaluated to create the condition (use 'any' to alwaus evaluate to true)
# @parameter duration The persistence suffix in minutes
#
# @class_prefix condition_from_expression_persistent
# @class_parameter condition_prefix
#
# This bundle will define a class condition_from_expression_persistent_{kept,ok,reached}
#
# This bundle will additionnaly produce a ${condition_prefix}_true or a ${condition_prefix}_false condition depending on the result on the expression
#
# The created condition (class in cfengine speaking) is global to the agent and is persisted accross runs.
# The persistence duration is controlled using ${duration}. There is no way to persist indefinitly.
# To make the condition specific to current technique instance (or directive), add ${class_prefix} within the condition prefix,
# this variable is automatically defined in your technique and is available if you use Rudder agent >= 4.0 or cfengine >= 3.8
#
bundle agent condition_from_expression_persistent(condition_prefix, condition_expression, duration)
{
vars:
"old_class_prefix" string => canonify("condition_from_expression_persistent_${command}");
"class_prefix" string => canonify(join("_", "this.callers_promisers"));
"args" slist => { "${condition_prefix}", "${condition_expression}", "${duration}" };

classes:
"${condition_prefix}_true" expression => "${condition_expression}",
persistence => "${duration}",
scope => "namespace";

"${condition_prefix}_false" expression => "!(${condition_expression})",
persistence => "${duration}",
scope => "namespace";

methods:
"success" usebundle => _classes_success("${old_class_prefix}");
"success" usebundle => _classes_success("${class_prefix}");

"report" usebundle => _logger("Create the persistent condition ${condition_prefix}_{true,false} with ${condition_expression}", "${old_class_prefix}", "${class_prefix}", @{args});
}

0 comments on commit 3e44962

Please sign in to comment.