$(sys.inputdir)/standalone_self_upgrade.cf is an independent policy set entry like promises.cf and update.cf. The policy is executed by an independent agent executed from the update.cf entry when the class trigger_upgrade is defined and the host is not seen to be running the desired version of the agent. The policy is designed for use with Enterprise packages, but can be customized for use with community packages.


file bodies

control

Prototype: control

Implementation:

code
body file control
{
  inputs => { @(standalone_self_upgrade_file_control.inputs) };
}

agent bodies

control

Prototype: control

Description: Agent controls for standalone self upgrade

Implementation:

code
body agent control
{
    control_agent_agentfacility_configured::

        agentfacility => "$(default:update_def.control_agent_agentfacility)";
}

action bodies

u_immediate

Prototype: u_immediate

Description: Ignore promise locks, actuate the promise immediately

Implementation:

code
body action u_immediate
{
      ifelapsed => "0";
}

copy_from bodies

u_dsync

Prototype: u_dsync(from, server)

Description: Synchronize promiser with from on server using digest comparison. If host is a policy hub, then it skips the remote copy, preferring the local file path. For this reason, this body is not compatible with shortcuts defined by cf-serverd.

Arguments:

  • from: File path to copy from on remote server
  • server: Remote server to copy file from if executing host is not a policy server

Implementation:

code
body copy_from u_dsync(from,server)
{
      # NOTE policy servers cheat and copy directly from the local file system.
      # This works even if cf-serverd is down and it makes sense if your serving
      # yourself.

      source      => "$(from)";
      compare     => "digest";
      trustkey    => "false";
      purge       => "true";

    !am_policy_hub::

      servers => { "$(server)" };

    cfengine_internal_encrypt_transfers::

      encrypt => "true";
}

classes bodies

u_if_repaired

Prototype: u_if_repaired(x)

Description: Define x if promise results in a repair

Arguments:

  • x: Name of the class to be defined if promise results in repair

Implementation:

code
body classes u_if_repaired(x)
{
      promise_repaired => { "$(x)" };
}

u_if_else

Prototype: u_if_else(yes, no)

Description: Define yes if promise results in a repair, no if promise is not kept (failed, denied, timeout)

Arguments:

  • yes: class to define if promise results in repair
  • no: class to define if promise is not kept (failed, denied, timeout)

Implementation:

code
body classes u_if_else(yes,no)
{
      #      promise_kept     => { "$(yes)" };
      promise_repaired => { "$(yes)" };
      repair_failed    => { "$(no)" };
      repair_denied    => { "$(no)" };
      repair_timeout   => { "$(no)" };
}

common bodies

control

Prototype: control

Description: Common control for standalone self upgrade

Implementation:

code
body common control
{
      version => "CFEngine Standalone Self Upgrade 3.24.1a.e5d27f397";


    control_common_tls_min_version_defined::
        tls_min_version => "$(default:def.control_common_tls_min_version)"; # See also: allowtlsversion in body server control

    control_common_tls_ciphers_defined::
        tls_ciphers => "$(default:def.control_common_tls_ciphers)"; # See also: allowciphers in body server control

    (debian|ubuntu)::
      package_inventory => { $(package_module_knowledge.platform_default) };

      # We only define pacakge_inventory on redhat like systems that have a
      # python version that works with the package module.

    (redhat|centos)::
      package_inventory => { $(package_module_knowledge.platform_default) };

    (debian|redhat)::
      package_module => $(package_module_knowledge.platform_default);
}

depth_search bodies

u_recurse_basedir

Prototype: u_recurse_basedir(d)

Description: Search recursively from (and including) the referenced directory directory to depth d excluding common version control paths

Arguments:

  • d: maximum depth to descend

Implementation:

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

edit_defaults bodies

u_empty_no_backup

Prototype: u_empty_no_backup

Description: Do not create backups and ensure we are promising the entire content of the file.

Implementation:

code
body edit_defaults u_empty_no_backup
{
      empty_file_before_editing => "true";
      edit_backup => "false";
}

file_select bodies

plain

Prototype: plain

Description: Select plain, regular files

Implementation:

code
body file_select plain
{
      file_types  => { "plain" };
      file_result => "file_types";
}

package_method bodies

u_generic

Prototype: u_generic(repo)

Description: Generic package_method capable of managing packages on multiple platforms.

Arguments:

  • repo: Local directory to look for packages in

Implementation:

