Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save nickanderson/4d56f261c68e664c05402b99ab0e98b9 to your computer and use it in GitHub Desktop.
Save nickanderson/4d56f261c68e664c05402b99ab0e98b9 to your computer and use it in GitHub Desktop.
Investigate bug in templating files where move_obstruction is in use but not respected

The original example from Bas:

bundle agent main
{
    methods:
        "" usebundle => create_link();
        "" usebundle => create_template();
}

bundle agent create_link
{
    files:
      "/tmp/link_file"
        move_obstructions => "true",
        link_from => ln_s( "/etc/resolv.conf" );
        #move_obstructions => "true",
}

bundle agent create_template
{
    vars:
        "mustache" string => "this is a test";

    files:
      "/tmp/link_file"
        move_obstructions => "true",
        edit_template_string   => "$(mustache)",
        template_method => "inline_mustache";
}

body link_from ln_s(x)
# @brief Create a symbolink link to `x`
# The link is created even if the source of the link does not exist.
# @param x The source of the link
{
      link_type => "symlink";
      source => "$(x)";
      when_no_source => "force";
}
    info: Linked files '/tmp/link_file' -> '/etc/resolv.conf'
   error: Cannot follow symlink '/tmp/link_file'; it is not owned by root or the user running this process, and the target owner and/or group differs from that of the symlink itself.
   error: Unable to open destination file '/etc/../run/systemd/resolve/stub-resolv.conf.cf-after-edit' for writing. (fopen: Permission denied)
   error: Failed to update rendering of '/tmp/link_file' from mustache template 'inline'
   error: Errors encountered when actuating files promise '/tmp/link_file'
   error: Method 'create_template' failed in some repairs

Checking against the behavior of the content attribute:

bundle agent main
{
    methods:
        "" usebundle => create_link();
        "" usebundle => create_template();
        "" usebundle => content();
}

bundle agent create_link
{
    files:
      "/tmp/link_target"
        content => "Link Target Content";

      "/tmp/link_file"
        move_obstructions => "true",
        link_from => ln_s( "/tmp/link_target" );
        #move_obstructions => "true",
}

bundle agent create_template
{
    vars:
        "mustache" string => "this is a test";

    files:
      "/tmp/link_file"
        move_obstructions => "true",
        edit_template_string   => "$(mustache)",
        template_method => "inline_mustache";
}

bundle agent content
{
  files:
      "/tmp/link_file"
        move_obstructions => "true",
        content   => "Rendered via content attribute targeting symlink";
}

body link_from ln_s(x)
# @brief Create a symbolink link to `x`
# The link is created even if the source of the link does not exist.
# @param x The source of the link
{
      link_type => "symlink";
      source => "$(x)";
      when_no_source => "force";
}
    info: Created file '/tmp/link_target', mode 0600
    info: Updated file '/tmp/link_target' with content 'Link Target Content'
    info: Linked files '/tmp/link_file' -> '/tmp/link_target'
    info: Updated rendering of '/tmp/link_file' from mustache template 'inline'
    info: Updated file '/tmp/link_file' with content 'Rendered via content attribute targeting symlink'

Checking against the behavior of the copy_from attribute: f

bundle agent main
{
  methods:
      "" usebundle => create_link();
      "" usebundle => create_template();
      "" usebundle => content();
      "" usebundle => copy();
}

bundle agent create_link
{
  files:
      "/tmp/link_target"
        content => "Link Target Content";

      "/tmp/link_file"
        move_obstructions => "true",
        link_from => ln_s( "/tmp/link_target" );
      #move_obstructions => "true",
}

bundle agent create_template
{
  vars:
      "mustache" string => "this is a test";

  files:
      "/tmp/link_file"
        move_obstructions => "true",
        edit_template_string   => "$(mustache)",
        template_method => "inline_mustache";
}

bundle agent content
{
  files:
      "/tmp/link_file"
        move_obstructions => "true",
        content   => "Rendered via content attribute targeting symlink";
}

bundle agent copy
{
  files:
      "/tmp/link_file"
        move_obstructions => "true",
        copy_from => local_dcp( $(this.promise_filename) );
}

body copy_from local_dcp(from)
# @brief Copy a local file if the hash on the source file differs.
# @param from The path to the source file.
#
# **Example:**
#
# ```cf3
# bundle agent example
# {
#   files:
#       "/tmp/file.bak"
#       copy_from => local_dcp("/tmp/file");
# }
# ```
#
# **See Also:** `local_cp()`, `remote_dcp()`
{
        source      => "$(from)";
        compare     => "digest";
}
body link_from ln_s(x)
# @brief Create a symbolink link to `x`
# The link is created even if the source of the link does not exist.
# @param x The source of the link
{
        link_type => "symlink";
        source => "$(x)";
        when_no_source => "force";
}
    info: Updated file '/tmp/link_target' with content 'Link Target Content'
    info: Updated rendering of '/tmp/link_file' from mustache template 'inline'
    info: Updated file '/tmp/link_file' with content 'Rendered via content attribute targeting symlink'
    info: Removing old symbolic link '/tmp/link_file' to make way for copy
    info: Copied file '/home/nickanderson/org/roam/daily/work/cfengine3-k1evev' to '/tmp/link_file.cfnew' (mode '600')
    info: Moved '/tmp/link_file.cfnew' to '/tmp/link_file'
    info: Updated file '/tmp/link_file' from 'localhost:/home/nickanderson/org/roam/daily/work/cfengine3-k1evev'

The content attribute appears to function in the same way as when template_method inline_mustache is used. That is to say that it operates on the final destination and does not convert the file into a plain file. However, copy_from does replace the symlink with a plain file.

Re-reading the documentation on move_obstructions (https://docs.cfengine.com/docs/3.24/reference-promise-types-files.html#move_obstructions), it does seem to me like both template_method inline_mustache ( probably all cases of template_method are affected ) and the content attribute have bugs in this regard and do not respect move_obstructions as they should.

As far as working around it, for now I think you will have to use additional promises and check to see if the file islink() or not before actuating the promise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment