copy_from bodies

u_cmdb_data

Prototype: u_cmdb_data

Description: Sync CMDB data from policy server Note: Not all hosts necessarily have CMDB data

Implementation:

code
body copy_from u_cmdb_data
{
        copy_backup => "false";
        trustkey => "false";
        compare => "digest";
        source  => "hub_cmdb";
        servers => { "$(sys.policy_hub)" };
        purge => "true";
@if minimum_version(3.12)
        missing_ok => "true";
@endif
}

perms bodies

u_m

Prototype: u_m(p)

Description: Ensure file mode is p

Arguments:

  • p: Desired file mode

Implementation:

code
body perms u_m(p)
{
      mode  => "$(p)";

#+begin_ENT-951
# Remove after 3.20 is not supported
        rxdirs => "true";
@if minimum_version(3.20)
        rxdirs => "false";
@endif
#+end
}

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:

code
body perms u_mo(p,o)
{
      mode   => "$(p)";

#+begin_ENT-951
# Remove after 3.20 is not supported
        rxdirs => "true";
@if minimum_version(3.20)
        rxdirs => "false";
@endif
#+end

      !(windows|termux)::
        owners => {"$(o)"};
}

u_shared_lib_perms

Prototype: u_shared_lib_perms

Description: Shared library permissions

Implementation:

code
body perms u_shared_lib_perms
{
#+begin_ENT-951
# Remove after 3.20 is not supported
        rxdirs => "true";
@if minimum_version(3.20)
        rxdirs => "false";
@endif
#+end

    !hpux::
      mode => "0644";
    hpux::
      mode => "0755"; # Mantis 1114, Redmine 1179
}

file_select bodies

u_all

Prototype: u_all

Description: Select all file system entries

Implementation:

code
body file_select u_all
{
        leaf_name => { ".*" };
        file_result => "leaf_name";
}

u_cf3_files

Prototype: u_cf3_files

Description: Select files starting with cf- (cfengine binaries)

Implementation:

code
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:

code
body file_select u_input_files
{
      leaf_name => { @(update_def.input_name_patterns),
                     @(update_def.input_name_patterns_extra) };
      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:

code
body copy_from u_rcp(from,server)
{
      source      => "$(from)";
      compare     => "digest";
      trustkey    => "false";
      purge => "true"; # CFE-3662

      # 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_disabled::
      purge => "false";

    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:

code
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:

code
body copy_from u_cp_nobck(from)
{
      source      => "$(from)";
      compare     => "digest";
      copy_backup => "false";
}

u_cp_missing_ok

Prototype: u_cp_missing_ok(from)

Description: same as u_cp but allow from to be missing

Arguments:

Implementation:

code
body copy_from u_cp_missing_ok(from)
{
      inherit_from => u_cp($(from));
      missing_ok => "true";
}

u_remote_dcp_missing_ok

Prototype: u_remote_dcp_missing_ok(from, server)

Description: Download a file from a remote server if available and if differs from the local copy.

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

See Also: remote_dcp()

Implementation:

code
body copy_from u_remote_dcp_missing_ok(from,server)
{
        servers     => { "$(server)" };
        source      => "$(from)";
        compare     => "digest";
        missing_ok => "true";
}

action bodies

u_immediate

Prototype: u_immediate

Description: Actuate the promise immediately, ignoring locks

Implementation:

code
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:

code
body depth_search u_recurse(d)
{
      depth => "$(d)";
      exclude_dirs => { "\.svn", "\.git", "git-core" };
}

u_infinite_client_policy

Prototype: u_infinite_client_policy

Description: Search recursively for files excluding vcs related files and .no-distrib directories

Implementation:

code
body depth_search u_infinite_client_policy
{
        depth => "inf";
        exclude_dirs => { "\.svn", "\.git", "git-core", "\.no-distrib" };
}

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:

code
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:

code
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:

code
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:

code
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:

code
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:

code
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:

code
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:

code
body action u_ifwin_bg
{
    windows::
      background => "true";
}

service_method bodies

u_bootstart

Prototype: u_bootstart

Description: Attributes for u_bootstart service method

Implementation:

code
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:

code
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:

code
body contain u_silent_in_dir(s)
{
      chdir => "$(s)";
      no_output => "true";
}

u_ln_s

Prototype: u_ln_s(x)

Description: Symlink to x, even if it does not exist

Arguments:

  • x: Path to symlink

Implementation:

code
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:

code
body delete u_tidy
{
      dirlinks => "delete";
      rmdirs   => "true";
}

file_select bodies

not_vendored_modules

Prototype: not_vendored_modules(pathname)

Arguments:

  • pathname

Implementation:

code
body file_select not_vendored_modules(pathname)
{
  path_name => { "$(pathname)" };
  file_result => "path_name";
}

u_dirs

Prototype: u_dirs

Description: Select directories

Implementation:

code
body file_select u_dirs
{
        file_types  => { "dir" };
        file_result => "file_types";
}

u_not_dir

Prototype: u_not_dir

Description: Select all files that are not directories

Implementation:

code
body file_select u_not_dir
{
        file_types => { "dir" };
        file_result => "!file_types";
}

agent bundles

cfe_internal_update_policy

Prototype: cfe_internal_update_policy

Description: This bundle is responsible for activating the policy to update inputs.

Implementation:

code
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;

    any::
      "CMDB data update" -> { "ENT-6788", "ENT-8847" }
        usebundle => cfe_internal_update_cmdb,
        action => u_immediate;

  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"
                  );


}

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_path of vars promiser path of vars promiser path_folders of vars promiser abs_path_folders of vars promiser abs_path_folders of vars promiser exact_version_globs of vars promiser generic_python_globs of vars promiser python_exact[$(exact_version_globs)] of vars promiser python_generic[$(generic_python_globs)] of vars promiser python_platform_fallback[/usr/libexec/platform-python] of vars promiser python_exact_sorted of vars promiser pythons of vars promiser python, used as promiser of type files , used as promiser of type files of files promiser $(sys.bindir)/python

