Forum: CFEngine Help
Subject: Trying to understand promise status codes
Author: zzamboni
Link to topic: https://cfengine.com/forum/read.php?3,23494,23494#msg-23494

Hi,

I'm trying to understand under which conditions are the different promise 
status codes set. This is a long and convoluted post meant more for the 
CFEngine developers and advanced users, please bear with me if you are so 
inclined, otherwise just skip it :)

First things first, AFAICT, these are all the possible status that a promise 
can have after evaluation. Am I missing any?

- Promise kept: The state of the system was already as described by the 
promise, so no action had to be taken.
- Promise repaired: The state of the system was not as required by the promise, 
so CFEngine took the appropriate actions, and repaired the system state to 
match the requirements of the promise.
- Repair failed: Repair actions were attempted by CFEngine, but they failed for 
some reason (for example, lack of permissions to edit a file)
- Repair denied: Repair actions were attempted by CFEngine, but they failed due 
to lack of access to some resource
- Repair timeout: Repair actions were attempted by CFEngine but took too long 
to execute, and CFEngine cancelled the operation.

(all examples are tested using 3.2.0 community)

Now, consider the following base policy for testing:

body common control
{
  bundlesequence => { "main" };
}

bundle agent main
{
files:
  "/tmp/testfile"
    edit_line => dostuff,
    create => "true",
    perms => setperms,
    classes => setclasses;

reports:
  kept:: "Promise kept";
  repaired:: "Promise repaired";
  failed:: "Promise repair failed";
  denied:: "Promise repair denied";
  timeout:: "Promise repair timeout";
}

bundle edit_line dostuff
{
  delete_lines:
     "foobar";
}

body classes setclasses
{
  promise_kept => { "kept" };
  promise_repaired => { "repaired" };
  repair_failed => { "failed" };
  repair_denied => { "denied" };
  repair_timeout => { "timeout" };
}

body perms setperms
{
  owners => { "root" };
}


If the file exists and does not contain the line "foobar", then evidently, the 
status is Kept. Easy enough:

$ cat /tmp/testfile
$ cf-agent -KI -f ./test_promise_failures.cfat /tmp/testfile
R: Promise kept


If the file does not exist, then CFEngine creates it (Repaired) and then checks 
that it does not contain the line (Kept), so I get two status codes. Still 
understandable:

$ rm /tmp/testfile
$ cf-agent -KI -f ./test_promise_failures.cfrm /tmp/testfile
 -> Created file /tmp/testfile, mode = 600
R: Promise kept
R: Promise repaired


Now, for the part where it starts getting complicated. If the file exists and 
contains the line to delete, but is owned by root so it cannot be modified (I'm 
running cf-agent from my regular user account), then I get the following:

$ ls -l /tmp/testfile
-rw-r--r--  1 root  wheel  7 Sep 15 01:08 /tmp/testfile
$ cat /tmp/testfile
foobar
$ cf-agent -KI -f ./test_promise_failures.cf
 -> Edited file /tmp/testfile
 !! Can't rename /tmp/testfile to /tmp/testfile.cf-before-edit - so promised 
edits could not be moved into place
 !!! System reports error for cf_rename: "Permission denied"
I: Report relates to a promise with handle ""
I: Made in version 'not specified' of './test_promise_failures.cf' near line 10
R: Promise kept
R: Promise repaired
R: Promise repair failed

I understand that the file exists (Kept) and that it could not be edited 
(Failed - but more on this below). But what part of the promise was "Repaired"? 
(also - why does it complain about the lack of handle here, but not in the 
other examples?)

Another point of confusion: as I read the description in the manual, a failure 
due to lack of permissions should be "Denied" rather than "Failed" (or maybe 
both "Denied" and "Failed"). But in fact, looking at the code, it seems Denied 
is defined for exactly three conditions: failure to chown, failure to chflags 
(BSD flags) and failure to touch (utime):


src/files_operators.c
1906:                   cfPS(cf_inform,CF_DENIED,"chown",pp,attr," !! Cannot 
set ownership on file %s!\n",file);
2248:                cfPS(cf_error,CF_DENIED,"chflags",pp,attr," !! Failed 
setting BSD flags %x on %s\n",newflags,file);
2269:      cfPS(cf_inform,CF_DENIED,"utime",pp,attr," !! Touching file %s 
failed",file);


Testing for chown, it works. First let's change the files: promise:

files:
  "/tmp/testfile"
    perms => setperms,
    classes => setclasses;

And now:

$ ls -l /tmp/testfile
-rw-r--r--  1 zamboni  wheel  0 Sep 15 01:24 /tmp/testfile
$ cf-agent -KI -f ./test_promise_failures.cf
 -> Owner of /tmp/testfile was 503, setting to 0
 !! Cannot set ownership on file /tmp/testfile!
 !!! System reports error for chown: "Operation not permitted"
 -> Owner of /tmp/testfile was 503, setting to 0
 !! Cannot set ownership on file /tmp/testfile!
 !!! System reports error for chown: "Operation not permitted"
 -> Owner of /tmp/testfile was 503, setting to 0
 !! Cannot set ownership on file /tmp/testfile!
 !!! System reports error for chown: "Operation not permitted"
R: Promise kept
R: Promise repaired
R: Promise repair denied

"Kept" because the file existed already. "Denied" because it couldn't chown it 
to root. But again, why Repaired? What was repaired in this case?

Now, for touch it gets confusing, and I think this is a bug. It seems touch can 
fail with either Failed or Denied:

src/files_operators.c
990:   if (utime(path,NULL) != -1)
996:      cfPS(cf_inform,CF_FAIL,"utime",pp,attr,"Touch %s failed to update 
timestamps\n",path);
2267:   if (utime(file,NULL) == -1)
2269:      cfPS(cf_inform,CF_DENIED,"utime",pp,attr," !! Touching file %s 
failed",file);

I'm not familiar enough with the code to trace how each one of those is 
executed. In my test above, it seems to be the one in line 996.

So, to conclude for now because this is getting unwieldy, my two main questions 
are:

- Why is "Repaired" reported in promises in which nothing was repaired (AFAICT)
- Do we need promise_denied? The distinction seems arbitrary at the moment. It 
should either be clearly defined, or merged into repair_failed.

Thanks for your patience and attention,

_______________________________________________
Help-cfengine mailing list
Help-cfengine@cfengine.org
https://cfengine.org/mailman/listinfo/help-cfengine

Reply via email to