Skip to content
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

Fixes #18695: Create a technique to manage yum repository #1645

1 change: 1 addition & 0 deletions maintained-techniques
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ applications/aptPackageManagerSettings/3.2
applications/packageManagement/1.2
applications/repoGpgKeyManagement/1.1
applications/snmp/1.0
applications/yumPackageManagerRepositories/1.0/
applications/zmdPackageManagerSettings/3.0
applications/zypperPackageManagerRepositories/1.0
applications/zypperPackageManagerSetup/1.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Nicolas CHARLES <nicolas.charles@normation.com> Wed Nov 25 12:00:00 CEST 2014
* Version 1.0
** Creation of technique to manage Yum repositories

110 changes: 110 additions & 0 deletions techniques/applications/yumPackageManagerRepositories/1.0/metadata.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<TECHNIQUE name="Package sources (Yum)">
<DESCRIPTION>This technique configures repositories for Yum package manager.

Each repositories are identified by their repository id.</DESCRIPTION>

<MULTIINSTANCE>true</MULTIINSTANCE>
<POLICYGENERATION>separated</POLICYGENERATION>

<COMPATIBLE>
<AGENT version=">= 3.12.0">cfengine-community</AGENT>
</COMPATIBLE>

<BUNDLES>
<NAME>yum_repositories_management_RudderUniqueID</NAME>
</BUNDLES>

<TMLS>
<TML name="yum-repositories-management"/>
</TMLS>

<FILES>
<FILE name="yum-repositories-post-hook.cf">
<OUTPATH>yumPackageManagerRepositories/yum-repositories-post-hook.cf</OUTPATH>
<INCLUDED>true</INCLUDED>
</FILE>
</FILES>

<RUNHOOKS>
<POST bundle="runhook_yum_repositories">
<REPORT name="Refresh repositories"/>
</POST>
</RUNHOOKS>


<TRACKINGVARIABLE>
<SAMESIZEAS>YUM_REPO_ID</SAMESIZEAS>
</TRACKINGVARIABLE>