code
body package_method u_generic(repo)
{

    debian::

        package_changes => "individual";
        package_list_command => "/usr/bin/dpkg -l";

      # package_list_update_command => "/usr/bin/apt-get update";
        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";

        package_list_name_regex    => "ii\s+([^\s:]+).*";
      # package_list_version_regex => "ii\s+[^\s]+\s+([^\s]+).*";
        package_list_version_regex => "ii\s+[^\s]+\s+(\d+\.\d+((\.|-)\d+)+).*";

        package_installed_regex    => ".*"; # all reported are installed

        package_file_repositories  => { "$(repo)" };
        package_version_equal_command => "/usr/bin/dpkg --compare-versions '$(v1)' eq '$(v2)'";
        package_version_less_command => "/usr/bin/dpkg --compare-versions '$(v1)' lt '$(v2)'";


    debian.x86_64::
        package_name_convention    => "$(name)_$(version)_amd64.deb";

    debian.i686::
        package_name_convention    => "$(name)_$(version)_i386.deb";

    debian::
        package_add_command        => "/usr/bin/dpkg --force-confdef --force-confnew --install";
        package_delete_command     => "/usr/bin/dpkg --purge";

    redhat|SuSE|suse|sles::

        package_changes => "individual";

        package_list_command => "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\"";

        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";

        package_list_name_regex    => "[^|]+\|[^|]+\|\s+([^\s|]+).*";
        package_list_version_regex => "[^|]+\|[^|]+\|[^|]+\|\s+([^\s|]+).*";
        package_list_arch_regex    => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*";

        package_installed_regex    => "i.*";

        package_file_repositories  => { "$(repo)" };

        package_name_convention    => "$(name)-$(version).$(arch).rpm";

        package_add_command        => "/bin/rpm -ivh ";
        package_delete_command     => "/bin/rpm -e --nodeps";
        package_verify_command     => "/bin/rpm -V";
        package_noverify_regex     => ".*[^\s].*";

        package_version_less_command => "$(sys.bindir)/rpmvercmp '$(v1)' lt '$(v2)'";
        package_version_equal_command => "$(sys.bindir)/rpmvercmp '$(v1)' eq '$(v2)'";

    (redhat|SuSE|suse|sles|debian|solarisx86|solaris)::
        package_update_command     => "$(sys.workdir)/bin/cf-upgrade -b $(cfengine_software_version_packages1.backup_script) -s $(cfengine_software_version_packages1.backup_file) -i $(cfengine_software_version_packages1.install_script)";

    redhat.!redhat_4::
        package_list_update_command => "/usr/bin/yum --quiet check-update";
    redhat_4::
        package_list_update_command => "/usr/bin/yum check-update";
    SuSE|suse|sles::
        package_list_update_command => "/usr/bin/zypper list-updates";

    windows::

        package_changes => "individual";
        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
        package_file_repositories  => { "$(repo)" };

        package_installed_regex    => ".*";

        package_name_convention    => "$(name)-$(version)-$(arch).msi";

        package_add_command        => "\"$(sys.winsysdir)\msiexec.exe\" /qn /i";
        package_update_command     => "\"$(sys.winsysdir)\msiexec.exe\" /qn /i";
        package_delete_command     => "\"$(sys.winsysdir)\msiexec.exe\" /qn /x";
        package_version_less_command => '$(sys.winsysdir)$(const.dirsep)WindowsPowerShell$(const.dirsep)v1.0$(const.dirsep)powershell.exe "$(sys.bindir)$(const.dirsep)vercmp.ps1" "$(v1)" "lt" "$(v2)"';
        package_version_equal_command => '$(sys.winsysdir)$(const.dirsep)WindowsPowerShell$(const.dirsep)v1.0$(const.dirsep)powershell.exe "$(sys.bindir)$(const.dirsep)vercmp.ps1" "$(v1)" "eq" "$(v2)"';

    freebsd::

        package_changes => "individual";

        package_list_command => "/usr/sbin/pkg_info";

        package_list_update_command => "/usr/bin/true";
        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";

        package_list_name_regex    => "^(\S+)-(\d+\.?)+";
        package_list_version_regex => "^\S+-((\d+\.?)+\_\d)";

        package_file_repositories  => { "$(repo)" };

        package_installed_regex    => ".*";

        package_name_convention    => "$(name)-$(version).tbz";
        package_delete_convention  => "$(name)-$(version)";

        package_add_command        => "/usr/sbin/pkg_add";
        package_delete_command     => "/usr/sbin/pkg_delete";

    netbsd::

        package_changes => "individual";

        package_list_command => "/usr/sbin/pkg_info";

        package_list_update_command => "/usr/bin/true";
        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";

        package_list_name_regex    => "^(\S+)-(\d+\.?)+";
        package_list_version_regex => "^\S+-((\d+\.?)+\nb\d)";

        package_file_repositories  => { "$(repo)" };

        package_installed_regex    => ".*";

        package_name_convention    => "$(name)-$(version).tgz";
        package_delete_convention  => "$(name)-$(version)";

        package_add_command        => "/usr/sbin/pkg_add";
        package_delete_command     => "/usr/sbin/pkg_delete";

    solarisx86|solaris::

        package_changes => "individual";
        package_list_command => "/usr/bin/pkginfo -l";
        package_list_update_command => "/usr/bin/true";
        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";

        package_multiline_start    => "\s*PKGINST:\s+[^\s]+";
        package_list_name_regex    => "\s*PKGINST:\s+([^\s]+)";
        package_list_version_regex => "\s*VERSION:\s+([^\s]+)";
        package_list_arch_regex    => "\s*ARCH:\s+([^\s]+)";

        package_file_repositories  => { "$(repo)" };

        package_installed_regex    => "\s*STATUS:\s*(completely|partially)\s+installed.*";
        package_name_convention    => "$(name)-$(version)-$(arch).pkg";
        package_delete_convention  => "$(name)";

      # Cfengine appends path to package and package name below, respectively
        package_add_command        => "/bin/sh $(repo)/add_scr $(repo)/admin_file";
        package_delete_command     => "/usr/sbin/pkgrm -n -a $(repo)/admin_file";

    aix::

        package_changes => "individual";

        package_list_update_command => "/usr/bin/true";
        package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";

        package_list_command       => "/usr/bin/lslpp -lc";
        package_list_name_regex    => "[^:]+:([^:]+):[^:]+:.*";
        package_list_version_regex => "[^:]+:[^:]+:([^:]+):.*";

        package_file_repositories  => { "$(repo)" };

        package_installed_regex    => "[^:]+:[^:]+:[^:]+:[^:]*:(COMMITTED|APPLIED):.*";

        package_name_convention    => "$(name)-$(version).bff";
        package_delete_convention  => "$(name)";

      # Redirecting the output to '/dev/null' below makes sure 'geninstall' has
      # its stdout open even if the 'cf-agent' process that started it
      # terminates (e.g. gets killed).
        package_add_command        => "/usr/bin/rm -f $(repo)/.toc && /usr/sbin/geninstall -IacgXNY -d $(repo) cfengine-nova > /dev/null$";
        package_update_command     => "/usr/bin/rm -f $(repo)/.toc && /usr/sbin/geninstall -IacgXNY -d $(repo) cfengine-nova > /dev/null$";
        package_delete_command     => "/usr/sbin/installp -ug cfengine-nova$";

      # Internal version comparison model doesn't work for W.X.Y.Z
        package_version_less_command => "$(sys.bindir)/rpmvercmp '$(v1)' lt '$(v2)'";
        package_version_equal_command => "$(sys.bindir)/rpmvercmp '$(v1)' eq '$(v2)'";
}

package_module bodies

yum

Prototype: yum

Description: Yum package module default settings

Implementation:

code
body package_module yum
{
      query_installed_ifelapsed => "10";
      query_updates_ifelapsed => "30";
@if minimum_version(3.12.2)
      interpreter => "$(sys.bindir)/cfengine-selected-python";
@endif
}

apt_get

Prototype: apt_get

Description: apt_get package module default settings

Implementation:

code
body package_module apt_get
{
      query_installed_ifelapsed => "10";
      query_updates_ifelapsed => "30";
@if minimum_version(3.12.2)
      interpreter => "$(sys.bindir)/cfengine-selected-python";
@endif
}

zypper

Prototype: zypper

Implementation:

code
body package_module zypper
{
        query_installed_ifelapsed => "0";
        query_updates_ifelapsed => "30";
      #default_options =>  {};
@if minimum_version(3.12.2)
        interpreter => "$(sys.bindir)/cfengine-selected-python";
@endif
}

msiexec

Prototype: msiexec

Description: msiexec package module default settings

Implementation:

code
body package_module msiexec
{
      query_installed_ifelapsed => "10";
      query_updates_ifelapsed => "30";
@if minimum_version(3.12.2)
      interpreter => "$(sys.winsysdir)$(const.dirsep)cmd.exe /c ";
@endif
      module_path => "$(sys.workdir)$(const.dirsep)modules$(const.dirsep)packages$(const.dirsep)msiexec.bat";
}

perms bodies

u_m

Prototype: u_m(p)

Description: Ensure mode is p

Arguments:

  • p: permissions

Implementation:

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

copy_from bodies

local_dcp

Prototype: local_dcp(from)

Description: Copy a local file if the hash on the source file differs.

Arguments:

  • from: The path to the source file.

Example:

code
bundle agent example
{
  files:
      "/tmp/file.bak"
      copy_from => local_dcp("/tmp/file");
}

See Also: local_cp(), remote_dcp()

Implementation:

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

common bodies

standalone_self_upgrade_file_control

Prototype: standalone_self_upgrade_file_control

Implementation:

code
bundle common standalone_self_upgrade_file_control
{
  vars:
    "inputs" slist => { "$(this.promise_dirname)$(const.dirsep)cfe_internal$(const.dirsep)update$(const.dirsep)windows_unattended_upgrade.cf" };
}

def_standalone_self_upgrade

Prototype: def_standalone_self_upgrade

Implementation:

code
bundle common def_standalone_self_upgrade
{
  vars:

      "control_agent_agentfacility" -> { "ENT-10209" }
        string => "",
        if => not( isvariable ( "default:def.control_agent_agentfacility" ));

  classes:

      "control_agent_agentfacility_configured" -> { "ENT-10209" }
        expression => regcmp( "LOG_(USER|DAEMON|LOCAL[0-7])",
                              $(control_agent_agentfacility) ),
        comment => concat( "If default:def.control_agent_agentfacility is a",
                           " valid setting, we want to use it in body agent",
                           " control for setting agentfacility" );

      "control_common_tls_min_version_defined" -> { "ENT-10198" }
        expression => isvariable( "default:def.control_common_tls_min_version"),
        comment => concat( "If default:def.control_common_tls_min_version is defined then",
                           " its value will be used for the minimum version in outbound",
                           " connections. Else the binary default will be used.");

      "control_common_tls_ciphers_defined" -> { "ENT-10198" }
        expression => isvariable( "default:def.control_common_tls_ciphers"),
        comment => concat( "If default:def.control_common_tls_ciphers is defined then",
                           " its value will be used for the set of tls ciphers allowed",
                           " for outbound connections. Else the binary default will be used.");
}

agent bundles

main

Prototype: main

Description: This bundle drives the self upgrade. It actuates the appropriate bundles to download binaries to the hub for serving to clients, caching the software to remote clients, and managing the version of cfengine installed on non hubs.

Implementation:

code
bundle agent main
{
  classes:

      "policy_server_dat_unstable"
        expression => isnewerthan( "$(sys.workdir)/policy_server.dat", "$(sys.workdir)/outputs" ),
        comment => "If $(sys.workdir)/policy_server.dat is newer than the
                    outputs directory, it can indicate that the current agent
                    execution is a result of bootstrap. For stability we want to
                    skip upgrades during bootstrap. The outputs directory should
                    be newer than the policy_server.dat on the next agent run
                    and allow upgrade then.";

  reports:
      "Running $(this.promise_filename)";

  methods:

      "cfengine_software";

    (am_policy_hub|policy_server).!mpf_disable_hub_masterfiles_software_update_seed::

      "Master Software Repository Data"
        usebundle => cfengine_master_software_content;

    !(am_policy_hub|policy_server|policy_server_dat_unstable)::

      "Local Software Cache"
        usebundle => cfengine_software_cached_locally;

      "CFEngine Version"
        usebundle => cfengine_software_version;

}

common bodies

package_module_knowledge

Prototype: package_module_knowledge

Description: common package_module_knowledge bundle

This common bundle defines which package modules are the defaults on different platforms.

Implementation:

code
bundle common package_module_knowledge
{
  vars:
    debian|ubuntu::
      "platform_default" string => "apt_get";

    redhat|centos|amazon_linux::
      "platform_default" string => "yum";
}

u_common_knowledge

Prototype: u_common_knowledge

Description: standalone common packages knowledge bundle

