Skip to content

Commit a17dbbf

Browse files
feat: conditionPolicy anyOf/allOf implementation
1 parent dbb83df commit a17dbbf

12 files changed

Lines changed: 365 additions & 56 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ With this controller you can:
2525

2626
### Key Features
2727

28-
- **Multi-condition Rules**: Define rules that require ALL specified conditions to be satisfied
28+
- **Multi-condition Rules**: Define rules with flexible condition policies (`allOf` or `anyOf`) to require ALL or ANY specified conditions to be satisfied
2929
- **Flexible Enforcement**: Support for bootstrap-only and continuous enforcement modes
3030
- **Conflict Prevention**: Validation webhook prevents conflicting taint configurations
3131
- **Dry Run Mode**: Preview rule impact before applying changes

api/v1alpha1/nodereadinessrule_types.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ const (
3333
EnforcementModeContinuous EnforcementMode = "continuous"
3434
)
3535

36+
// ConditionPolicy defines how the list of conditions is aggregated when evaluating a rule.
37+
// +kubebuilder:validation:Enum=allOf;anyOf
38+
type ConditionPolicy string
39+
40+
const (
41+
// ConditionPolicyAllOf requires ALL conditions to match their requiredStatus (default).
42+
ConditionPolicyAllOf ConditionPolicy = "allOf"
43+
44+
// ConditionPolicyAnyOf requires at least ONE condition to match its requiredStatus.
45+
ConditionPolicyAnyOf ConditionPolicy = "anyOf"
46+
)
47+
3648
// TaintStatus specifies status of the Taint on Node.
3749
// +kubebuilder:validation:Enum=Present;Absent
3850
type TaintStatus string
@@ -46,6 +58,14 @@ const (
4658
)
4759

4860
// NodeReadinessRuleSpec defines the desired state of NodeReadinessRule.
61+
//
62+
// We put the conditionPolicy immutability validation at `NodeReadinessRuleSpec` level instead of
63+
// putting it on `conditionPolicy`. This is required because conditionPolicy is optional field. If a
64+
// user transitions from an omitted field to an explicit "allOf" or vice-versa, field-level
65+
// transition rules are not evaluated, since validations are only performed only when both self and
66+
// oldSelf are present. By evaluating it at the struct level, we can safely use the `has()` macro to
67+
// normalize absent values and force evaluation under any condition.
68+
// +kubebuilder:validation:XValidation:rule="(!has(oldSelf.conditionPolicy) ? 'allOf' : oldSelf.conditionPolicy) == (!has(self.conditionPolicy) ? 'allOf' : self.conditionPolicy)",message="conditionPolicy is immutable"
4969
type NodeReadinessRuleSpec struct {
5070
// conditions contains a list of the Node conditions that defines the specific
5171
// criteria that must be met for taints to be managed on the target Node.
@@ -99,6 +119,15 @@ type NodeReadinessRuleSpec struct {
99119
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="nodeSelector is immutable"
100120
NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty,omitzero"`
101121

122+
// conditionPolicy controls how the conditions list is evaluated.
123+
// "allOf" (default) requires every condition to match its requiredStatus before the taint is removed.
124+
// "anyOf" requires at least one condition to match its requiredStatus.
125+
//
126+
// anyOf cannot be used with enforcementMode: bootstrap-only.
127+
//
128+
// +optional
129+
ConditionPolicy ConditionPolicy `json:"conditionPolicy,omitempty"` // Use GetConditionPolicy() for safe access; field may be empty even when allOf applies.
130+
102131
// dryRun when set to true, The controller will evaluate Node conditions and log intended taint modifications
103132
// without persisting changes to the cluster. Proposed actions are reflected in the resource status.
104133
//
@@ -379,6 +408,20 @@ func (c *ConditionRequirement) GetDefaultStatus() corev1.ConditionStatus {
379408
return c.DefaultStatus
380409
}
381410

411+
// GetConditionPolicy returns the effective condition policy, defaulting to allOf
412+
// when the field is not explicitly set.
413+
//
414+
// Always use this method instead of reading ConditionPolicy directly. The field
415+
// is intentionally left without an OpenAPI schema default (kubebuilder:default
416+
// is forbidden by project policy) and the Spec is immutable, so defaulting
417+
// must happen at read time via this accessor.
418+
func (spec *NodeReadinessRuleSpec) GetConditionPolicy() ConditionPolicy {
419+
if spec.ConditionPolicy == "" {
420+
return ConditionPolicyAllOf
421+
}
422+
return spec.ConditionPolicy
423+
}
424+
382425
func init() {
383426
objectTypes = append(objectTypes, &NodeReadinessRule{}, &NodeReadinessRuleList{})
384427
}

charts/node-readiness-controller/crds/nodereadinessrules.readiness.node.x-k8s.io.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ spec:
6262
spec:
6363
description: spec defines the desired state of NodeReadinessRule
6464
properties:
65+
conditionPolicy:
66+
description: |-
67+
conditionPolicy controls how the conditions list is evaluated.
68+
"allOf" (default) requires every condition to match its requiredStatus before the taint is removed.
69+
"anyOf" requires at least one condition to match its requiredStatus.
70+
71+
anyOf cannot be used with enforcementMode: bootstrap-only.
72+
enum:
73+
- allOf
74+
- anyOf
75+
type: string
6576
conditions:
6677
description: |-
6778
conditions contains a list of the Node conditions that defines the specific
@@ -251,6 +262,10 @@ spec:
251262
- nodeSelector
252263
- taint
253264
type: object
265+
x-kubernetes-validations:
266+
- message: conditionPolicy is immutable
267+
rule: '(!has(oldSelf.conditionPolicy) ? ''allOf'' : oldSelf.conditionPolicy)
268+
== (!has(self.conditionPolicy) ? ''allOf'' : self.conditionPolicy)'
254269
status:
255270
description: status defines the observed state of NodeReadinessRule
256271
minProperties: 1

config/crd/bases/readiness.node.x-k8s.io_nodereadinessrules.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ spec:
6262
spec:
6363
description: spec defines the desired state of NodeReadinessRule
6464
properties:
65+
conditionPolicy:
66+
description: |-
67+
conditionPolicy controls how the conditions list is evaluated.
68+
"allOf" (default) requires every condition to match its requiredStatus before the taint is removed.
69+
"anyOf" requires at least one condition to match its requiredStatus.
70+
71+
anyOf cannot be used with enforcementMode: bootstrap-only.
72+
enum:
73+
- allOf
74+
- anyOf
75+
type: string
6576
conditions:
6677
description: |-
6778
conditions contains a list of the Node conditions that defines the specific
@@ -251,6 +262,10 @@ spec:
251262
- nodeSelector
252263
- taint
253264
type: object
265+
x-kubernetes-validations:
266+
- message: conditionPolicy is immutable
267+
rule: '(!has(oldSelf.conditionPolicy) ? ''allOf'' : oldSelf.conditionPolicy)
268+
== (!has(self.conditionPolicy) ? ''allOf'' : self.conditionPolicy)'
254269
status:
255270
description: status defines the observed state of NodeReadinessRule
256271
minProperties: 1

0 commit comments

Comments
 (0)