<SECTIONS>
<SECTION name="Repositories" multivalued="true" component="true" componentKey="YUM_REPO_ID">
<INPUT>
<NAME>YUM_REPO_ID</NAME>
<DESCRIPTION>Repository id</DESCRIPTION>
<LONGDESCRIPTION>The unique repository id, in [ ] in the repository configuration</LONGDESCRIPTION>
</INPUT>
<SELECT1>
<NAME>YUM_REPO_ACTION</NAME>
<DESCRIPTION>Add/modify or delete the repository</DESCRIPTION>
<ITEM>
<VALUE>add</VALUE>
<LABEL>Add/modify</LABEL>
</ITEM>
<ITEM>
<VALUE>delete</VALUE>
<LABEL>Delete</LABEL>
</ITEM>
<CONSTRAINT>
<DEFAULT>add</DEFAULT>
</CONSTRAINT>
</SELECT1>
<INPUT>
<NAME>YUM_REPO_NAME</NAME>
<DESCRIPTION>Repository name</DESCRIPTION>
</INPUT>
<INPUT>
<NAME>YUM_REPO_URL</NAME>
<DESCRIPTION>Repository URL</DESCRIPTION>
</INPUT>
<SELECT1>
<NAME>YUM_REPO_ENABLED</NAME>
<DESCRIPTION>Repository activation status</DESCRIPTION>
<LONGDESCRIPTION>Activate or deactive a specific repository</LONGDESCRIPTION>
<ITEM>
<VALUE>0</VALUE>
<LABEL>Disabled</LABEL>
</ITEM>
<ITEM>
<VALUE>1</VALUE>
<LABEL>Enabled</LABEL>
</ITEM>
<CONSTRAINT>
<DEFAULT>1</DEFAULT>
</CONSTRAINT>
</SELECT1>
<SELECT1>
<NAME>YUM_REP_GPG_CHECK</NAME>
<DESCRIPTION>Activate GPG check</DESCRIPTION>
<ITEM>
<VALUE>0</VALUE>
<LABEL>No</LABEL>
</ITEM>
<ITEM>
<VALUE>1</VALUE>
<LABEL>Yes</LABEL>
</ITEM>
<CONSTRAINT>
<DEFAULT>1</DEFAULT>
</CONSTRAINT>
</SELECT1>
<INPUT>
<NAME>YUM_REPO_GPG_URI</NAME>
<DESCRIPTION>Location of the GPG Key</DESCRIPTION>
<CONSTRAINT>
<MAYBEEMPTY>true</MAYBEEMPTY>
</CONSTRAINT>
</INPUT>
</SECTION>
<SECTION name="Refresh repositories" component="true"/>
</SECTIONS>
</TECHNIQUE>
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
bundle agent yum_repositories_management_&RudderUniqueID& {

vars:
&YUM_REPO_ID:{repo_id |"repo_id[&i&]" string => "&repo_id&";
}&
&YUM_REPO_NAME:{repo_name |"repo_name[&i&]" string => "&repo_name&";
}&
&YUM_REPO_ACTION:{repo_action |"repo_action[&i&]" string => "&repo_action&";
}&

&YUM_REPO_URL:{repo_url |"repo_url[&i&]" string => "&repo_url&";
}&
&YUM_REPO_ENABLED:{repo_enabled |"repo_enabled[&i&]" string => "&repo_enabled&";
}&
&YUM_REP_GPG_CHECK:{repo_gpg_check |"repo_gpg_check[&i&]" string => "&repo_gpg_check&";
}&
&YUM_REPO_GPG_URI:{repo_gpg_uri |"repo_gpg_uri[&i&]" string => "&repo_gpg_uri&";
}&
&TRACKINGKEY:{uuid |"trackingkey[&i&]" string => "&uuid&";
}&

"index" slist => getindices("repo_id");


# Construct the identifier string to grep: \[repo_id\], with start and end line
# there may be some spaces after the repo identifier
"grep_repo_id[${index}]" string => "^\[${repo_id[${index}]}\]\s*$";

pass1::
"filename[${index}]" string => execresult("${paths.grep} -rl '${grep_repo_id[${index}]}' /etc/yum.repos.d", "noshell");

# override filename if repo is absent
"filename[${index}]" string => "/etc/yum.repos.d/rudder-${repo_id[${index}]}.repo",
if => "repo_entry_absent_${index}";

"canonified_filename[${index}]" string => canonify("${filename[${index}]}");

classes:
"delete_repo_${index}" expression => strcmp("${repo_action[${index}]}", "delete");

"is_yum_present" expression => fileexists("/usr/bin/yum");

pass1::
"repo_entry_absent_${index}" expression => strcmp("${filename[${index}]}", "");

any::
"pass3" expression => "pass2";
"pass2" expression => "pass1";
"pass1" expression => "any";


methods:
pass2.is_yum_present::
# create or enforce entries
# if file doesn't exist yet, we need to precreate it with an empty section, and a blank line
"create file ${index}" usebundle => file_content("${filename[${index}]}", "[${repo_id[${index}]}]
", "false"),
if => "!delete_repo_${index}.repo_entry_absent_${index}";
"set repo name ${index}" usebundle => file_key_value_present_in_ini_section("${filename[${index}]}", "${repo_id[${index}]}", "name", "${repo_name[${index}]}"),
if => "!delete_repo_${index}";
"set repo url ${index}" usebundle => file_key_value_present_in_ini_section("${filename[${index}]}", "${repo_id[${index}]}", "baseurl", "${repo_url[${index}]}"),
if => "!delete_repo_${index}";
"set repo enabled ${index}" usebundle => file_key_value_present_in_ini_section("${filename[${index}]}", "${repo_id[${index}]}", "enabled", "${repo_enabled[${index}]}"),
if => "!delete_repo_${index}";
"set repo gpgcheck ${index}" usebundle => file_key_value_present_in_ini_section("${filename[${index}]}", "${repo_id[${index}]}", "gpgcheck", "${repo_gpg_check[${index}]}"),
if => "!delete_repo_${index}";
"set repo gpg uri ${index}" usebundle => file_key_value_present_in_ini_section("${filename[${index}]}", "${repo_id[${index}]}", "gpgkey", "${repo_gpg_uri[${index}]}"),
if => "!delete_repo_${index}";


# report
# Using old_class_prefix to avoid having to copy all classes. Drawback is
# if there are several repos managed in the same file, there will be
# classes colision
"report for modification" usebundle => rudder_common_reports_generic_index("Package sources (Yum)", "file_key_value_present_in_ini_section_${canonified_filename[${index}]}", "${trackingkey[${index}]}", "Repositories", "${repo_id[${index}]}", "Setting repository ${repo_name[${index}]} with id ${repo_id[${index}]} ", "${index}"),
if => "!delete_repo_${index}";

# Deletion of repo
"delete repo ${index}" usebundle => clean_repo_file_&RudderUniqueID&("${index}"),
if => "delete_repo_${index}.!repo_entry_absent_${index}";

"report for deletion" usebundle => rudder_common_reports_generic_index("Package sources (Yum)", "yum_repositories_repo_removed_${repo_id[${index}]}", "${trackingkey[${index}]}", "Repositories", "${repo_id[${index}]}", "Deleting repository with id ${repo_id[${index}]} ", "${index}"),
if => "delete_repo_${index}.!repo_entry_absent_${index}";

"repo not there" usebundle => rudder_common_report_index("Package sources (Yum)", "result_success", "${trackingkey[${index}]}", "Repositories", "${repo_id[${index}]}", "Deleting repository with id ${repo_id[${index}]} ", "${index}"),
if => "repo_entry_absent_${index}";

"trigger refresh if repo changed ${index}" usebundle => yum_repositories_set_refresh_condition_for_post_hook,
if => "file_key_value_present_in_ini_section_${canonified_filename[${index}]}_repaired|yum_repositories_repo_removed_${repo_id[${index}]}_repaired";

!is_yum_present::
"not a yum system" usebundle => rudder_common_report_index("Package sources (Yum)", "result_na", "${trackingkey[${index}]}", "Repositories", "${repo_id[${index}]}", "Yum is not available on this system, skipping", "${index}");
}

# Delete repo entrys
bundle agent clean_repo_file_&RudderUniqueID&(index) {
vars:
# search all sections in the repos
"grep_string_${index}" string => "'^\[.*\]\s*$'";
"numberofentriesinfile_${index}" string => execresult("${paths.grep} -cl '${grep_string_${index}}' /etc/yum.repos.d/${yum_repositories_management_&RudderUniqueID&.filename[${index}]}", "noshell");

# innder class prefix
"inner_class_prefix_file_absent_${index}" string => "file_absent_${yum_repositories_management_&RudderUniqueID&.canonified_filename[${index}]}";
"inner_class_prefix_section_absent_${index}" string => "file_ini_section_absent_${yum_repositories_management_&RudderUniqueID&.canonified_filename[${index}]}_${yum_repositories_management_&RudderUniqueID&.repo_id[${index}]}";
classes:
"multiple_section_in_file_${index}" not => strcmp("${numberofentriesinfile_${index}}", "1");

methods:
# Deletion of repo
"delete repo ${index}" usebundle => file_absent("${yum_repositories_management_&RudderUniqueID&.filename[${index}]}"),
if => "!multiple_section_in_file_${index}";
# Deletion of repo
"delete repo ${index}" usebundle => file_ini_section_absent("${yum_repositories_management_&RudderUniqueID&.filename[${index}]}", "${yum_repositories_management_&RudderUniqueID&.repo_id[${index}]}"),
if => "multiple_section_in_file_${index}";

"combine class" usebundle => ncf_classes_combine_two("${inner_class_prefix_file_absent_${index}}", "${inner_class_prefix_section_absent_${index}}", "yum_repositories_repo_removed_${yum_repositories_management_&RudderUniqueID&.repo_id[${index}]}");
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
bundle agent yum_repositories_set_refresh_condition_for_post_hook()
{
classes:
"yum_repositories_refresh_condition_for_post_hook_defined" expression => "any", scope => "namespace";
}

#
# Refresh yum if repositories are changed
# Parameters are:
# "name" : Repo id
# "condition" : Condition on when to check the package (not mandatory)
# Reports format is
# "id" : Rule id
# "mode" : enforce or audit
# "technique" : technique name
# "name" : component name for reporting
# "value" : component value for reporting
bundle agent runhook_yum_repositories(json) {
vars:
"definitions" data => parsejson("${json}");

"parameters" data => mergedata("definitions[parameters]");

"reporting" data => mergedata("definitions[reports]");

"reportkeys" slist => getindices("reporting");

"yum_path" string => "/usr/bin/yum";
"cyum_path" string => canonify("${yum_path}");

pass1.is_enforce::
"set_dry_mode" string => "false";

pass1.!is_enforce::
"set_dry_mode" string => "true";

pass1::
"class_condition" string => "yum_repositories_refresh_condition_for_post_hook_defined";


classes:
# define if there is at least one enforce
"is_enforce" expression => strcmp("${reporting[${reportkeys}][mode]}", "enforce");

# For reporting, detect each enforce/audit
"is_enforce_${reportkeys}" expression => strcmp("${reporting[${reportkeys}][mode]}", "enforce");


any::
"pass2" expression => "pass1";
"pass1" expression => "any";

methods:
pass2::
"configure_dry_run_mode_${reporting[0][id]}"
usebundle => set_dry_run_mode("${set_dry_mode}");

# All GM reports at least a log_info, so we need to set the reporting context correctly
"set_reporting_context_for_GM"
usebundle => rudder_reporting_context_id("${reporting[0][id]}", "${reporting[0][technique]}");

"disable_reporting_sudo_pre_hook"
usebundle => disable_reporting;

"refresh yum"
usebundle => command_execution("${yum_path} clean expire-cache"),
ifvarclass => "${class_condition}";

"reenable_reporting_sudo_pre_hook"
usebundle => enable_reporting;

# here we need to report correctly based on each mode for each directive
"reporting"
usebundle => _rudder_common_reports_generic_hooks("${reporting[${reportkeys}][technique]}", "command_execution_${cyum_path}_clean_expire_cache", "${reporting[${reportkeys}][id]}", "${reporting[${reportkeys}][name]}", "${reporting[${reportkeys}][value]}", "Refresh yum repositories", "${reporting[${reportkeys}][mode]}"),
ifvarclass => "${class_condition}";

"na_report"
usebundle => _rudder_common_report_hooks("${reporting[${reportkeys}][technique]}", "result_na", "${reporting[${reportkeys}][id]}", "${reporting[${reportkeys}][name]}", "${reporting[${reportkeys}][value]}", "Refreshing yum repositories was not necessary", "${reporting[${reportkeys}][mode]}"),
ifvarclass => "!${class_condition}";

"clean_reporting_context_${reporting[0][id]}"
usebundle => clean_reporting_context;

}