cfe_internal/update/update_policy.cf
agent bundles
cfe_internal_update_policy
Prototype: cfe_internal_update_policy
Description: This bundle is responsible for activating the policy to update inputs.
Implementation:
bundle agent cfe_internal_update_policy
{
  classes:
      # Define classes if we see a user is requesting a custom policy update bundle
      "have_user_specified_update_bundle"
        expression => isvariable( "def.mpf_update_policy_bundle" );
      # Define classes if we are able to find the specific bundle they requested
      # (otherwise we may get an error about undefined bundle)
      "have_found_user_specified_update_bundle"
        expression  => some(".*", "found_matching_user_specified_bundle");
      "missing_user_specified_update_bundle"
       not  => some(".*", "found_matching_user_specified_bundle");
  vars:
      "default_policy_update_bundle" string => "cfe_internal_update_policy_cpv";
      # Look for a bundle that matches what the user wants
      "found_matching_user_specified_bundle"
        slist => bundlesmatching( "$(def.mpf_update_policy_bundle)" );
  methods:
    # Use the user specified bundle when it's found
    have_found_user_specified_update_bundle::
        "User specified policy update bundle"
          usebundle => $(found_matching_user_specified_bundle);
    # Fall back to stock policy update bundle if we have not found one
    # specified by user
    !have_found_user_specified_update_bundle::
      "Stock policy update"
        usebundle => cfe_internal_update_policy_cpv;
  reports:
    inform_mode|verbose_mode|DEBUG|DEBUG_cfe_internal_update_policy::
      # Report a human readable way to understand the policy behavior
      "Found user specified update bundle."
        if => "have_user_specified_update_bundle";
      "User specified update bundle: $(def.mpf_update_policy_bundle)"
        if => "have_user_specified_update_bundle";
      "User specified update bundle MISSING! Falling back to $(default_policy_update_bundle)."
        if => and( "have_user_specified_update_bundle",
                   "missing_user_specified_update_bundle"
                  );
}
cfe_internal_setup_python_symlink
Prototype: cfe_internal_setup_python_symlink(symlink_path)
Description: Create the /var/cfengine/bin/python symlink pointing to some installed python (if any)
Arguments:
- symlink_pathof- varspromiser path of- varspromiser path_folders of- varspromiser abs_path_folders of- varspromiser abs_path_folders of- varspromiser exact_version_globs of- varspromiser generic_python_globs of- varspromiser python_exact[$(exact_version_globs)] of- varspromiser python_generic[$(generic_python_globs)] of- varspromiser python_platform_fallback[/usr/libexec/platform-python] of- varspromiser python_exact_sorted of- varspromiser pythons of- varspromiser python, used as promiser of type- files, used as promiser of type- files
Implementation:
bundle agent cfe_internal_setup_python_symlink(symlink_path)
{
  vars:
      "path" string => getenv("PATH", 1024);
      "path_folders" slist => filter("$(sys.bindir)",
                                     splitstring("$(path)", ":", 128),
                                     false, true, 128);
    windows::
      "abs_path_folders" -> {"CFE-2309"}
        slist => filter("([A-Z]|//):.*", path_folders, "true", "false", 128),
        comment => "findfiles() complains about relative directories";
    !windows::
      "abs_path_folders" -> {"CFE-2309"}
        slist => filter("/.*", path_folders, "true", "false", 128),
        comment => "findfiles() complains about relative directories";
    any::
      "exact_version_globs" slist => maplist("$(this)/python[23]", @(abs_path_folders)),
        comment => "Looking for Python 2 and/or Python 3 in the $PATH folders";
      "generic_python_globs" slist => maplist("$(this)/python", @(abs_path_folders)),
        comment => "Looking for the 'python' symlink/executable which can be any
                    version of Python (usually Python 2 for backwards compatibility)";
      "python_exact[$(exact_version_globs)]" slist => findfiles("$(exact_version_globs)");
      "python_generic[$(generic_python_globs)]" slist => findfiles("$(generic_python_globs)");
      "python_platform_fallback[/usr/libexec/platform-python]" -> { "CFE-3291" }
        slist => { "/usr/libexec/platform-python" };
      "python_exact_sorted" slist => reverse(sort(getvalues(@(python_exact)), "lex")),
        comment => "Prefer higher major versions of Python";
      "pythons" slist => getvalues(mergedata(@(python_exact_sorted),
                                             getvalues(@(python_generic)),
                                             getvalues(@(python_platform_fallback)))),
        comment => "Prefer exact versions over unknown";
      "python" string => nth(@(pythons), 0),
        if => isgreaterthan(length(@(pythons)), 0),
        comment => "Taking the first item from the list (sorted by preference)";
  files:
      "$(symlink_path)"
        delete => u_tidy,
        if => not(isvariable("python"));
      "$(symlink_path)"
        link_from => u_ln_s("$(python)"),
        move_obstructions => "true",
        if => isvariable("python");
}
cfe_internal_update_policy_cpv
Prototype: cfe_internal_update_policy_cpv
Description: Update inputs from masterfiles when cf_promises_validated changes
Implementation:
bundle agent cfe_internal_update_policy_cpv
{
  vars:
      "inputs_dir"         string => translatepath("$(sys.inputdir)"),
      comment => "Directory containing CFEngine policies",
      handle => "cfe_internal_update_policy_vars_inputs_dir";
      "master_location" -> { "ENT-3692" }
        string => "$(update_def.mpf_update_policy_master_location)",
        comment => "The path to request updates from the policy server.",
        handle => "cfe_internal_update_policy_vars_master_location";
    windows::
      "modules_dir_source"        string => "/var/cfengine/masterfiles/modules",
      comment => "Directory containing CFEngine modules",
      handle => "cfe_internal_update_policy_vars_modules_dir_windows";
      "plugins_dir_source"        string => "/var/cfengine/plugins",
      comment => "Directory containing CFEngine plugins",
      handle => "cfe_internal_update_policy_vars_plugins_dir_windows";
    !windows::
      "modules_dir_source"        string => translatepath("$(master_location)/modules"),
      comment => "Directory containing CFEngine modules",
      handle => "cfe_internal_update_policy_vars_modules_dir";
      "plugins_dir_source"        string => translatepath("$(sys.workdir)/plugins"),
      comment => "Directory containing CFEngine plugins",
      handle => "cfe_internal_update_policy_vars_plugins_dir";
    any::
      "file_check"         string => translatepath("$(inputs_dir)/promises.cf"),
      comment => "Path to a policy file",
      handle => "cfe_internal_update_vars_file_check";
      "ppkeys_file"        string => translatepath("$(sys.workdir)/ppkeys/localhost.pub"),
      comment => "Path to public key file",
      handle => "cfe_internal_update_policy_vars_ppkeys_file";
      "postgresdb_dir"        string => "$(sys.workdir)/state/pg/data",
      comment => "Directory where Postgres database files will be stored on hub -",
      handle => "cfe_internal_update_policy_postgresdb_dir";
      "postgresdb_log"        string => "/var/log/postgresql.log",
      comment => "File where Postgres database files will be logging -",
        handle => "cfe_internal_update_policy_postgresdb_log_file";
      "python_symlink"        string => "$(sys.bindir)/python",
        comment => "Symlink to Python we found (if any)",
        handle => "cfe_internal_update_policy_python_symlink";
    cfredis_in_enterprise::
      # TODO Remove from MPF after 3.12 EOL
      "redis_conf_file" -> { "ENT-2797" }
        string => translatepath("$(sys.workdir)/config/redis.conf"),
        comment => "Path to Redis configuration file",
        handle => "cfe_internal_update_policy_redis_conf_file";
  classes:
      "validated_updates_ready"
        expression => "cfengine_internal_disable_cf_promises_validated",
        comment => "If cf_promises_validated is disabled, then updates are
                    always considered validated.";
    any::
      "local_files_ok" expression => fileexists("$(file_check)"),
      comment => "Check for $(sys.masterdir)/promises.cf",
      handle => "cfe_internal_update_classes_files_ok";
      # create a global files_ok class
      "cfe_internal_trigger" expression => "local_files_ok",
      classes => u_if_else("files_ok", "files_ok");
  files:
    !am_policy_hub::  # policy hub should not alter inputs/ uneccessary
      "$(inputs_dir)/cf_promises_validated"
      comment => "Check whether a validation stamp is available for a new policy update to reduce the distributed load",
      handle => "cfe_internal_update_policy_check_valid_update",
      copy_from => u_rcp("$(master_location)/cf_promises_validated", @(update_def.policy_servers)),
      action => u_immediate,
      classes => u_if_repaired("validated_updates_ready");
    any::
      "$(sys.workdir)$(const.dirsep)modules"
      comment => "Always update modules files on client side",
      handle => "cfe_internal_update_policy_files_update_modules",
      copy_from => u_rcp("$(modules_dir_source)", @(update_def.policy_servers)),
      depth_search => u_recurse("inf"),
      perms => u_m("755"),
      action => u_immediate;
    !am_policy_hub::
      "$(sys.workdir)$(const.dirsep)plugins"
      comment => "Always update plugins files on client side",
      handle => "cfe_internal_update_policy_files_update_plugins",
      copy_from => u_rcp("$(plugins_dir_source)", @(update_def.policy_servers)),
      depth_search => u_recurse("inf"),
      perms => u_m("755"),
      action => u_immediate;
    am_policy_hub|validated_updates_ready::  # policy hub should always put masterfiles in inputs in order to check new policy
      "$(inputs_dir)"
      comment => "Copy policy updates from master source on policy server if a new validation was acquired",
      handle => "cfe_internal_update_policy_files_inputs_dir",
      copy_from => u_rcp("$(master_location)", @(update_def.policy_servers)),
      depth_search => u_recurse("inf"),
      file_select  => u_input_files,
      action => u_immediate,
      classes => u_results("bundle", "update_inputs");
    update_inputs_not_kept::
      "$(inputs_dir)/cf_promises_validated" -> { "CFE-2587" }
        delete => u_tidy,
        comment => "If there is any problem copying to $(inputs_dir) then purge
                    the cf_promises_validated file must be purged so that
                    subsequent agent runs will perform a full scan.";
    !policy_server.enable_cfengine_enterprise_hub_ha::
      "$(sys.workdir)/policy_server.dat"
      comment => "Copy policy_server.dat file from server",
      handle => "cfe_internal_update_ha_policy_server",
      copy_from => u_rcp("$(sys.workdir)/state/master_hub.dat", @(update_def.policy_servers)),
      action => u_immediate,
      classes => u_if_repaired("replica_failover");  # not needed ?
    am_policy_hub::
      "$(master_location)/."
      comment => "Make sure masterfiles folder has right file permissions",
      handle => "cfe_internal_update_policy_files_sys_workdir_masterfiles",
      perms => u_m($(update_def.masterfiles_perms_mode)),
      depth_search => u_recurse_basedir("inf"),
      action => u_immediate;
  methods:
    debian|redhat|amazon_linux|suse|sles|opensuse::
      # Only needed on distros with Python-based package modules
      "setup_python_symlink" usebundle => cfe_internal_setup_python_symlink("$(python_symlink)");
}
perms bodies
u_m
Prototype: u_m(p)
Description: Ensure file mode is p
Arguments:
- p: Desired file mode
Implementation:
body perms u_m(p)
{
      mode  => "$(p)";
}
u_mo
Prototype: u_mo(p, o)
Description: Ensure file mode is p and owner is o
Arguments:
- p: Desired file owner (username or uid)
- o
Implementation:
body perms u_mo(p,o)
{
      mode   => "$(p)";
      owners => {"$(o)"};
}
u_shared_lib_perms
Prototype: u_shared_lib_perms
Description: Shared library permissions
Implementation:
body perms u_shared_lib_perms
{
    !hpux::
      mode => "0644";
    hpux::
      mode => "0755"; # Mantis 1114, Redmine 1179
}
file_select bodies
u_cf3_files
Prototype: u_cf3_files
Description: Select files starting with cf- (cfengine binaries)
Implementation:
body file_select u_cf3_files
{
      leaf_name => { "cf-.*" };
      file_result => "leaf_name";
}
u_input_files
Prototype: u_input_files
Description: Select files by extension that we should include when updating inputs
Implementation:
body file_select u_input_files
{
      leaf_name => { @(update_def.input_name_patterns) };
      file_result => "leaf_name";
}
copy_from bodies
u_rcp
Prototype: u_rcp(from, server)
Description: Ensure file is a copy of from on server using digest comparison
Arguments:
- from: The path to copy from
- server: The remote host to copy from
Implementation:
body copy_from u_rcp(from,server)
{
      source      => "$(from)";
      compare     => "digest";
      trustkey    => "false";
      # CFE-2932 For testing, we want to be able to avoid this local copy optimiztion
    !am_policy_hub|mpf_skip_local_copy_optimizaton::
      servers => { "$(server)" };
    !am_policy_hub.(sys_policy_hub_port_exists|mpf_skip_local_copy_optimization)::
      portnumber => "$(sys.policy_hub_port)";
    cfengine_internal_encrypt_transfers::
      encrypt => "true";
    cfengine_internal_purge_policies::
      purge => "true";
    cfengine_internal_preserve_permissions::
      preserve => "true";
    cfengine_internal_verify_update_transfers::
      verify      => "true";
}
u_cp
Prototype: u_cp(from)
Description: Ensure file is a copy from from on the local server based on digest comparison
Arguments:
- from: string, used in the value of attribute- source
Implementation:
body copy_from u_cp(from)
{
      source  => "$(from)";
      compare => "digest";
}
u_cp_nobck
Prototype: u_cp_nobck(from)
Description: copy from from locally with digest comparison and making no backups
Arguments:
- from: string, used in the value of attribute- source
Implementation:
body copy_from u_cp_nobck(from)
{
      source      => "$(from)";
      compare     => "digest";
      copy_backup => "false";
}
action bodies
u_immediate
Prototype: u_immediate
Description: Actuate the promise immediately, ignoring locks
Implementation:
body action u_immediate
{
      ifelapsed => "0";
}
depth_search bodies
u_recurse
Prototype: u_recurse(d)
Description: Search recursively for files up to d levels excluding common version control data
Arguments:
- d: Maximum depth to search recursively
Implementation:
body depth_search u_recurse(d)
{
      depth => "$(d)";
      exclude_dirs => { "\.svn", "\.git", "git-core" };
}
u_recurse_basedir
Prototype: u_recurse_basedir(d)
Description: Search recursively for files up to d levels excluding common version control data and including the base directory
Arguments:
- d: Maximum depth to search recursively
Implementation:
body depth_search u_recurse_basedir(d)
{
      include_basedir => "true";
      depth => "$(d)";
      exclude_dirs => { "\.svn", "\.git", "git-core" };
}
classes bodies
u_if_repaired
Prototype: u_if_repaired(x)
Description: Define x if the promise is repaired
Arguments:
- x: Class to define if promise repaired
Implementation:
body classes u_if_repaired(x)
{
      promise_repaired => { "$(x)" };
}
u_if_repaired_then_cancel
Prototype: u_if_repaired_then_cancel(y)
Description: Cancel class x if the promise is repaired
Arguments:
- y
Implementation:
body classes u_if_repaired_then_cancel(y)
{
      cancel_repaired => { "$(y)" };
}
u_if_else
Prototype: u_if_else(yes, no)
Description: define yes if the promise is repaired, and no if the promise fails to repair (notkept)
Arguments:
- yes: Class to define if promise repaired
- no: Class to undefine if promise notkept
Implementation:
body classes u_if_else(yes,no)
{
#      promise_kept     => { "$(yes)" };
      promise_repaired => { "$(yes)" };
      repair_failed    => { "$(no)" };
      repair_denied    => { "$(no)" };
      repair_timeout   => { "$(no)" };
}
contain bodies
u_in_shell
Prototype: u_in_shell
Description: Run command within shell environment
Implementation:
body contain u_in_shell
{
      useshell => "true";
}
u_in_shell_and_silent
Prototype: u_in_shell_and_silent
Description: Run command within shell environment suppressing output
Implementation:
body contain u_in_shell_and_silent
{
      useshell => "true";
      no_output => "true";
}
u_postgres
Prototype: u_postgres
Description: Run command within postgres users shell environment
Implementation:
body contain u_postgres
{
  useshell   => "useshell";
  exec_owner => "cfpostgres";
  exec_group => "cfpostgres";
  chdir      => "/tmp";
  no_output  => "true";
}
action bodies
u_ifwin_bg
Prototype: u_ifwin_bg
Description: Run command in the background if windows is defined
Implementation:
body action u_ifwin_bg
{
    windows::
      background => "true";
}
service_method bodies
u_bootstart
Prototype: u_bootstart
Description: Attributes for u_bootstart service method
Implementation:
body service_method u_bootstart
{
      service_autostart_policy => "boot_time";
}
contain bodies
u_in_dir
Prototype: u_in_dir(s)
Description: Run command from within s
Arguments:
- s: Path to change into before running command
Implementation:
body contain u_in_dir(s)
{
      chdir => "$(s)";
}
u_silent_in_dir
Prototype: u_silent_in_dir(s)
Description: Run command from within s and suppress output
Arguments:
- s: Path to change into before running command
Implementation:
body contain u_silent_in_dir(s)
{
      chdir => "$(s)";
      no_output => "true";
}
link_from bodies
u_ln_s
Prototype: u_ln_s(x)
Description: Symlink to x, even if it does not exist
Arguments:
- x: Path to symlink
Implementation:
body link_from u_ln_s(x)
{
      link_type => "symlink";
      source => "$(x)";
      when_no_source => "force";
}
delete bodies
u_tidy
Prototype: u_tidy
Description: Delete directories and symlinks
Implementation:
body delete u_tidy
{
      dirlinks => "delete";
      rmdirs   => "true";
}