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