Implementation:

code
bundle agent cfe_internal_setup_python_symlink(symlink_path)
{
  vars:
      "path" string => getenv("PATH", 1024);
      "path_folders" slist => splitstring("$(path)", ":", 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");

      "$(sys.bindir)/python" -> { "CFE-3512", "CFE-4146" }
        delete => u_tidy,
        if => and( islink( "$(sys.bindir)/python" ),
                   strcmp( "$(sys.bindir)", "/var/cfengine/bin")),
        comment => concat( "We don't want to leave a python that is potentially in $PATH ",
                           "after having re-named our python symlink that is used for various ",
                           "modules. Additionally we want to be cautious that we don't delete ",
                           "system python symlinks in the event the binary was built for FHS.");
}

cfe_internal_update_policy_cpv

Prototype: cfe_internal_update_policy_cpv

Description: Update inputs from masterfiles when cf_promises_validated changes

Implementation:

code
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";

    !windows::

      "modules_dir_source"        string => translatepath("$(master_location)/modules"),
      comment => "Directory containing CFEngine modules",
      handle => "cfe_internal_update_policy_vars_modules_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" -> { "CFE-2602", "CFE-3512" }
        string => "$(sys.bindir)/cfengine-selected-python",
        comment => "Symlink to Python we found (if any)",
        handle => "cfe_internal_update_policy_python_symlink";

  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");

    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_infinite_client_policy,
      file_select  => u_input_files,
      action => u_immediate,
      classes => u_results("bundle", "update_inputs"),
      move_obstructions => "true";

      # Note that here we do not filter with `update_def.input_name_patterns` so
      # that we copy any and all modules scripts.
      "$(inputs_dir)/modules"
      comment => "Copy any files in modules from master source on policy server if a new validation was acquired",
      handle => "cfe_internal_update_policy_files_modules_dir",
      copy_from => u_rcp("$(modules_dir_source)", @(update_def.policy_servers)),
      depth_search => u_recurse("inf"),
      action => u_immediate;

    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)/." -> { "CFE-951" }
        comment => "Make sure masterfiles folder has right file permissions",
        handle => "cfe_internal_update_policy_files_sys_workdir_masterfiles_dirs",
        perms => u_m($(update_def.masterfiles_perms_mode_dirs)),
        file_select => u_dirs,
        depth_search => u_recurse_basedir("inf"),
        action => u_immediate;

      "$(master_location)/." -> { "CFE-951" }
        comment => "Make sure masterfiles folder has right file permissions",
        handle => "cfe_internal_update_policy_files_sys_workdir_masterfiles_not_dir",
        perms => u_m($(update_def.masterfiles_perms_mode_not_dir)),
        file_select => u_not_dir,
        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" -> { "CFE-2602" }
        usebundle => cfe_internal_setup_python_symlink("$(python_symlink)");

    any::
      # Install vendored and user provided modules to $(sys.workdir) from $(sys.inputdir)
      "modules_presence";
}

