Fwd: Language suggestion: Commands promises outcome improvement

11 views
Skip to first unread message

Nick Anderson

unread,
Apr 19, 2017, 11:13:50 AM4/19/17
to dev-cfengine
This should be seen on the dev list.

On Saturday, May 14, 2016 at 3:46:30 PM UTC-5, Ted Zlatanov wrote:
On Fri, 13 May 2016 21:29:36 -0700 (PDT) mike.weilgart@verticalsysadmin.com wrote:

mw> Something has been bothering me about commands promises for a long while
mw> and I've finally put my finger on it.
...
mw> I think *the most intuitive way* to handle commands promise outcomes based
mw> on the above would be:

mw> - When a command is run successfully, by default it should mean the promise
mw> was repaired.  *(Current CFEngine behavior)*
mw> - When a command is run and fails (non-zero exit status), by default it
mw> should mean the promise was not kept.  *(Current CFEngine behavior)*
mw> - When a command *does not need to be run*, it is because the desired state
mw> is already present, *and the promise should be considered kept*.  This is
mw> NOT the current behavior.

(much shorter version originally posted on IRC responding to user "dsx")

My feeling is that the class system is too rough, it groups everything
as repaired/kept/not kept.

For a large promise like "detect changes in /tmp" or "run three
commands" it makes sense to capture the outcome in a structure where you
can look up the aggregate result, but also the result for each
individual promise. This would easily scale up to bundles as well.

It would also be good if the outcomes were variables and not classes,
and then the classes can be derived from the outcomes.

Let's take a simple example:

    bundle agent main
    {
      vars:
          "todo" slist => { "big", "small" };
   
      reports:
          "$(todo)";
    }


`cf-promises -p json` gives us the parse tree:

    {
      "bodies": [],
      "bundles": [
        {
          "arguments": [],
          "bundleType": "agent",
          "line": 1,
          "name": "main",
          "namespace": "default",
          "promiseTypes": [
            {
              "contexts": [
                {
                  "name": "any",
                  "promises": [
                    {
                      "attributes": [
                        {
                          "line": 4,
                          "lval": "slist",
                          "rval": {
                            "type": "list",
                            "value": [
                              {
                                "type": "string",
                                "value": "big"
                              },
                              {
                                "type": "string",
                                "value": "small"
                              }
                            ]
                          }
                        }
                      ],
                      "line": 4,
                      "promiser": "todo"
                    }
                  ]
                }
              ],
              "line": 3,
              "name": "vars"
            },
            {
              "contexts": [
                {
                  "name": "any",
                  "promises": [
                    {
                      "attributes": [],
                      "line": 7,
                      "promiser": "$(todo)"
                    }
                  ]
                }
              ],
              "line": 6,
              "name": "reports"
            }
          ],
          "sourcePath": "/home/tzz/sync/cf/test/test_simple.cf"
        }
      ]
    }

The outcome variable for that `reports` promise towards the end could
look like this, right inside the parse tree if they were in JSON:

    "promises": [
      {
        "attributes": [],
        "line": 7,
        "promiser": "$(todo)",
        "outcomes": [ { repaired: true, kept: true, failed: false,
                        state: "repaired",
                        iteration_frame: { variable: "todo", value: "big" },
                        ... }, ... more outcomes ... ]
      }

...and it could be accessible with a function call like
`collect_outcomes($(this.bundle))` to get all outcomes in a bundle;
`collect_outcomes($(this.bundle), "reports")` to get just the reports
outcomes; or some similarly special syntax.

In my example above, the outcomes can be linked to the specific
iteration frame that generated them. That's really useful for debugging,
for instance, so it has utility far beyond just class outcomes.

The class outcome would then simply be *deterministically* calculated
from the outcome variable. In other words, you will be able to look at
the outcomes of any given promises or bundle and predict the class
outcome. But it also lets you look at specific details of the execution
and capture, for instance, the return code of a commands promise or
exactly how a bundle execution went.

This would help CFEngine Enterprise because the reporting outcomes could
be stored and transmitted together with the class outcomes in a single
data structure that the server can then present and report consistently.

Ted

Reply all
Reply to author
Forward
0 new messages