Change in behavior: depth_search can now be used (but warns) with an individual file as source

Posted by Nick Anderson
June 19, 2024

You may see a new warning in the upcoming releases of 3.21.5 and 3.24.0. A new warning was introduced with a fix to the behavior of depth_search when combined with a copy_from source targeting a file:

depth_search (recursion) is promised for a base object '<filename>' that is not a directory

Prior to versions 3.21.5 and 3.24.0 CFEngine would copy the file initially but would subsequently avoid updating the file providing only debug log message about the fact that the file was being skipped (because it’s not possible to descend into a file). Beginning with 3.21.5 and 3.24.0 CFEngine will copy the file and properly update the file but will also emit a warning that recursion was promised for something that was not a directory.

If you see these warnings consider adjusting your policy so that you do not promise depth_search with a non-directory source.

For example, consider this policy which implements an agent bundle abstracting the copying of a single file or directory tree recursively.

bundle agent copy_remote_file_or_dir(src, dest)
# @brief Make `dest` a copy of `src`.
#
# This bundle copies recursively. If a single file is
# specified only the single file is copied. If a directory
# is specified that directory is copied recursively.
#
#@ **Examples:**
#@ "Copy a single file"
#@    usebundle => copy_remote_file_or_dir(
#@      "/var/cfengine/masterfiles/promises.cf",
#@      "/tmp/dest-test/dest_file.txt");
#@
#@ "Copy a directory tree"
#@   usebundle => copy_remote_file_or_dir(
#@     "/var/cfengine/masterfiles/.",
#@     "/tmp/dest-dir/.");
{
  files:
    "$(dest)"
      copy_from => remote_dcp_missing_ok($(src), $(sys.policy_hub)),
      depth_search => recurse_with_base(inf),
      classes => results("namespace", "$(src)__file_from_policy_hub");
}

body copy_from remote_dcp_missing_ok(source_path,source_server)
# @brief Copy resources from a remote server if the content hash differs from the local copy.
#
# @param `source_path` The location of the file on the remote server
# @param `source_server` The hostname or IP of the server from which to download
#
# **See Also:** `local_dcp()`
{
  servers    => { "$(source_server)" };
  source     => "$(source_path)";
  compare    => "digest";
  missing_ok => "false";
}

Using the above policy, you could trigger the old behavior if the source is actually a directory but not denoted as one with a trailing slash dot (/.).

For example:

methods:
  "Copy ambiguous dir to dir"
    usebundle => copy_remote_file_or_dir(
      "/src/source/file.txt",
      "/tmp/file.txt");

With the old behavior, the contents of the source file (/src/source/file.txt) would be copied locally on the first agent execution, but changes to that file would not be realized and transmitted in the future.

With the new behavior, the agent will copy the file locally on the first agent execution and any changes to the source file will be realized and updates will be transmitted on subsequent agent executions but a warning will be emitted:

warning: depth_search (recursion) is promised for a base object '/tmp/file.txt' that is not a directory

For more information, or if you find this change problematic in some way see CFE-4403.