From b9e48b6731fb566a96fc8fbf1b60dce07d563ded Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Mon, 13 Jul 2026 13:06:14 -0500 Subject: [PATCH] Made evaluation_order in body common control configurable via Augments This exposes evaluation_order in body common control of the main policy (promises.cf) via default:def.control_common_evaluation_order, which sets the default promise evaluation order for all components (cf-agent, cf-serverd, cf-execd, cf-monitord). body agent control now only sets evaluation_order for cf-agent when it is explicitly configured via augments, so cf-agent inherits the evaluation order from body common control unless overridden. This keeps the existing default:def.control_agent_evaluation_order override working while letting body common control act as the base default. Acceptance tests exercise both the common control default (inherited by cf-agent) and the cf-agent specific override against the built promises.cf. Ticket: ENT-13495 Changelog: Title --- MPF.md | 30 ++++++- controls/cf_agent.cf | 12 ++- controls/def.cf | 20 ++--- promises.cf.in | 9 ++ .../control_agent_evaluation_order.cf | 85 +++++++++++++++++++ .../control_agent_evaluation_order.cf.json | 7 ++ .../control_common_evaluation_order.cf | 85 +++++++++++++++++++ .../control_common_evaluation_order.cf.json | 7 ++ 8 files changed, 237 insertions(+), 18 deletions(-) create mode 100644 tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf create mode 100644 tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf.json create mode 100644 tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf create mode 100644 tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf.json diff --git a/MPF.md b/MPF.md index 5023359254..6f56d8b58b 100644 --- a/MPF.md +++ b/MPF.md @@ -708,16 +708,39 @@ To override the default for **cf-agent** configure `default:update_def.control_a The following settings are defined in `controls/def.cf` can be set from an [augments file][Augments]. +### Configure the default promise evaluation order for all components + +By default CFEngine processes promises in "Normal Order". Configure `default:def.control_common_evaluation_order` to control the default promise evaluation order for all components (`cf-agent`, `cf-serverd`, `cf-execd` and `cf-monitord`). This variable is used to set `evaluation_order` in `body common control` in `promises.cf`. Override it for a specific component (for example `cf-agent`, see below) or for a specific file (with `evaluation_order` in `body file control`). + +Example Augments: + +```json +{ + "variables": { + "default:def.control_common_evaluation_order": { + "value": "top_down", + "comment": "Our policy writers find it easier to understand policy that is evaluated in the order it is written." + } + } +} +``` + +**History:** + +- Introduced in 3.28.0. + +**See also:** [Policy evaluation ordering](reference/language-concepts/policy-evaluation/), [Configure the evaluation order of cf-agent for main policy](#Configure the evaluation order of cf-agent for main policy), [`evaluation_order` in `body common control`][cf-agent#evaluation_order], [Policy style guide on promise ordering][Policy style guide#Promise ordering] + ### Configure the evaluation order of cf-agent for main policy -By default CFEngine processes promises in "Normal Order". Configure `default:def.control_agent_evaluation_order` to control the default promise evaluation order for `cf-agent`. This variable is used to set `evaluation_order` in `body agent control` in `promises.cf` +By default `cf-agent` inherits the promise evaluation order from `body common control` (see [Configure the default promise evaluation order for all components](#Configure the default promise evaluation order for all components)). Configure `default:def.control_agent_evaluation_order` to control the promise evaluation order for `cf-agent` specifically, overriding `body common control`. This variable is used to set `evaluation_order` in `body agent control` in `promises.cf` and is only applied when set to a valid value (`classic` or `top_down`) via augments. Example Augments: ```json { "variables": { - "default:def.control_agent_evalution_order": { + "default:def.control_agent_evaluation_order": { "value": "top_down", "comment": "Our policy writers find it easier to understand policy that is evaluated in the order it is written." } @@ -728,8 +751,9 @@ Example Augments: **History:** - Introduced in 3.27.0. +- Since 3.28.0, `cf-agent` inherits the evaluation order from `body common control` when `default:def.control_agent_evaluation_order` is not set. -**See also:** [Policy evaluation ordering](reference/language-concepts/policy-evaluation/), [Configure cf-agent promise evaluation order for update policy](#Configure the evaluation order for cf-agent evaluated promises for update policy), [Policy style guide on promise ordering][Policy style guide#Promise ordering] +**See also:** [Policy evaluation ordering](reference/language-concepts/policy-evaluation/), [Configure the default promise evaluation order for all components](#Configure the default promise evaluation order for all components), [Configure cf-agent promise evaluation order for update policy](#Configure the evaluation order for cf-agent evaluated promises for update policy), [Policy style guide on promise ordering][Policy style guide#Promise ordering] ### Automatically migrate ignore_interfaces.rx to workdir diff --git a/controls/cf_agent.cf b/controls/cf_agent.cf index 4c51cbaa21..47840bc1f6 100644 --- a/controls/cf_agent.cf +++ b/controls/cf_agent.cf @@ -11,10 +11,6 @@ body agent control # Global default for time that must elapse before promise will be rechecked. # Don't keep any promises. any:: -@if minimum_version(3.27.0) - # The order in which promises are evaluated (top_down|classic) - evaluation_order => "$(default:def.control_agent_evaluation_order)"; -@endif # Minimum time (in minutes) which should have passed since the last time # the promise was verified before it is checked again. ifelapsed => "1"; @@ -58,4 +54,12 @@ body agent control # Environment variables based on Distro control_agent_agentfacility_configured:: agentfacility => "$(default:def.control_agent_agentfacility)"; +@if minimum_version(3.27.0) + # Only override cf-agent's promise evaluation order when explicitly + # configured via augments; otherwise cf-agent inherits the order set in + # body common control. See ENT-13495. + control_agent_evaluation_order_configured:: + # The order in which promises are evaluated (top_down|classic) + evaluation_order => "$(default:def.control_agent_evaluation_order)"; +@endif } diff --git a/controls/def.cf b/controls/def.cf index f51fd407e3..5d9234ac35 100644 --- a/controls/def.cf +++ b/controls/def.cf @@ -275,17 +275,6 @@ bundle common def if => and("enterprise_edition", "am_policy_hub"); # Agent Controls - "control_agent_evaluation_order" -> { "ENT-13495" } - string => ifelse( - isvariable($(this.promiser)), - "$(default:def.control_agent_evaluation_order)", - "classic" - ), - comment => concat( - 'We default to the classic "Normal Order"', - ' for cf-agent if not otherwise specified' - ); - "control_agent_update_evaluation_order" -> { "ENT-13495" } string => ifelse( isvariable($(this.promiser)), @@ -451,6 +440,15 @@ bundle common def " control for setting agentfacility" ); + "control_agent_evaluation_order_configured" -> { "ENT-13495" } + expression => isvariable("default:def.control_agent_evaluation_order"), + comment => concat( + "cf-agent overrides body common control's promise evaluation order", + " only when default:def.control_agent_evaluation_order is set via", + " augments. Otherwise cf-agent inherits the evaluation order from body", + " common control" + ); + "_control_agent_environment_vars_validated" -> { "CFE-3927" } and => { # The variable must be defined diff --git a/promises.cf.in b/promises.cf.in index d7273aa928..c35edcb5c4 100644 --- a/promises.cf.in +++ b/promises.cf.in @@ -133,6 +133,15 @@ body common control # The number of minutes after which last-seen entries are purged from cf_lastseen.lmdb lastseenexpireafter => "$(def.control_common_lastseenexpireafter)"; +@if minimum_version(3.27.0) + # The order in which promises are evaluated (top_down|classic). Sets the + # default evaluation order for all components. Override it for a specific + # component (e.g. cf-agent in body agent control) or file (body file + # control). Defaults to the classic "Normal Order" unless + # default:def.control_common_evaluation_order is set via augments. + evaluation_order => "$(default:def.control_common_evaluation_order)"; +@endif + control_common_tls_min_version_defined:: tls_min_version => "$(default:def.control_common_tls_min_version)"; # See also: allowtlsversion in body server control diff --git a/tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf b/tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf new file mode 100644 index 0000000000..3243cbfd6f --- /dev/null +++ b/tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf @@ -0,0 +1,85 @@ +# Tests that default:def.control_agent_evaluation_order set via augments +# changes cf-agent's promise evaluation order via body agent control +# (ENT-13495). +# +# Only the agent control augment is set. The eo_discriminator bundle (appended +# to the bundlesequence via control_common_bundlesequence_end) prints EARLY +# before LATE in classic (Normal) order, and LATE before EARLY in top_down +# order, so the order of the reports reveals the evaluation order in effect. +body common control +{ + inputs => { "../../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; +} + +####################################################### +bundle agent test +{ + vars: + "files_to_copy" + slist => { + "cfe_internal", + "controls", + "inventory", + "lib", + "promises.cf", + "services", + }; + + files: + "$(sys.inputdir)/." create => "true"; + + "$(sys.inputdir)/$(files_to_copy)" + copy_from => dcs_sync( + "$(this.promise_dirname)/../../../../$(files_to_copy)" + ), + perms => m(600), + depth_search => recurse("inf"); + + methods: + "" + usebundle => file_make( + "$(sys.inputdir)/secondary.cf", + ' +bundle agent eo_discriminator +{ + reports: + eo_marker:: + "ENT13495_EO_EARLY"; + + classes: + "eo_marker" expression => "any"; + + reports: + any:: + "ENT13495_EO_LATE"; +} +' + ); + + "" + usebundle => file_copy( + "$(this.promise_filename).json", "$(sys.inputdir)/def.json" + ); +} + +####################################################### +bundle agent check +{ + vars: + # Only keep the discriminator's reports so the order check is not defeated + # by the (large) rest of the policy output. + "command" + string => "$(sys.cf_agent) -Kf $(sys.inputdir)/promises.cf 2>/dev/null | grep ENT13495_EO"; + + methods: + # In top_down order LATE is reported before EARLY. + "" + usebundle => dcs_passif_output( + "(?s).*ENT13495_EO_LATE.*ENT13495_EO_EARLY.*", + "", + $(command), + $(this.promise_filename) + ); +} diff --git a/tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf.json b/tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf.json new file mode 100644 index 0000000000..98ca300f5e --- /dev/null +++ b/tests/acceptance/00_basics/def.json/control_agent_evaluation_order.cf.json @@ -0,0 +1,7 @@ +{ + "inputs": ["$(sys.inputdir)/secondary.cf"], + "vars": { "control_common_bundlesequence_end": ["eo_discriminator"] }, + "variables": { + "default:def.control_agent_evaluation_order": { "value": "top_down" } + } +} diff --git a/tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf b/tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf new file mode 100644 index 0000000000..649c7fd65b --- /dev/null +++ b/tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf @@ -0,0 +1,85 @@ +# Tests that default:def.control_common_evaluation_order set via augments +# changes the promise evaluation order for all components (ENT-13495). +# +# Here only the common control augment is set (not the agent one), so cf-agent +# must inherit "top_down" from body common control. The eo_discriminator bundle +# (appended to the bundlesequence via control_common_bundlesequence_end) prints +# EARLY before LATE in classic (Normal) order, and LATE before EARLY in top_down +# order, so the order of the reports reveals the evaluation order in effect. +body common control +{ + inputs => { "../../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; +} + +####################################################### +bundle agent test +{ + vars: + "files_to_copy" + slist => { + "cfe_internal", + "controls", + "inventory", + "lib", + "promises.cf", + "services", + }; + + files: + "$(sys.inputdir)/." create => "true"; + + "$(sys.inputdir)/$(files_to_copy)" + copy_from => dcs_sync( + "$(this.promise_dirname)/../../../../$(files_to_copy)" + ), + perms => m(600), + depth_search => recurse("inf"); + + methods: + "" + usebundle => file_make( + "$(sys.inputdir)/secondary.cf", + ' +bundle agent eo_discriminator +{ + reports: + eo_marker:: + "ENT13495_EO_EARLY"; + + classes: + "eo_marker" expression => "any"; + + reports: + any:: + "ENT13495_EO_LATE"; +} +' + ); + + "" + usebundle => file_copy( + "$(this.promise_filename).json", "$(sys.inputdir)/def.json" + ); +} + +####################################################### +bundle agent check +{ + vars: + # Only keep the discriminator's reports so the order check is not defeated + # by the (large) rest of the policy output. + "command" + string => "$(sys.cf_agent) -Kf $(sys.inputdir)/promises.cf 2>/dev/null | grep ENT13495_EO"; + + methods: + # In top_down order LATE is reported before EARLY. + "" + usebundle => dcs_passif_output( + "(?s).*ENT13495_EO_LATE.*ENT13495_EO_EARLY.*", + "", + $(command), + $(this.promise_filename) + ); +} diff --git a/tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf.json b/tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf.json new file mode 100644 index 0000000000..65903a06b2 --- /dev/null +++ b/tests/acceptance/00_basics/def.json/control_common_evaluation_order.cf.json @@ -0,0 +1,7 @@ +{ + "inputs": ["$(sys.inputdir)/secondary.cf"], + "vars": { "control_common_bundlesequence_end": ["eo_discriminator"] }, + "variables": { + "default:def.control_common_evaluation_order": { "value": "top_down" } + } +}