This common bundle defines general things about platforms.

Implementation:

code
bundle common u_common_knowledge
{
  vars:
      "list_update_ifelapsed_now" string => "10080";
}

agent bundles

cfengine_software

Prototype: cfengine_software

Description: Variables to control the specifics in desired package selection

Implementation:

code
bundle agent cfengine_software
{
  vars:
    any::

      # Extract the hub binary version info if it's available. Only expected to
      # be available on a client.
      "hub_binary_version" -> { "ENT-10664" }
        data => data_regextract(
                                 "^(?<major_minor_patch>\d+\.\d+\.\d+)-(?<release>\d+)",
                                 readfile("$(sys.statedir)$(const.dirsep)hub_cf_version.txt" ) ),
        if => fileexists( "$(sys.statedir)$(const.dirsep)hub_cf_version.txt" );

      # Default desired CFEngine software
      "pkg_name" string => ifelse( isvariable( "def.cfengine_software_pkg_name" ), $(def.cfengine_software_pkg_name), "cfengine-nova");


      "pkg_version" -> { "ENT-10664" }
        string => "$(sys.cf_version_major).$(sys.cf_version_minor).$(sys.cf_version_patch)",
        if => "am_policy_hub|policy_server",
        comment => "The hub will use its own version to seed client packages.";
      "pkg_version" -> { "ENT-10664" }
        string => "$(hub_binary_version[major_minor_patch])",
        if => isvariable("hub_binary_version[major_minor_patch]"),
        comment => "Use the hub binary version if available.";
      "pkg_version"
        string => "$(def.cfengine_software_pkg_version)",
        if => isvariable( "def.cfengine_software_pkg_version" ),
        comment => "If the target version is explicitly set, we want to use that.";


      "pkg_release" string => "$(sys.cf_version_release)", if => "am_policy_hub|policy_server";
      "pkg_release" string => "$(hub_binary_version[release])", if => isvariable("hub_binary_version[release]");
      "pkg_release" string => "$(def.cfengine_software_pkg_release)", if => isvariable("def.cfengine_software_pkg_release");

      "pkg_arch" string => ifelse( isvariable( "def.cfengine_software_pkg_arch" ), $(def.cfengine_software_pkg_arch), "x86_64");
      "package_dir" string => ifelse( isvariable( "def.cfengine_software_pkg_dir" ), $(def.cfengine_software_pkg_dir), "$(sys.flavour)_$(sys.arch)");
      "pkg_edition_path" string => ifelse( isvariable( "def.cfengine_software_pkg_edition_path" ), $(def.cfengine_software_pkg_edition_path), "enterprise/Enterprise-$(pkg_version)/agent");

    community_edition::
      "pkg_name" string => "cfengine-community";
      "pkg_edition_path" string => "community_binaries/Community-$(pkg_version)";

    aix::
      "pkg_name" string => "cfengine-nova";
      "pkg_arch" string => "default";

    solaris|solarisx86::
      "pkg_name" string => "cfengine-nova";

    amzn_2::

      "package_dir"
        string => "amazon_2_$(pkg_arch)";

    (debian|ubuntu).64_bit::

      "pkg_arch"
        string => "amd64",
        comment => "On debian hosts it's the standard to use 'amd64' instead of
                   'x86_64' in package architectures.";

    (debian|ubuntu).aarch64::

      "pkg_arch"
        string => "arm64",
        comment => concat( "On debian hosts it's the CFEngine standard to use 'arm64' in",
                           "the package filename." );

      "package_dir"
        string => "$(sys.flavor)_arm_64";

    (redhat|centos|suse|sles).32_bit::
      "pkg_arch"
        string => "i386",
        comment => "i686 is the detected architecture, but the package is
                    compatible from i386 up.";

    hpux::

      "package_dir"
        string => "$(sys.class)_$(sys.arch)",
        comment => "The directory within software updates to look for packages.
                    On HPUX sys.flavor includes versions, so we use sys.class
                    instead.";

    windows::

      "package_dir" -> { "ENT-9010" }
        string => "$(sys.class)_$(sys.arch)",
        comment => concat( "The directory within software updates to look for ",
                           "packages. Since one package is built for each",
                           "supported architecture instead of each platform",
                           "version architecture we use sys.class and sys.arch.");

    any::

      "local_software_dir"
        string => translatepath( "$(sys.workdir)/software_updates/$(package_dir)" ),
        comment => "So that we converge on the first pass we set this last as
                    package_dir may vary across platforms.";

  reports:
    DEBUG|DEBUG_cfengine_software::
      "$(this.bundle) pkg_name = $(pkg_name)";
      "$(this.bundle) pkg_version = $(pkg_version)";
      "$(this.bundle) pkg_release = $(pkg_release)";
      "$(this.bundle) pkg_arch = $(pkg_arch)";
      "$(this.bundle) package_dir = $(package_dir)";


  files:
    windows::
      "$(sys.bindir)$(const.dirsep)vercmp.ps1"
        create => "true",
        template_method => "mustache",
        edit_template => "$(this.promise_dirname)$(const.dirsep)/templates/vercmp.ps1",
        template_data => mergedata( '{}' ),
        comment => "We need to use specialized version comparison logic for unattended self upgrades.";

}

cfengine_software_cached_locally

Prototype: cfengine_software_cached_locally

Description: Ensure that the internal local software mirror is up to date

Implementation:

code
bundle agent cfengine_software_cached_locally
{
  reports:
    inform_mode::
      "Ensuring local software cache in $(local_software_dir) is up to date";

  vars:
      "local_software_dir"
        string => "$(cfengine_software.local_software_dir)";

      "package_dir"
        string => "$(cfengine_software.package_dir)";

      "master_software_location" -> { "ENT-4953" }
        string => "master_software_updates",
        comment => "The Cfengine binary updates directory on the policy server",
        handle => "cfe_internal_update_bins_vars_master_software_location";

  files:
      "$(local_software_dir)/."
        create => "true",
        comment => "Ensure the local software directory exists for new binaries
                    to be downloaded to";

      # NOTE This is pegged to the single upstream policy hub, it won't fail
      # over to a secondary for copying the binarys to update.

      "$(local_software_dir)/."
        comment => "Copy binary updates from master source on policy server",
        handle => "cfe_internal_update_bins_files_pkg_copy",
        copy_from => u_dsync( "$(master_software_location)/$(package_dir)", $(sys.policy_hub) ),
        file_select => plain,
        depth_search => u_recurse_basedir(inf),
        action => u_immediate,
        classes => u_if_repaired("bin_newpkg");

}

cfengine_software_version

Prototype: cfengine_software_version

Description: Ensure the version of CFEngine installed is correct for supported platforms. Different platforms leverage different implementations for self upgrading.

Implementation:

code
bundle agent cfengine_software_version
{
  classes:
      "__supported_platform" -> { "ENT-5045", "ENT-5152", "ENT-4094", "ENT-8247" }
        or => {
                "amazon_linux",
                "redhat.!redhat_4",
                "centos.!centos_4",
                "debian",
                "suse|opensuse",
                "ubuntu",
                "hpux",
                "aix",
                "windows", # ENT-4094
        };

      # Add "windows" to __new_implementation classes with ENT-6823
      "__new_implementation"
        or => { "amazon_linux", "redhat", "centos", "ubuntu", "debian", "suse", "opensuse" };

  vars:
      "pkg_name" string => "$(cfengine_software.pkg_name)";
      "pkg_version" string => "$(cfengine_software.pkg_version)";
      "pkg_release" string => "$(cfengine_software.pkg_release)";
      "_cf_version_release" string => ifelse( isvariable( "sys.cf_version_release" ), "$(sys.cf_version_release)", "1" );
      "pkg_arch" string => "$(cfengine_software.pkg_arch)";
      "package_dir" string => "$(cfengine_software.package_dir)";
      "local_software_dir" string => "$(cfengine_software.local_software_dir)";

  methods:
    __supported_platform.__new_implementation::
      "Manage CFEngine Version"
        usebundle => cfengine_software_version_packages2;

    __supported_platform.!__new_implementation::
      "Manage CFEngine Version"
        usebundle => cfengine_software_version_packages1;

      # TODO, remove this and cfe_internal/enterprise/windows_unattended_upgrade.cf
      # when ENT-6823 allows us to use msiexec.bat packages module.
      "Windows Unattended Upgrade Workaround"
        usebundle => windows_unattended_upgrade,
        if => and(
          "windows",
          or(
              not(strcmp("$(cfengine_software.pkg_version)", "$(sys.cf_version)")),
              not(strcmp("$(cfengine_software.pkg_release)", "$(_cf_version_release)"))
            )
        );

  reports:
      !__supported_platform.inform_mode::
        "$(this.bundle) $(package_dir) is not supported";
}

cfengine_software_version_packages2

Prototype: cfengine_software_version_packages2

Description: Ensure the correct version of software is installed using the new packages promise implementation

Implementation:

code
bundle agent cfengine_software_version_packages2
{
  vars:
      "pkg_name" string => "$(cfengine_software.pkg_name)";
      "pkg_version" string => "$(cfengine_software.pkg_version)";
      "pkg_release" string => "$(cfengine_software.pkg_release)";
      "pkg_arch" string => "$(cfengine_software.pkg_arch)";
      "package_dir" string => "$(cfengine_software.package_dir)";
      "local_software_dir" string => "$(cfengine_software.local_software_dir)";

  packages:

    (amazon_linux|redhat|centos)::
      "$(local_software_dir)/$(cfengine_package_names.my_pkg)"
      policy => "present",
      package_module => yum,
      comment => "Ensure the latest package is installed";

    (debian|ubuntu)::
      "$(local_software_dir)/$(cfengine_package_names.my_pkg)"
      policy => "present",
      package_module => apt_get,
      comment => "Ensure the latest package is installed";

    (opensuse|suse)::
      "$(local_software_dir)/$(cfengine_package_names.my_pkg)"
        policy => "present",
        package_module => zypper,
        comment => "Ensure the latest package is installed";

     # TODO, uncomment the following to enable msiexec packages module (ENT-6823)
#    windows::
#      "$(local_software_dir)$(const.dirsep)$(cfengine_package_names.my_pkg)"
#        policy => "present",
#        package_module => msiexec,
#        comment => "Ensure the latest package is installed";
  reports:

    "DEBUG|DEBUG_$(this.bundle)"::
      "Running $(this.bundle)";
}

cfengine_software_version_packages1

Prototype: cfengine_software_version_packages1

Description: Ensure the correct version of software is installed using the legacy self update mechanism

Implementation:

code
bundle agent cfengine_software_version_packages1
{
  classes:

      "cf_upgrade" expression => "(redhat|suse|sles|debian|solaris|solarisx86).!(am_policy_hub|policy_server)";

  vars:

      # NOTE These logs are not actively used or cleaned up by anything. Their
      # use will be phased as platforms migrate to the new packages
      # implementation for self upgrades.

      "local_update_log_dir"
        string => translatepath("$(sys.workdir)/software_updates/update_log"),
        comment => "Local directory to store update log for this host.",
        handle => "cfe_internal_update_bins_vars_local_update_log_dir";

      "local_software_dir" string => "$(cfengine_software.local_software_dir)";

      "desired_version" -> { "ENT-4094" }
        string => ifelse("linux", "$(cfengine_software.pkg_version)-$(cfengine_software.pkg_release)",
                         "windows", "$(cfengine_software.pkg_version).$(cfengine_software.pkg_release)", # ENT-4094
                         "aix", "$(cfengine_software.pkg_version).0",
                         $(cfengine_software.pkg_version) ),
        comment => "The version attribute sometimes contains package release
                    information and sometimes does not. Here we construct the
                    version used in the package promise for the given
                    platform.";

    cf_upgrade::

      # We only use cf-upgrade for some platforms, the need for it has been
      # deprecated by the new packages promise implementation.

      # backup script for cf-upgrade
      # the script should have 2 conditions, BACKUP and RESTORE
      # BACKUP and RESTORE status is $(const.dollar)1 variable in the script
      # see more details at bundle edit_line u_backup_script

      # NOTE cf-upgrade wants to execute from /tmp by default. This is
      # problematic for systems where /tmp is mounted with no-exec.

      "backup_script"   string => "/tmp/cf-upgrade_backup.sh";

      # a single compressed backup file for cf-upgrade
      # this backup_file is passed to backup_script as $(const.dollar)2 variable
      # cf-upgrade will extract this file if return signal of upgrade command is not 0

      "backup_file"     string => "/tmp/cfengine-nova-$(sys.cf_version).tar.gz";

      # install script for cf-upgrade
      # each distribution has its own way to upgrade a package
      # see more details at bundle edit_line u_install_script

      "install_script"  string => "/tmp/cf-upgrade_install.sh";

    (solarisx86|solaris).enterprise::

      # to automatically remove or install packages on Solaris
      # admin_file is a must to have to avoid pop-up interaction
      # see more details at bundle edit_line u_admin_file

      "admin_file"      string => "/tmp/cf-upgrade_admin_file";

  files:

      # Remote enterprise agents (non policy hubs) that have `trigger_upgrade` defined

    cf_upgrade.enterprise.trigger_upgrade::

      "$(backup_script)"
      comment => "Create a backup script for cf-upgrade",
      handle => "cfe_internal_update_bins_files_backup_script",
      create => "true",
      if => "!windows",
      edit_defaults => u_empty_no_backup,
      edit_line => u_backup_script,
      perms => u_m("0755");

      "$(install_script)"
      comment => "Create an install script for cf-upgrade",
      handle => "cfe_internal_update_bins_files_install_script",
      create => "true",
      if => "!windows",
      edit_defaults => u_empty_no_backup,
      edit_line => u_install_script,
      perms => u_m("0755");

      "$(admin_file)"
      comment => "Create solaris admin_file to automate remove and install packages",
      handle => "cfe_internal_update_bins_files_solaris_admin_file",
      create => "true",
      edit_defaults => u_empty_no_backup,
      edit_line => u_admin_file,
      perms => u_m("0644"),
      if => "solarisx86|solaris";

  packages:
    # Only non policy hubs running are allowed to self upgrade
    # We don't upgrade during bootstrap

    !(am_policy_hub|policy_server|bootstrap_mode).enterprise_edition::

      "$(cfengine_software.pkg_name)"
        comment => "Update Nova package to a newer version",
        handle => "cfe_internal_update_bins_packages_nova_update",
        package_policy => "update",
        package_select => "==",
        package_architectures => { "$(cfengine_software.pkg_arch)" },
        package_version => "$(desired_version)",
        package_method => u_generic( $(cfengine_software.local_software_dir) ),
        classes => u_if_else("bin_update_success", "bin_update_fail");

  reports:

    "DEBUG|DEBUG_$(this.bundle)"::
      "Running $(this.bundle)";
}

common bodies

cfengine_package_names

Prototype: cfengine_package_names

Description: Maps platforms to the package naming convention used by the self upgrade policy

Implementation:

code
bundle common cfengine_package_names
{
  vars:
      "pkg_name" string => "$(cfengine_software.pkg_name)";
      "pkg_version" string => "$(cfengine_software.pkg_version)";
      "pkg_release" string => "$(cfengine_software.pkg_release)";
      "pkg_arch" string => "$(cfengine_software.pkg_arch)";

      # Redhat/Centos/Oracle 5, SuSE 11 use the same package

      "pkg[redhat_5_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el5.centos.x86_64.rpm";
      "pkg[centos_5_x86_64]" string => "$(pkg[redhat_5_x86_64])";
      "pkg[oracle_5_x86_64]" string => "$(pkg[redhat_5_x86_64])";
      "pkg[SuSE_11_x86_64]" string => "$(pkg[redhat_5_x86_64])";

      # 32bit RPMs
      "pkg[$(cfengine_master_software_content._rpm_dists)_$(cfengine_master_software_content._32bit_arches)]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el5.centos.i386.rpm";

      # Redhat/Centos/Oracle 6, SuSE 12-15, Opensuse Leap 15 use the same package

      "pkg[redhat_6_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el6.x86_64.rpm";
      "pkg[centos_6_x86_64]" string => "$(pkg[redhat_6_x86_64])";
      "pkg[oracle_6_x86_64]" string => "$(pkg[redhat_6_x86_64])";
      "pkg[SuSE_12_x86_64]" string => "$(pkg[redhat_6_x86_64])";
      "pkg[SuSE_15_x86_64]" string => "$(pkg[redhat_6_x86_64])";
      "pkg[opensuse_leap_15_x86_64]" string => "$(pkg[redhat_6_x86_64])";

      # Redhat/Centos/Oracle/Rocky 7/Amazon 2 use the same package
      "pkg[redhat_7_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el7.x86_64.rpm";
      "pkg[centos_7_x86_64]" string => "$(pkg[redhat_7_x86_64])";
      "pkg[oracle_7_x86_64]" string => "$(pkg[redhat_7_x86_64])";
      "pkg[rocky_7_x86_64]" string => "$(pkg[redhat_7_x86_64])";
      "pkg[amazon_2_x86_64]" string => "$(pkg[redhat_7_x86_64])";

      # Redhat/Centos/Oracle/Rocky 8 use the same package
      "pkg[redhat_8_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el8.x86_64.rpm";
      "pkg[centos_8_x86_64]" string => "$(pkg[redhat_8_x86_64])";
      "pkg[oracle_8_x86_64]" string => "$(pkg[redhat_8_x86_64])";
      "pkg[rocky_8_x86_64]" string => "$(pkg[redhat_8_x86_64])";

      # Redhat/Centos/Oracle/Rocky 8 use the same package
      "pkg[redhat_9_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el9.x86_64.rpm";
      "pkg[centos_9_x86_64]" string => "$(pkg[redhat_9_x86_64])";
      "pkg[oracle_9_x86_64]" string => "$(pkg[redhat_9_x86_64])";
      "pkg[rocky_9_x86_64]" string => "$(pkg[redhat_9_x86_64])";


      # 64bit Debian

      "pkg[debian_7_x86_64]"  string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian7_amd64.deb";
      "pkg[debian_8_x86_64]"  string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian8_amd64.deb";
      "pkg[debian_9_x86_64]"  string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian9_amd64.deb";
      "pkg[debian_10_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian10_amd64.deb";
      "pkg[debian_11_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian11_amd64.deb";

      # 64bit Ubuntu
      "pkg[ubuntu_14_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu14_amd64.deb";
      "pkg[ubuntu_16_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu16_amd64.deb";
      "pkg[ubuntu_18_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu18_amd64.deb";
      "pkg[ubuntu_20_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu20_amd64.deb";
      "pkg[ubuntu_22_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu22_amd64.deb";

      # aarch64 Ubuntu
      "pkg[ubuntu_22_arm_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu22_arm64.deb";

      # aarch64 Debian
      "pkg[debian_11_arm_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian11_arm64.deb";

      # 32bit DEBs
      "pkg[$(cfengine_master_software_content._deb_dists)_$(cfengine_master_software_content._32bit_arches)]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian7_i386.deb";

      # Windows
      "pkg[windows_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release)-x86_64.msi";
      "pkg[windows_i686]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release)-i686.msi";

      "my_pkg"
        string => "$(pkg[$(cfengine_software.package_dir)])",
        comment => "The package name for the currently executing platform.";

  reports:

    "DEBUG|DEBUG_$(this.bundle)"::

      "My Package: $(my_pkg)";
}

agent bundles

cfengine_master_software_content

Prototype: cfengine_master_software_content

Description: When cfengine_master_software_content_state_present is defined the software will try be be automatically downloaded.

Implementation:

code
bundle agent cfengine_master_software_content
{
  vars:
      "pkg_name" string => "$(cfengine_software.pkg_name)";
      "pkg_version" string => "$(cfengine_software.pkg_version)";
      "pkg_release" string => "$(cfengine_software.pkg_release)";
      "pkg_arch" string => "$(cfengine_software.pkg_arch)";
      "package_dir" string => "$(cfengine_software.package_dir)";
      "pkg_edition" string => "$(cfengine_software.pkg_edition_path)";
      "base_url" string => "https://cfengine-package-repos.s3.amazonaws.com/$(pkg_edition)";

      # Map platform/directory identifier to upstream package URLs
      # Better to read in an external explicit data structure?

      "_32bit_arches" slist => { "i386", "i586", "i686" };

      # Redhat/Centos/Oracle 5 and SuSE 11 all use the same package
      "dir[redhat_5_x86_64]" string => "agent_rpm_x86_64";
      "dir[centos_5_x86_64]" string => "$(dir[redhat_5_x86_64])";
      "dir[oracle_5_x86_64]" string => "$(dir[redhat_5_x86_64])";
      "dir[SuSE_11_x86_64]" string => "$(dir[redhat_5_x86_64])";
      "pkg[SuSE_12_x86_64]" string => "$(pkg[redhat_6_x86_64])";
      "pkg[SuSE_15_x86_64]" string => "$(pkg[redhat_6_x86_64])";
      "pkg[opensuse_leap_15_x86_64]" string => "$(pkg[redhat_6_x86_64])";


      # All 32bit rpms use the same package
      "_rpm_dists" slist => { "redhat_5", "redhat_6", "redhat_7",
                              "centos_5", "centos_6", "centos_7",
                              "SuSE_11", "SuSE_10" };

      "dir[$(_rpm_dists)_$(_32bit_arches)]" string => "agent_rpm_i386";

      # Redhat/Centos/Oracle 6 use the same package
      "dir[redhat_6_x86_64]" string => "agent_rhel6_x86_64";
      "dir[centos_6_x86_64]" string => "$(dir[redhat_6_x86_64])";
      "dir[oracle_6_x86_64]" string => "$(dir[redhat_6_x86_64])";

      # Redhat/Centos/Oracle/Rocky 7/Amazon 2 use the same package
      "dir[redhat_7_x86_64]" string => "agent_rhel7_x86_64";
      "dir[centos_7_x86_64]" string => "$(dir[redhat_7_x86_64])";
      "dir[oracle_7_x86_64]" string => "$(dir[redhat_7_x86_64])";
      "dir[rocky_7_x86_64]" string => "$(dir[redhat_7_x86_64])";
      "dir[amazon_2_x86_64]" string => "$(dir[redhat_7_x86_64])";

      # Redhat/Centos/Oracle/Rocky 8 use the same package
      "dir[redhat_8_x86_64]" string => "agent_rhel8_x86_64";
      "dir[centos_8_x86_64]" string => "$(dir[redhat_8_x86_64])";
      "dir[oracle_8_x86_64]" string => "$(dir[redhat_8_x86_64])";
      "dir[rocky_8_x86_64]" string => "$(dir[redhat_8_x86_64])";

      # Redhat/Centos/Oracle/Rocky 9 use the same package
      "dir[redhat_9_x86_64]" string => "agent_rhel9_x86_64";
      "dir[centos_9_x86_64]" string => "$(dir[redhat_9_x86_64])";
      "dir[oracle_9_x86_64]" string => "$(dir[redhat_9_x86_64])";
      "dir[rocky_9_x86_64]" string => "$(dir[redhat_9_x86_64])";

      # Debian
      "dir[debian_7_x86_64]" string => "agent_deb_x86_64";
      "dir[debian_8_x86_64]" string => "agent_debian8_x86_64";
      "dir[debian_9_x86_64]" string => "agent_debian9_x86_64";
      "dir[debian_10_x86_64]" string => "agent_debian10_x86_64";
      "dir[debian_11_x86_64]" string => "agent_debian11_x86_64";
      "dir[debian_11_arm_64]" string => "agent_debian11_arm_64";

      # Ubuntu
      "dir[ubuntu_14_x86_64]" string => "agent_ubuntu14_x86_64";
      "dir[ubuntu_16_x86_64]" string => "agent_ubuntu16_x86_64";
      "dir[ubuntu_18_x86_64]" string => "agent_ubuntu18_x86_64";
      "dir[ubuntu_20_x86_64]" string => "agent_ubuntu20_x86_64";
      "dir[ubuntu_22_x86_64]" string => "agent_ubuntu22_x86_64";
      "dir[ubuntu_22_arm_64]" string => "agent_ubuntu22_arm_64";

      # All 32bit debs use the same package
      "_deb_dists" slist => { "debian_4", "debian_5", "debian_6",
                              "debian_7", "debian_8", "debian_9",
                              "debian_10", "ubuntu_14", "ubuntu_16",
                              "ubuntu_18" };

      "dir[$(_deb_dists)_$(_32bit_arches)]" string => "agent_deb_i386";

      # Windows
      "dir[windows_x86_64]" string => "windows_x86_64";
      "dir[windows_i686]"   string => "windows_i686";

      "platform_dir" slist => getindices( dir );
      "download_dir" string => "$(sys.workdir)/master_software_updates";

  files:
      "$(download_dir)/$(platform_dir)/."
       create => "true",
       comment => "We need a place to download each packge we build";

      "$(download_dir)/$(cfengine_software.pkg_version)-downloaded.txt"
        content => join( "\n", classesmatching( "binary_downloaded_.*" ) ),
        if => isgreaterthan( length(classesmatching( "binary_downloaded_.*" )),
                             0 ),
        comment => concat( "We place a marker of the files downloaded so that",
                           " the hub can skip the self upgrade policy after",
                           " download.");

  commands:
      # Fetch each package that we don't already have
       "/usr/bin/curl"
        args => "-s $(base_url)/$(dir[$(platform_dir)])/$(cfengine_package_names.pkg[$(platform_dir)]) --output /var/cfengine/master_software_updates/$(platform_dir)/$(cfengine_package_names.pkg[$(platform_dir)])",
        if => not( fileexists( "$(download_dir)/$(platform_dir)/$(cfengine_package_names.pkg[$(platform_dir)])" ) ),
        classes => u_if_else("binary_downloaded_$(platform_dir)","binary_not_downloaded");

  reports:
    DEBUG|DEBUG_cfengine_master_software_content::
      "curl -s $(base_url)/$(dir[$(platform_dir)])/$(cfengine_package_names.pkg[$(platform_dir)]) --output $(download_dir)/$(platform_dir)/$(cfengine_package_names.pkg[$(platform_dir)])";
}

edit_line bundles

u_backup_script

Prototype: u_backup_script

Description: Backup script used by cf-upgrade

Implementation:

code
bundle edit_line u_backup_script
{
  insert_lines:

    linux::

      "#!/bin/sh

if [ $(const.dollar)1 = \"BACKUP\" ]; then
 tar cfzS $(const.dollar)2 $(sys.workdir) > /dev/null
fi
if [ $(const.dollar)1 = \"RESTORE\" ]; then
 tar xfz $(const.dollar)2
fi";

    solarisx86|solaris::

      "#!/bin/sh

if [ $(const.dollar)1 = \"BACKUP\" ]; then
 tar cf $(const.dollar)2 $(sys.workdir);  gzip $(const.dollar)2
fi
if [ $(const.dollar)1 = \"RESTORE\" ]; then
 gunzip $(const.dollar)2.gz; tar xf $(const.dollar)2
fi";

}

u_install_script

Prototype: u_install_script

Description: Install script used by cf-upgrade

Implementation:

code
bundle edit_line u_install_script
{
  insert_lines:

    redhat|suse|sles::

      "#!/bin/sh

/bin/rpm -U $(const.dollar)1";

    debian::

      "#!/bin/sh

/usr/bin/dpkg --force-confdef --force-confnew --install $(const.dollar)1 > /dev/null";

    solarisx86|solaris::

      "#!/bin/sh

pkgname=`pkginfo -d $(const.dollar)1 | awk '{print $(const.dollar)2}'`
/usr/sbin/pkgrm -n -a $(cfengine_software_version_packages1.admin_file) $pkgname
/usr/sbin/pkgadd -n -a $(cfengine_software_version_packages1.admin_file) -d $(const.dollar)1 all
$(sys.workdir)/bin/cf-execd || true
exit 0";

}

u_admin_file

Prototype: u_admin_file

Description: Admin file specification to enable unattended installation

Implementation:

code
bundle edit_line u_admin_file
{
  insert_lines:

    sunos_5_8::

      "mail=
instance=unique
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=nocheck
setuid=nocheck
conflict=nocheck
action=nocheck
basedir=default";

    solaris.!sunos_5_8::

      "mail=
instance=overwrite
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=nocheck
setuid=nocheck
conflict=nocheck
action=nocheck
networktimeout=60
networkretries=3
authentication=quit
keystore=/var/sadm/security
proxy=
basedir=default";

}