cfe_internal_update_cmdb

Prototype: cfe_internal_update_cmdb

Description: Ensure local cache of CMDB data is up to date

Implementation:

code
bundle agent cfe_internal_update_cmdb
{
  classes:
      "have_cf_reactor" expression => fileexists("$(sys.bindir)/cf-reactor");

  methods:

    policy_server.enterprise_edition.(!have_cf_reactor|cmdb_data_files_updates_done_in_policy)::
      "cfe_internal_update_cmdb_data_distribution";

@if feature(host_specific_data_load)
      # Only hosts with this feature, introduced in 3.18.0 can use the data.

      # Don't pull CMDB data on policy_hub self bootstrap because
      # there will be no cf-serverd listening to serve files yet.
    enterprise_edition.!(bootstrap_mode)::  # ENT-6840
      "cfe_internal_update_cmdb_data_consumption" -> { "ENT-8847" }
        action => u_immediate;
@endif
}

cfe_internal_update_cmdb_data_distribution

Prototype: cfe_internal_update_cmdb_data_distribution

Description: Ensure data is ready for agents to download

Implementation:

code
bundle agent cfe_internal_update_cmdb_data_distribution
{
  classes:

      "_have_cmdb_next_request_state_file" -> { "ENT-9933" }
        expression => fileexists( "$(_cmdb_next_request_state_file)" );

  vars:
    !bootstrap_mode.(policy_server.enterprise_edition)::

      # The API response for host specific data from cmdb tells us the timestamp of the last data change
      # We store this timestamp and use it for the next request.
      "_cmdb_next_request_state_file"
        string => "$(sys.statedir)/cmdb_next_request_from.dat";

    !bootstrap_mode.(policy_server.enterprise_edition._have_cmdb_next_request_state_file)::
      # If we have the timestamp from a previous response we use it, else we start from 0
      "_cmdb_previous_next_request_from"
        string => readfile( $(_cmdb_next_request_state_file), inf ),
        if => regline( "^\d+$", $(_cmdb_next_request_state_file) );

      "_cmdb_previous_next_request_from"
        string => "0",
        unless => regline( "^\d+$", $(_cmdb_next_request_state_file) );

    !bootstrap_mode.(policy_server.enterprise_edition)::
      # We need a script to call that should return the API response
      "_get_cmdb_data_bin" string => "$(sys.workdir)/httpd/htdocs/scripts/get_cmdb.php";
      "_get_cmdb_data_cmd" string => "/var/cfengine/httpd/php/bin/php $(_get_cmdb_data_bin) $(_cmdb_previous_next_request_from)";

      # We call the script and we pass it the timestamp from the prior call
      "_get_cmdb_data_response"
        string => execresult( $(_get_cmdb_data_cmd), useshell ),
        if => fileexists( $(_get_cmdb_data_bin) );

      "_get_cmdb_data_response_d"
        data => parsejson('$(_get_cmdb_data_response)'),
        if => validjson( '$(_get_cmdb_data_response)' );

      # So that we can write a JSON file for each host we get the indicies of data in the response
      "_i" slist => getindices( "_get_cmdb_data_response_d[data]");

      # We need to store the timestamp from the most recent change so that we can use that as a starting point for future requests.
      "_next_request_from"
        string => "$(_get_cmdb_data_response_d[meta][cmdb_epoch])";

  files:
      # "$(_get_cmdb_data_cmd)" perms => m( 700 );

@if minimum_version(3.18)
    !bootstrap_mode.(policy_server.enterprise_edition)::
      # This functionality is only present on 3.18.0+ Enterprise hubs, and this
      # promise uses the /content/ attribute which was first introduced in
      # 3.16.0.

      # If the next request state file doesn't exist, we seed one with 0, the
      # lowest epoch value possible. because we populate variables from this
      # file content.
      "$(_cmdb_next_request_state_file)"
        content => "0$(const.n)",
        handle => "cmdb_data_change_next_seed",
        if => and( not(fileexists("$(_cmdb_next_request_state_file)" )),
                   isvariable( "_cmdb_next_request_state_file" ));
@endif

      # Write out the data for each host that had a data change
      "$(sys.workdir)/cmdb/$(_i)/host_specific.json"
        create => "true", # CFE-2329, ENT-4792
        template_data => mergedata("_get_cmdb_data_response_d[data][$(_i)]" ), # mergedata() is necessary in order to pick out a substructure, parsejson() is insufficient because expanding a key results in iteration of /values/ under that key
        template_method => "inline_mustache",
        edit_template_string => string_mustache( "", "_get_cmdb_data_response_d[data][$(_i)]" ),
        if => isgreaterthan( $(_next_request_from), $(_cmdb_previous_next_request_from) );

@if minimum_version(3.18)
      # This functionality is only present on 3.18.0+ Enterprise hubs, and this
      # promise uses the /content/ attribute which was first introduced in
      # 3.16.0.

      # Write out the last data change timestamp so we can use it as a startring point
      "$(_cmdb_next_request_state_file)"
        handle => "cmdb_data_change_next_update",
        content => "$(_next_request_from)$(const.n)",
        unless => strcmp( $(_next_request_from), $(_cmdb_previous_next_request_from) );
@endif

  reports:
    DEBUG|DEBUG_cfe_internal_update_cmdb_data_distribution::
      "'$(_get_cmdb_data_cmd)' response indicates '$(sys.workdir)/cmdb/$(_i)/host_specific.json' needs refreshed"
        if => and( isvariable( "_i" ),
                   isgreaterthan( $(_next_request_from), $(_cmdb_previous_next_request_from) ));

}

