CFE-4686: Add cancel attribute to classes promises to undefine a class#6175
CFE-4686: Add cancel attribute to classes promises to undefine a class#6175nickanderson wants to merge 5 commits into
Conversation
|
@cf-bottom jenkins please |
|
Alright, I triggered a build: Jenkins: https://ci.cfengine.com/job/pr-pipeline/13945/ Packages: http://buildcache.cfengine.com/packages/testing-pr/jenkins-pr-pipeline-13945/ |
|
Thanks nice addition!! Much needed |
d9151e2 to
bf85569
Compare
olehermanse
left a comment
There was a problem hiding this comment.
It looks a bit backwards that cancel has a class expression. To me it reads as if what you put as the value to the cancel attribute should be the class you want to cancel. With the proposed behavior I guess something like cancel_if would be a better name.
But why should it have a class expression? We already have conditions (class expressions) in many places: class guard, if, unless, and expression for classes promises. Why do we need one more?
Some other proposals;
bundle agent example
{
classes:
some_condition.linux::
"my_class"
# true / false, default to false:
cancel => true;
}
bundle agent example
{
classes:
"my_class"
# define / undefine, default to define:
do => "undefine",
if => "some_condition.linux";
}
On the naming, yeah right after I pushed it I was thinking to mysefl that maintenance cancel blah read quite poorly. I was thinking a bit about That leaves if/unless still available for further compound constraint. or maybe invalidated_by ? we already have if/ifvarclass so cancel_if just seemed a bit yeuck. @basvandervlies @ncharles opinions on language here?
Most class-defining attributes accepts full class expressions:
|
|
Another idea, expire_when |
|
expire is growing on me (I don't need the _when suffix, but im ok with it or even _if I guess.) |
|
I like what @olehermanse suggested as syntax: Only I suggest If we got for the |
An attribute that specifies define/undefined would change how all classes promises are processed instead of adding a new canceling expression. What would be un-defined?. What about the distribution and selected class? Yes, the attribute could be made mutually exclusive. The attributes that are currently involved in determining if the class should be defined (and, dist, expression, or, not, select_class, and xorg are already mutually exclusive. I don't know if that is that is cleaner. it feels like more to remember. Persistent classes were on my mind when I suggested expire. It doesn't fit as well with non-persistent classes. So I am back to preferring |
|
@nickanderson the exact same problems exist with your suggestion. We should make all those other attributes mutually exclusive with the new cancel attribute regardless. It's not at all clear what should happen here or how
|
ugh, true.
I still don't like it and I still think it's wrong. We use expressions all over the place, it's weird to not take an expression. |
Covers undefining a defined class when the cancel expression is true, retaining the class when the expression is false, function-based triggers, the no-op case for an undefined class, refusal to cancel reserved hard classes, and mutual exclusion with the class-defining attributes. Ticket: CFE-4686 Changelog: None Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Until now a classes promise could only define a class; the only way to undefine one from policy was a cancel_* classes body attached to another promise's outcome. This adds a 'cancel' attribute to the classes promise itself: when its class expression (a string, or a function returning a boolean) evaluates true, the promiser class is undefined if it is defined. The class-skipping optimization in ExpandDeRefPromise() normally excludes a classes promise whose promiser is already defined. A cancel promise must run precisely in that case, so the skip now makes an exception when the 'cancel' attribute is present. 'cancel' is mutually exclusive with the class-defining attributes (expression, and, or, xor, not, dist, select_class) via the existing "Irreconcilable constraints" check, since it is counted like any other class-body attribute. Reserved hard classes cannot be cancelled. The evaluation and undefine path mirrors the cancel_* classes body behaviour (DeleteAllClasses). Ticket: CFE-4686 Changelog: Title Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Was a class expression; now a cfbool. Reverts the no-longer-needed EvalClassExpression split Ticket: CFE-4686 Changelog: None Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These only tune how a class is defined or persisted, so they are meaningless when undefining one. Reject them rather than ignoring them. Ticket: CFE-4686 Changelog: None Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ecord Defines a persistent class then cancels it across two runs, asserting via cf-check dump that the cf_state record is present then gone. Ticket: CFE-4686 Changelog: None Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bf85569 to
df85b19
Compare
|
Updated the branch (force-pushed, rebased onto current
Docs PR cfengine/documentation#3683 updated to match. |
Summary
Adds a
cancelattribute toclassespromises so policy can undefine aclass. Until now a classes promise could only define a class; the only way
to undefine one from policy was a
cancel_*classes body attached to anotherpromise's outcome. With
cancel, the classes promise can undefine its ownpromiser directly:
When the cancel expression (a class expression — string or a function
returning a boolean) evaluates true, the promiser class is undefined if it is
currently defined. False trigger, or an already-undefined class, is a no-op.
Key detail: the class-skipping exception
ExpandDeRefPromise()normally excludes a classes promise whose promiser isalready defined (an optimization). A
cancelpromise must run precisely inthat case, so the skip now makes an exception when the
cancelattribute ispresent. This is the crux of the change — without it the promise would never
actuate.
Behaviour
expression,and,or,xor,not,dist,select_class) via the existing "Irreconcilableconstraints" check —
cancelis counted like any other class-body attribute.consistent with the
cancel_*classes body attributes (ENT-7718 / CFE-3647).cancel_*classes body(
DeleteAllClasses): persistent removal +EvalContextClassRemove+bundle-frame removal.
Tests
New acceptance tests in
tests/acceptance/02_classes/01_basic/:cancel_attribute.cf,cancel_attribute_hardclass.cf,cancel_attribute_mutex.cf(+.sub).Full
02_classessuite: 117 passed, 0 failed (3 staging-skipped, 3pre-existing soft failures).
Docs
Documentation PR: cfengine/documentation (CFE-4686) adds the
cancelreferencesection.
Ticket: https://northerntech.atlassian.net/browse/CFE-4686
🤖 Generated with Claude Code