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
[email protected]
https://cfengine.org/mailman/listinfo/help-cfengine