cfe_internal_update_cmdb_data_consumption

Prototype: cfe_internal_update_cmdb_data_consumption

Description: Ensure data to load is up to date

Implementation:

code
bundle agent cfe_internal_update_cmdb_data_consumption
{
  files:
      "$(sys.workdir)/data/."
        create => "true",
        comment => "If a host is to load data from the CMDB, it needs to have a directory where said data is cached.";

      "$(sys.workdir)/data/." -> { "ENT-6788", "ENT-8847" }
        depth_search => u_recurse( inf ),
        file_select => u_all,
        copy_from => u_cmdb_data,
        comment => "So that hosts have access to the most recent CMDB data, we make sure that it's up to date.",
        action => u_immediate;

}

modules_presence

Prototype: modules_presence

Description: Render vendored and user provided modules from $(sys.inputdir) to $(sys.workdir)

Implementation:

code
bundle agent modules_presence
{
  vars:
    "_vendored_dir" string => "$(this.promise_dirname)/../../modules/packages/vendored/";
    "_override_dir" string => "$(this.promise_dirname)/../../modules/packages/";
    "_custom_template_dir" string => "$(this.promise_dirname)/../../modules/mustache/";
    "_vendored_paths" slist => findfiles("$(_vendored_dir)*.mustache");
    "_custom_template_paths" slist => findfiles("$(_custom_template_dir)*.mustache"), if => isdir( "$(_custom_template_dir)" );
    "_package_paths" slist => filter("$(_override_dir)vendored", _package_paths_tmp, "false", "true", 999);
    windows::
      "_package_paths_tmp" slist => findfiles("\Q$(_override_dir)\E*");
      "_vendored_modules" slist => maplist(regex_replace("$(this)", "\Q$(_vendored_dir)\E(.*).mustache", "$1", "g"), @(_vendored_paths));
      "_override_modules" slist => maplist(regex_replace("$(this)", "\Q$(_override_dir)\E(.*)", "$1", "g"), @(_package_paths));
      # replace single backslashes in a windows path with double-backslashes
      # to avoid problems with things like `C:\Program Files` and `\promises`
      # causing PCRE to try and interpret special escape sequences.
      "_not_vendored_modules_pathname_regex" string => regex_replace("$(sys.inputdir)$(const.dirsep)modules$(const.dirsep)(?!packages$(const.dirsep)vendored).*","\\\\","\\\\\\\\","g");
    !windows::
      "_package_paths_tmp" slist => findfiles("$(_override_dir)*");
      "_vendored_modules" slist => maplist(regex_replace("$(this)", "$(_vendored_dir)(.*).mustache", "$1", "g"), @(_vendored_paths));
      "_override_modules" slist => maplist(regex_replace("$(this)", "$(_override_dir)(.*)", "$1", "g"), @(_package_paths));
      "_custom_template_modules" slist => maplist(regex_replace("$(this)", "$(_custom_template_dir)(.*).mustache", "$1", "g"), @(_custom_template_paths));
      "_not_vendored_modules_pathname_regex" string => "$(sys.inputdir)/modules/(?!(packages/vendored|mustache/)).*";

  classes:
    "override_vendored_module_$(_vendored_modules)" expression => fileexists("$(_override_dir)$(_vendored_modules)");
    "override_module_$(_override_modules)" expression => fileexists("$(_override_dir)$(_override_modules)");

# NOTE: here we are using the .mustache extension only to
# ensure that the modules scripts are copied as part of
# update (see controls/update_def.cf input_name_patterns var.

  files:
    "$(sys.workdir)/modules/packages/$(_vendored_modules)"
      create => "true",
      perms => u_mo("755", "root"),
      unless => canonify("override_vendored_module_$(_vendored_modules)"),
      edit_template => "$(_vendored_dir)$(_vendored_modules).mustache",
      template_method => "mustache";

    "$(sys.workdir)/modules/packages/$(_override_modules)"
      copy_from => u_cp_missing_ok("$(_override_dir)$(_override_modules)"),
      perms => u_mo("755", "root"),
      if => or (
        canonify("override_vendored_module_$(_override_modules)"),
        canonify("override_module_$(_override_modules)"));

    "$(sys.workdir)/modules/$(_custom_template_modules)" -> { "ENT-10793" }
        comment => "We want to render mustache templated modules",
        handle => "cfe_internal_update_policy_files_custom_template_modules",
        template_method => "mustache",
        edit_template => "$(_custom_template_dir)$(_custom_template_modules).mustache",
        perms => u_mo("500", "root"),
        if => fileexists("$(_custom_template_dir)$(_custom_template_modules).mustache");

    "$(sys.workdir)/modules"
      comment => "Copy any non-packages modules",
      handle => "cfe_internal_update_policy_files_nonpackages_modules",
      copy_from => u_cp("$(sys.inputdir)$(const.dirsep)modules"),
      if => fileexists("$(sys.inputdir)$(const.dirsep)modules"),
      depth_search => u_recurse("inf"),
      perms => u_mo("755", "root"),
      action => u_immediate,
      file_select => not_vendored_modules("$(_not_vendored_modules_pathname_regex)");


  reports:
    DEBUG::
      "_not_vendored_modules_pathname_regex: $(_not_vendored_modules_pathname_regex)";
      "_vendored_modules: $(_vendored_modules)";
      "_override_modules: $(_override_modules)";
      "_vendored_dir: $(_vendored_dir)";
      "_vendored_paths: $(_vendored_paths)";
      "_override_dir: $(_override_dir)";
      "_package_paths: $(_package_paths)";
      "override_vendored_module_$(_vendored_modules)"
        if => "override_vendored_module_$(_vendored_modules)";
      "override_module_$(_override_modules)"
        if => "override_module_$(_override_modules)";
      "canonified: $(with)" with => canonify("override_vendored_module_$(_vendored_modules)");
}