bundle agent env_classification # @brief Classify environment { vars: # We use presence of key files to know the hosts environment "environment_semaphores" slist => { "/etc/prod", "/etc/staging" }; "environment" string => ifelse( filesexist( @(environment_semaphores) ), "incoherent", fileexists("/etc/prod"), "production", fileexists("/etc/staging"), "staging", "unknown" ); "env_issue_msg" string => ifelse( strcmp( "incoherent", $(environment) ), "WARNING: Environment incoherent (multiple environment semaphores)", strcmp( "unknown", $(environment) ), "WARNING: Environment unknown (missing environment semaphores)", "Host environment classified as $(environment)"); # This is to extract the cfengine role ( hub or client ) "cf_role" string => ifelse( "policy_server", "Policy Server", "Policy Client"); classes: # We define a class for the selected environment. It's useful for making # decisions in other policies "env_$(environment)" expression => "any", scope => "namespace"; } bundle agent env_info # @brief Relevant environment information { vars: ## Based on the environment we define different contacts. "admin_contact" slist => { "admin@acme.com", "oncall@acme.com" }, if => strcmp( $(env_classification.environment), "production" ); "admin_contact" slist => { "dev@acme.com" }, if => strcmp( $(env_classification.environment), "staging" ); "admin_contact" slist => { "root@localhost" }, if => strcmp( $(env_classification.environment), "unknown" ); ## This is to extract the available package updates status "updates_available" data => packageupdatesmatching(".*", ".*", ".*", ".*"); "count_updates" int => length("updates_available"); classes: # We define a class indicating there are updates available, it might be # useful for various different policies. "have_updates_available" expression => isgreaterthan( $(count_updates), 0), scope => "namespace"; } bundle agent motd { vars: "motd_path" string => "/etc/motd"; # It's considered best practice to prepare a data container holding the # information you need to render the template instead of relying on # current datastate() # First we extract currently defined classes from datastate(), then we # construct the template data. "_state" data => datastate(), if => not( isvariable ( $(this.promiser) ) ); # Limit recursive growth # and multiple calls to # datastate() over # multiple passes. "template_data" data => mergedata('{ "fqhost": "$(sys.fqhost)", "primary_ip": "$(sys.ipv4)", "cf_version": "$(sys.cf_version)", "issue_msg": "$(env_classification.env_issue_msg)", "cf_role": "$(env_classification.cf_role)", "count_updates": "$(env_info.count_updates)", "contacts": env_info.admin_contact, "classes": _state[classes] }'); files: "$(motd_path)" create => "true", template_method => "inline_mustache", template_data => @(template_data), edit_template_string => '# Managed by CFEngine {{{issue_msg}}} *** *** Welcome to {{{fqhost}}} *** * *** * CFEngine Role: {{{cf_role}}} * *** * CFEngine Version:{{{cf_version}}} * *** * * * Host IP: {{{primary_ip}}} *** {{#classes.have_updates_available}}{{{count_updates}}} package updates available.{{/classes.have_updates_available}}{{^classes.have_updates_available}}No package updates available or status unknown.{{/classes.have_updates_available}} * * * * * * For support contact:{{#contacts}} - {{{.}}}{{/contacts}}$(const.n)'; } bundle agent __main__ { methods: "env_classification"; "env_info"; "motd"; }