Uploaded image for project: 'CFEngine Community'
  1. CFEngine Community
  2. CFE-1770

Mustache template repair of existing file not propagated to client bundle

    XMLWordPrintable

    Details

      Description

      If mustache template files promise is in a bundle, and it repairs an existing file, the repair class is not propagated to the client bundle. The output file contents are, however, updated. See also #4611 (the repair status is available in the bundle that renders the template, but not for its client bundle).

      Test

      bundle agent main {
        methods:
            "test";
            "check";
      }
      
      bundle agent test {
        vars:
            "mustache_lines" slist => { "{{value}}" };
            "template_path" string => "/tmp/mustache_calling_agent_test_template";
            "output_path" string => "/tmp/mustache_calling_agent_test_output";
      
        files:
            "$(template_path)"
              create => "true",
              edit_defaults => empty,
              edit_line => insert_lines(@(mustache_lines));
      
            "$(output_path)"
              create => "true",
              edit_defaults => empty,
              edit_line => insert_lines("foo");
      
        methods:
            # This should declare second_repaired but does not
            "template_output"
              usebundle => apply_template("{ \"value\" : 2 }"),
              classes => results( "namespace", "apply_template");
      
        vars:
            "c" slist => classesmatching( "apply_template.*" );
      
        reports:
            "$(c)";
          apply_template_repaired::
            "PASS";
          !apply_template_repaired::
            "FAIL";
      }
      
      bundle agent apply_template(json) {
        files:
            "$(test.output_path)"
              create => "true",
              #edit_defaults => empty,
              edit_template => "$(test.template_path)",
              template_method => "mustache",
              template_data => parsejson($(json)),
              classes => results( "namespace", "file_template" );
      
         vars:
          "c" slist => classesmatching( "file_template.*" );
      
         reports:
           "$(c)";
      }
      
      bundle agent check
      {
        classes:
            "OK" and => { "file_template_repaired",
                          "file_template_kept",
                          "file_template_reached",
      
                          # For enterprise compliance the worst outcome is rolled up
                          # as the promsie outcome, so in Mission Portal I would
                          # expect to see the promise to apply the template as
                          # repaired
      
                          "apply_template_repaired", # We expect the bundle to have a repaired outcome
                          "apply_template_kept",     # Promises can have multiple outcomes, this is not incorrect
                          "apply_template_reached"   # Promises can have multiple outcomes, this is not incorrect
                        };
      
        reports:
            OK::
            "$(this.promise_filename) Pass";
            !OK::
            "$(this.promise_filename) FAIL";
      }
      
      bundle edit_line insert_lines(lines) {
        insert_lines:
            "$(lines)";
      }
      
      body edit_defaults empty {
          empty_file_before_editing => "true";
          edit_backup => "false";
      }
      
      body classes if_repaired(x) {
          promise_repaired => { "$(x)" };
      }
      
      body classes results(scope, class_prefix)
      # @brief Define classes prefixed with `class_prefix` and suffixed with
      # appropriate outcomes: _kept, _repaired, _not_kept, _error, _failed,
      # _denied, _timeout, _reached
      #
      # @param scope The scope in which the class should be defined (`bundle` or `namespace`)
      # @param class_prefix The prefix for the classes defined
      #
      # This body can be applied to any promise and sets global
      # (`namespace`) or local (`bundle`) classes based on its outcome. For
      # instance, with `class_prefix` set to `abc`:
      #
      # * if the promise is to change a file's owner to `nick` and the file
      # was already owned by `nick`, the classes `abc_reached` and
      # `abc_kept` will be set.
      #
      # * if the promise is to change a file's owner to `nick` and the file
      # was owned by `adam` and the change succeeded, the classes
      # `abc_reached` and `abc_repaired` will be set.
      #
      # This body is a simpler, more consistent version of the body
      # `scoped_classes_generic`, which see. The key difference is that
      # fewer classes are defined, and only for outcomes that we can know.
      # For example this body does not define "OK/not OK" outcome classes,
      # since a promise can be both kept and failed at the same time.
      #
      # It's important to understand that promises may do multiple things,
      # so a promise is not simply "OK" or "not OK." The best way to
      # understand what will happen when your specific promises get this
      # body is to test it in all the possible combinations.
      #
      # **Suffix Notes:**
      #
      # * `_reached` indicates the promise was tried. Any outcome will result
      #   in a class with this suffix being defined.
      #
      # * `_kept` indicates some aspect of the promise was kept
      #
      # * `_repaired` indicates some aspect of the promise was repaired
      #
      # * `_not_kept` indicates some aspect of the promise was not kept.
      #   error, failed, denied and timeout outcomes will result in a class
      #   with this suffix being defined
      #
      # * `_error` indicates the promise repair encountered an error
      #
      # * `_failed` indicates the promise failed
      #
      # * `_denied` indicates the promise repair was denied
      #
      # * `_timeout` indicates the promise timed out
      #
      # **Example:**
      #
      # ```cf3
      # bundle agent example
      # {
      #   commands:
      #     "/bin/true"
      #       classes => results("bundle", "my_class_prefix");
      #
      #   reports:
      #     my_class_prefix_kept::
      #       "My promise was kept";
      #
      #     my_class_prefix_repaired::
      #       "My promise was repaired";
      # }
      # ```
      #
      # **See also:** `scope`, `scoped_classes_generic`, `classes_generic`
      {
        scope => "$(scope)";
      
        promise_kept => { "$(class_prefix)_reached",
                          "$(class_prefix)_kept" };
      
        promise_repaired => { "$(class_prefix)_reached",
                              "$(class_prefix)_repaired" };
      
        repair_failed => { "$(class_prefix)_reached",
                           "$(class_prefix)_error",
                           "$(class_prefix)_not_kept",
                           "$(class_prefix)_failed" };
      
        repair_denied => { "$(class_prefix)_reached",
                           "$(class_prefix)_error",
                           "$(class_prefix)_not_kept",
                           "$(class_prefix)_denied" };
      
        repair_timeout => { "$(class_prefix)_reached",
                            "$(class_prefix)_error",
                            "$(class_prefix)_not_kept",
                            "$(class_prefix)_timeout" };
      }
      

      Test run output on 3.12.0

      R: file_template_reached
      R: file_template_repaired
      R: file_template_kept
      R: FAIL
      R: apply_template_reached
      R: apply_template_kept
      R: /home/nickanderson/org/cfengine3-294058cj FAIL
      

      The test fails because promise outcomes are rolled up to the calling method. I expect that the method should have been repaired but we can see that the method itself reported to be kept.

      Original Test

      body common control {
          bundlesequence => { "test" };
      }
      
      bundle agent test {
        vars:
            "mustache_lines" slist => { "{{value}}" };
            "template_path" string => "/tmp/mustache_calling_agent_test_template";
            "output_path" string => "/tmp/mustache_calling_agent_test_output";
      
        files:
            "$(test.template_path)"
              create => "true",
              edit_defaults => empty,
              edit_line => insert_lines(@(mustache_lines));
      
            "$(test.output_path)"
              create => "true",
              edit_defaults => empty,
              edit_line => insert_lines("foo");
      
        methods:
            # This should declare second_repaired but does not
            "template_output"
              usebundle => apply_template("{ \"value\" : 2 }"),
              classes => if_repaired("template_output_repaired");
      
        reports:
          template_output_repaired::
            "PASS";
          !template_output_repaired::
            "FAIL";
      }
      
      bundle agent apply_template(json) {
        files:
            "$(test.output_path)"
              create => "true",
              edit_defaults => empty,
              edit_template => "$(test.template_path)",
              template_method => "mustache",
              template_data => parsejson($(json));
      }
      
      bundle edit_line insert_lines(lines) {
        insert_lines:
            "$(lines)";
      }
      
      body edit_defaults empty {
          empty_file_before_editing => "true";
          edit_backup => "false";
      }
      
      body classes if_repaired(x) {
          promise_repaired => { "$(x)" };
      }
      

      Outcome: Fail

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              vpodzime Vratislav Podzimek
              Reporter:
              naksu Mr Naksu
              Votes:
              1 Vote for this issue
              Watchers:
              5 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: