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 2 commits into
Conversation
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>
|
@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 |
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>
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.) |
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