Jeroen van den Haspel <jvdh...@gmail.com> writes:
> When changing a file including permissions I get surprising results back
> when setting classes based on ifrepaired and ifok.
> I made an overview in the attached file.
Hi Jeroen,
When posting policy questions to the list, it's best if you can include a self
contained runnable example.
It's hard to tell exactly what you are trying to do but I think you might be
confused by multiple outcomes from compound promises, or multiple outcomes as
the result of subsequent passes.
First, promises can have multiple outcomes. The devil is in the details.
Let's start by creating an immutable file.
#+Caption Create an immutable file
touch /tmp/immutable_file chatter +i /tmp/immutable_file lsattr /tmp/immutable_file
bundle agent main { vars: "immutable" string => "/tmp/immutable_file"; files: # Here we make a promise that the immutable file exists. Note we do not # make any statement about its extended attributes, just that the file # should exist. We attach the results classes body to make it easy to # visualize the classes that are defined by the promise. Because this file # exists before the agent run we expect this promise to be kept. "$(immutable)" handle => "immutable_present", create => "true", classes => results("bundle", "immutable_present"); # Here we we make no promise not that the file exists, but we do promise # that if the file does exist, it should contain the current value of the # $(sys.date) variable which is initalized at the beginning of the agent # run. Because this file is immutable and does not contain the desired # string, we expect that the agent will attempt to correct the content and # fail, resulting in a promise not kept. "$(immutable)" handle => "immutable_content_date", edit_line => append_if_no_line( $(sys.date) ), classes => results("bundle", "immutable_content_date"); # Here we promise not only that the file exists, but also that it contains # a line with the current value of the $(sys.date) variable which is # initalized at the beginning of the agent run. This promise compounds # multiple promises into a single statement. We expect that the promise # will be kept because the file does indeed exist, however we also expect # that the agent will fail to correct the content of the file since it is # immutable. "$(immutable)" handle => "immutable_present_content_date", create => "true", edit_line => append_if_no_line( $(sys.date) ), classes => results("bundle", "immutable_present_content_date"); vars: "immutable_present" slist => classesmatching("immutable_present_.*"); "immutable_content_date" slist => classesmatching("immutable_content_date_.*"); "immutable_present_content_date" slist => classesmatching("immutable_present_content_date_.*"); reports: "CFEngine Version: $(sys.cf_version)"; "$(immutable_present)"; "$(immutable_content_date)"; "$(immutable_present_content_date)"; }
It's also possible to have a condition where on the first pass (remember when
evaluating policy cf-agent will give each explicitly actuated bundle three
passes of evaluation) a promise is notkept, but later in that pass something
changes wich allows the promise to succeed resulting in the promise now having a
kept outcome.
Also of potential interest is the depends_on
attribute. It works on promise
outcomes, not classes. You can have one promise depend on one or more promise
handles. The promise that depends on the other will only actuate if all of the
other listed promises have had an outcome of kept or repaired (this is
independant of having any classes defined as a result of the other promsies).
I hope this helps.
--
You received this message because you are subscribed to the Google Groups "help-cfengine" group.
To unsubscribe from this group and stop receiving emails from it, send an email to help-cfengine+unsubscribe@googlegroups.com.
To post to this group, send email to help-c...@googlegroups.com.
Visit this group at https://groups.google.com/group/help-cfengine.
For more options, visit https://groups.google.com/d/optout.
Jeroen van den Haspel <jvdh...@gmail.com> writes:
> Thank you Nick!
You're most welcome.
files: user_exists:: "$(privatekeydir)/$(username).priv" comment => "Copy private rsa key", create => "true", perms => mo("0600","$(username)"), action => immediate_dlc, copy_from => cp_dlc("$(file_location.systemfiles_common)/rsa_keys/private/service/$(username).priv"), classes => ok_changed_error("private_key_$(username)");
> So the file block for "(username).priv" cannot be seen as
> "one" action.
> Is there a way to threat the combination of create-perms-copy as one action
> with one outcome?
Not exactly. It's considered "best practice" to make the smallest possible
promises. One thing you could do is to break that promise up in to it's smallest
parts and then place them into a bundle together. Bundles when actuated as
methods also have outcomes. They roll up the worst result. So if any promise in
a bundle is not kept the whole bundle is considered not kept.
bundle agent main { methods: "kept" classes => results("bundle", "test_first_bundle"); "not_kept" classes => results("bundle", "test_second_bundle"); "repaired" classes => results("bundle", "test_third_bundle"); vars: "c" slist => classesmatching("test_.*"); reports: "$(c)"; } bundle agent kept { reports: "Reports are always kept, so this bundle should be kept"; } bundle agent not_kept { commands: "/bin/false"; # not_kept "/bin/true"; # repaired reports: "Any promise resulting in not kept results in the bundle being not kept"; } bundle agent repaired { commands: "/bin/true"; reports: "One promise kept, one promise repaired results in a repaired bundle because repaired is worse than kept"; }
R: Reports are always kept, so this bundle should be kept error: Finished command related to promiser '/bin/false' -- an error occurred, returned 1 R: Any promise resulting in not kept results in the bundle being not kept error: Method 'not_kept' failed in some repairs R: One promise kept, one promise repaired results in a repaired bundle because repaired is worse than kept R: test_third_bundle_repaired R: test_third_bundle_reached R: test_second_bundle_failed R: test_second_bundle_not_kept R: test_second_bundle_error R: test_second_bundle_reached R: test_first_bundle_kept R: test_first_bundle_reached
Note: Promises that have potential for multiple outcomes because they are
compound are not rolled into a single atomic transaction. Each part of that
compound promise attempts to converge independently. Being able to compound them
into a single statement simply allows you to express multiple promises in a more
concise way.
Does that help?
Thanks! Great example.
This absolutely helps us in the right direction.
I like the methods method you use. I only knew the usebundle way but now it can set a class as result of a bundle.
So cutting it in smaller portions (bundles) and separating perms and copy is the way to go.
Regards,
Jeroen.
Verzonden vanaf mijn mobiel.
Great. Thanks!
Regards,
Jeroen.
Verzonden vanaf mijn mobiel.
Jeroen van den Haspel writes:
> Thanks! Great example.
>
> This absolutely helps us in the right direction.
> I like the methods method you use. I only knew the usebundle way but now it
> can set a class as result of a bundle.
> So cutting it in smaller portions (bundles) and separating perms and copy
> is the way to go.
I'm glad it's helpful.
One more thing to keep in mind. Bundles are not functions. They are
collections of promises, and they maintain state between actuation during the
same agent run.
bundle agent main { methods: "one" usebundle => mybundle("first_actuation"); "two" usebundle => mybundle("second_actuation"); "report"; } bundle agent mybundle(param) { vars: # Create a var named for the param containing its own name. "$(param)" string => "$(param)"; } bundle agent report { vars: "in_mybundle" slist => variablesmatching("default\:mybundle\..*"); reports: "Vars defined in mybundle $(in_mybundle) == $($(in_mybundle))"; }
R: Vars defined in mybundle default:mybundle.first_actuation == first_actuation R: Vars defined in mybundle default:mybundle.second_actuation == second_actuation
Similarly a promise that has already been kept or repaired won't run again.
bundle agent main { methods: "one" usebundle => mybundle("first_actuation"); "two" usebundle => mybundle("second_actuation"); } bundle agent mybundle(param) { commands: # This won't be actuated when mybundle is called a second time. "/bin/echo" args => "Hello1"; # This will because the promise is different from the previous actuation (differnt param value). "/bin/echo" args => "$(param)"; # THis will because the promise is different from the previous actuation (unique handle); "/bin/echo" args => "Hello2", handle => "$(param)"; vars: # Create a var named for the param containing its own name. "$(param)" string => "$(param)"; } bundle agent report { vars: "in_mybundle" slist => variablesmatching("default\:mybundle\..*"); reports: "Vars defined in mybundle $(in_mybundle) == $($(in_mybundle))"; }
notice: Q: ".../bin/echo Hello": Hello1 notice: Q: ".../bin/echo first": first_actuation notice: Q: ".../bin/echo Hello": Hello2 notice: Q: ".../bin/echo secon": second_actuation notice: Q: ".../bin/echo Hello": Hello2
–
Nick Anderson
Senior Solutions Engineer, https://cfengine.com/
mobile: +1-785-550-1767
Jeroen van den Haspel writes:
> Thanks again Nick.
>
> At the bottom of this page:
> https://docs.cfengine.com/docs/3.9/reference-promise-types-methods.html
> there is an example of sending return values.
>
> That's quite close to function behaviour.
Yes, it's similar, and you can successfully use bundles like functions as long
as you remember the details especially about how promises wont be actuated again
once they have been kept or repaired (you may need to make them unique).
bundle agent main { methods: "test" useresult => "test_result"; "test2a" usebundle => test2("test2a"), useresult => "test2_result"; "test2b" usebundle => test2("test2b"), useresult => "test2_result"; # Note I put the result into the same array, this may or may not be # useful depending on your sitatuation. reports: "$(test_result[1])"; "$(test2_result[test2a])"; "$(test2_result[test2b])"; } bundle agent test { reports: "Returned Value" bundle_return_value_index => "1"; } bundle agent test2(return_value_index) { reports: "Passed $(return_value_index)" bundle_return_value_index => "$(return_value_index)"; }
R: Returned Value R: Passed test2a R: Passed test2b