Files
kubevela/pkg/definition/defkit/status.go
Ayush Kumar 3f7ad2ba95 Feat: defkit comp def discrepancies (#7048)
* Feat: add MapVariant operation and support for OneOf parameters with default variant

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance string parameter output to include optional prefix in CUE generation

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add ConditionalOrFieldRef for fallback handling and support inline array values

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: update CUE generation to support inline arrays with conditional wrapping

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: update CUE generation to support inline arrays with conditional wrapping

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add support for compound optional fields and enhance array builder with guarded filtering

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add comprehensive tests for ArrayBuilder functionality

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: implement field grouping in StatusBuilder for consolidated CUE output

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance CUE decomposition to support condValues and improve filtering logic

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add metadata labels to ComponentDefinition and update CUE generation

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: clean up comments and formatting in cuegen and param files

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance CUE generation, trait definitions, and PatchContainer logic

CUE Generation:
- Simplify condition decomposition logic and rename GetDirective method
- Add condition decomposition and lifting logic to improve generated CUE output
- Refactor cueTypeForParamType to use a standalone cueTypeStr function for reusability

Collections:
- Enhance MapVariant operation to merge variant mappings and preserve non-matching items

Trait Definitions:
- Enhance TraitDefinition and PatchContainerConfig with new attributes (MultiContainerCheckField, MultiContainerErrMsg)
- Update emission logic in TraitCUEGenerator for multi-container support

PatchContainer:
- Update error messages to use camelCase for consistency with KubeVela conventions

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance test descriptions for clarity and accuracy in array_builder, collections, and status tests

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance handling of optional fields in collections and improve test descriptions for clarity

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: improve ConditionalOrFieldRef tests for clarity and accuracy in handling primary and fallback fields

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance test descriptions for clarity and accuracy in array_builder and expr tests

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: improve formatting of test data in collections tests for better readability

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

---------

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2026-03-01 21:29:18 -08:00

681 lines
23 KiB
Go

/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package defkit
import (
"fmt"
"strings"
)
// StatusBuilder provides a fluent API for building component status expressions.
type StatusBuilder struct {
fields []*StatusField
message string
rawCUE string // Raw CUE for complex status expressions that don't fit the builder pattern
}
// StatusField represents a status field derived from the output.
type StatusField struct {
name string
sourcePath string
defaultValue any
fieldType string // "int", "string", "bool"
}
// defaultExpr returns the CUE default expression for this field's type and value.
// For example: "*0 | int", "*\"\" | string", "*false | bool".
func (f *StatusField) defaultExpr() string {
switch f.fieldType {
case string(ParamTypeInt):
return fmt.Sprintf("*%d | int", f.defaultValue)
case string(ParamTypeString):
return fmt.Sprintf("*%q | string", f.defaultValue)
case string(ParamTypeBool):
return fmt.Sprintf("*%v | bool", f.defaultValue)
default:
return fmt.Sprintf("*%v | _", f.defaultValue)
}
}
// Status creates a new status builder.
func Status() *StatusBuilder {
return &StatusBuilder{
fields: make([]*StatusField, 0),
}
}
// IntField adds an integer field derived from output status.
// Usage: .IntField("ready.replicas", "status.numberReady", 0)
func (s *StatusBuilder) IntField(name, sourcePath string, defaultVal int) *StatusBuilder {
s.fields = append(s.fields, &StatusField{
name: name,
sourcePath: sourcePath,
defaultValue: defaultVal,
fieldType: "int",
})
return s
}
// StringField adds a string field derived from output status.
func (s *StatusBuilder) StringField(name, sourcePath string, defaultVal string) *StatusBuilder {
s.fields = append(s.fields, &StatusField{
name: name,
sourcePath: sourcePath,
defaultValue: defaultVal,
fieldType: string(ParamTypeString),
})
return s
}
// Message sets the status message template.
// Use \(fieldName) for field interpolation (CUE syntax).
// Usage: .Message("Ready:\\(ready.replicas)/\\(desired.replicas)")
func (s *StatusBuilder) Message(msg string) *StatusBuilder {
s.message = msg
return s
}
// Build generates the CUE expression for customStatus.
func (s *StatusBuilder) Build() string {
// If raw CUE is set, use it directly (for complex status expressions that don't fit builder pattern)
if s.rawCUE != "" {
return s.rawCUE
}
var parts []string
parts = append(parts, s.buildGroupedFields()...)
if s.message != "" {
// Use simple quotes around message - don't use %q which escapes backslashes,
// preserving CUE interpolation syntax like \(field)
parts = append(parts, fmt.Sprintf(`message: "%s"`, s.message))
}
return strings.Join(parts, "\n")
}
// buildGroupedFields groups fields by parent prefix and generates consolidated CUE blocks.
// Fields with the same parent (e.g., "status.active", "status.failed") are consolidated
// into a single block with column-aligned defaults.
func (s *StatusBuilder) buildGroupedFields() []string {
type fieldGroup struct {
parent string
fields []*StatusField
}
groups := make([]fieldGroup, 0)
groupIndex := make(map[string]int)
var simpleFields []*StatusField
for _, f := range s.fields {
parts := strings.Split(f.name, ".")
if len(parts) == 2 {
parent := parts[0]
if idx, ok := groupIndex[parent]; ok {
groups[idx].fields = append(groups[idx].fields, f)
} else {
groupIndex[parent] = len(groups)
groups = append(groups, fieldGroup{parent: parent, fields: []*StatusField{f}})
}
} else {
simpleFields = append(simpleFields, f)
}
}
var result []string
for _, g := range groups {
result = append(result, s.buildConsolidatedGroup(g.parent, g.fields))
}
for _, f := range simpleFields {
result = append(result, s.buildField(f))
}
return result
}
// buildConsolidatedGroup generates a single CUE block for fields sharing a parent prefix.
// Field values are column-aligned within the block.
func (s *StatusBuilder) buildConsolidatedGroup(parent string, fields []*StatusField) string {
var defaults []string
var conditionals []string
// Calculate max child field name length for column alignment
maxLen := 0
for _, f := range fields {
parts := strings.Split(f.name, ".")
childName := parts[1]
if len(childName) > maxLen {
maxLen = len(childName)
}
}
for _, f := range fields {
parts := strings.Split(f.name, ".")
childName := parts[1]
padding := strings.Repeat(" ", maxLen-len(childName))
defaults = append(defaults, fmt.Sprintf("\t%s:%s %s", childName, padding, f.defaultExpr()))
conditionals = append(conditionals, fmt.Sprintf("\tif context.output.%s != _|_ {\n\t\t%s: context.output.%s\n\t}", f.sourcePath, childName, f.sourcePath))
}
if len(conditionals) == 0 {
return fmt.Sprintf("%s: {\n%s\n}", parent, strings.Join(defaults, "\n"))
}
return fmt.Sprintf("%s: {\n%s\n} & {\n%s\n}", parent, strings.Join(defaults, "\n"), strings.Join(conditionals, "\n"))
}
// RawCUE sets raw CUE for complex status expressions that don't fit the builder pattern.
func (s *StatusBuilder) RawCUE(cue string) *StatusBuilder {
s.rawCUE = cue
return s
}
func (s *StatusBuilder) buildField(f *StatusField) string {
// Split nested path like "ready.replicas" into ["ready", "replicas"]
pathParts := strings.Split(f.name, ".")
defExpr := f.defaultExpr()
// For nested paths, build the structure
if len(pathParts) == 2 {
return fmt.Sprintf(`%s: {
%s: %s
} & {
if context.output.%s != _|_ {
%s: context.output.%s
}
}`, pathParts[0], pathParts[1], defExpr, f.sourcePath, pathParts[1], f.sourcePath)
}
// Simple field
return fmt.Sprintf(`%s: %s
if context.output.%s != _|_ {
%s: context.output.%s
}`, f.name, defExpr, f.sourcePath, f.name, f.sourcePath)
}
// HealthBuilder provides a fluent API for building health policy expressions.
type HealthBuilder struct {
*StatusBuilder
conditions []string
rawCUE string // Raw CUE string for complex health policies that don't fit the builder pattern
useDefault bool // if true, generates _isHealth: expr + isHealth: *_isHealth | bool
disableAnnotation string // if set, generates annotation-based health-check disable override
}
// Health creates a new health policy builder.
// It embeds StatusBuilder so you can add fields the same way.
func Health() *HealthBuilder {
return &HealthBuilder{
StatusBuilder: Status(),
conditions: make([]string, 0),
}
}
// IntField adds an integer field (delegates to StatusBuilder).
func (h *HealthBuilder) IntField(name, sourcePath string, defaultVal int) *HealthBuilder {
h.StatusBuilder.IntField(name, sourcePath, defaultVal)
return h
}
// StringField adds a string field (delegates to StatusBuilder).
func (h *HealthBuilder) StringField(name, sourcePath string, defaultVal string) *HealthBuilder {
h.StatusBuilder.StringField(name, sourcePath, defaultVal)
return h
}
// MetadataField adds a field from metadata (e.g., generation).
func (h *HealthBuilder) MetadataField(name, sourcePath string) *HealthBuilder {
h.fields = append(h.fields, &StatusField{
name: name,
sourcePath: sourcePath,
fieldType: "metadata",
})
return h
}
// HealthyWhen adds health conditions.
// Usage: .HealthyWhen("desired.replicas == ready.replicas", "updated.replicas == desired.replicas")
func (h *HealthBuilder) HealthyWhen(conditions ...string) *HealthBuilder {
h.conditions = append(h.conditions, conditions...)
return h
}
// WithDefault enables the _isHealth intermediate pattern.
// Generates: _isHealth: (expr) + isHealth: *_isHealth | bool
// instead of: isHealth: expr
func (h *HealthBuilder) WithDefault() *HealthBuilder {
h.useDefault = true
return h
}
// WithDisableAnnotation adds an annotation-based health-check disable override.
// Generates CUE that checks for the given annotation and sets isHealth: true if present.
func (h *HealthBuilder) WithDisableAnnotation(annotation string) *HealthBuilder {
h.disableAnnotation = annotation
return h
}
// StatusEq is a helper for equality conditions in status expressions.
// Usage: .HealthyWhen(StatusEq("desired.replicas", "ready.replicas"))
func StatusEq(left, right string) string {
return fmt.Sprintf("%s == %s", left, right)
}
// StatusGte is a helper for >= conditions in status expressions.
func StatusGte(left, right string) string {
return fmt.Sprintf("%s >= %s", left, right)
}
// StatusOr combines conditions with ||.
func StatusOr(conditions ...string) string {
return "(" + strings.Join(conditions, " || ") + ")"
}
// StatusAnd combines conditions with &&.
func StatusAnd(conditions ...string) string {
return "(" + strings.Join(conditions, " && ") + ")"
}
// Build generates the CUE expression for healthPolicy.
func (h *HealthBuilder) Build() string {
// If raw CUE is set, use it directly (for complex policies that don't fit builder pattern)
if h.rawCUE != "" {
return h.rawCUE
}
var parts []string
// Group fields by parent prefix and generate consolidated blocks
parts = append(parts, h.buildGroupedFields()...)
if len(h.conditions) > 0 {
// When multiple conditions are joined with &&, auto-parenthesize each
// for correct precedence and readability. Skip already-parenthesized
// conditions (e.g. from StatusOr). Single conditions are left as-is.
conditions := h.conditions
if len(conditions) > 1 {
wrapped := make([]string, len(conditions))
for i, c := range conditions {
wrapped[i] = parenthesizeCondition(c)
}
conditions = wrapped
}
healthExpr := strings.Join(conditions, " && ")
if h.useDefault {
parts = append(parts, fmt.Sprintf("_isHealth: %s", healthExpr), "isHealth: *_isHealth | bool")
} else {
parts = append(parts, fmt.Sprintf("isHealth: %s", healthExpr))
}
}
if h.disableAnnotation != "" {
parts = append(parts, fmt.Sprintf("if context.output.metadata.annotations != _|_ {\n\tif context.output.metadata.annotations[%q] != _|_ {\n\t\tisHealth: true\n\t}\n}", h.disableAnnotation))
}
return strings.Join(parts, "\n")
}
// parenthesizeCondition wraps a condition string in parentheses if it isn't already
// fully enclosed in a matching pair. This prevents double-wrapping conditions that
// are already parenthesized (e.g. from StatusOr).
func parenthesizeCondition(s string) string {
s = strings.TrimSpace(s)
if isFullyParenthesized(s) {
return s
}
return "(" + s + ")"
}
// isFullyParenthesized checks whether the string is enclosed by a single matching
// pair of parentheses. For example "(a == b)" and "(a || b)" return true, but
// "(a) && (b)" returns false because the first closing paren appears before the end.
func isFullyParenthesized(s string) bool {
if len(s) < 2 || s[0] != '(' || s[len(s)-1] != ')' {
return false
}
depth := 0
for i, ch := range s {
if ch == '(' {
depth++
} else if ch == ')' {
depth--
}
// If depth reaches 0 before the last character, the outer parens don't enclose everything
if depth == 0 && i < len(s)-1 {
return false
}
}
return depth == 0
}
// buildGroupedFields groups fields by parent prefix and generates consolidated CUE blocks.
// Fields with the same parent (e.g., "ready.replicas", "ready.updatedReplicas") are
// consolidated into a single block with column-aligned defaults.
//
// Only 2-level paths (parent.child) are grouped. Deeper paths (a.b.c) and simple
// paths (name) are treated as simple fields and rendered individually via buildField.
func (h *HealthBuilder) buildGroupedFields() []string {
type fieldGroup struct {
parent string
fields []*StatusField
}
groups := make([]fieldGroup, 0)
groupIndex := make(map[string]int)
var simpleFields []*StatusField
for _, f := range h.fields {
parts := strings.Split(f.name, ".")
if len(parts) == 2 {
parent := parts[0]
if idx, ok := groupIndex[parent]; ok {
groups[idx].fields = append(groups[idx].fields, f)
} else {
groupIndex[parent] = len(groups)
groups = append(groups, fieldGroup{parent: parent, fields: []*StatusField{f}})
}
} else {
simpleFields = append(simpleFields, f)
}
}
var result []string
for _, g := range groups {
result = append(result, h.buildConsolidatedGroup(g.parent, g.fields))
}
for _, f := range simpleFields {
if f.fieldType == "metadata" {
result = append(result, h.buildMetadataField(f))
} else {
result = append(result, h.StatusBuilder.buildField(f))
}
}
return result
}
// buildConsolidatedGroup generates a single CUE block for fields sharing a parent prefix.
// Metadata fields are placed in the defaults block. Typed fields get default values in the
// defaults block and conditional overrides in the conditionals block.
// Field values are column-aligned within the block.
func (h *HealthBuilder) buildConsolidatedGroup(parent string, fields []*StatusField) string {
var defaults []string
var conditionals []string
// Calculate max child field name length for column alignment
maxLen := 0
for _, f := range fields {
parts := strings.Split(f.name, ".")
childName := parts[1]
if len(childName) > maxLen {
maxLen = len(childName)
}
}
for _, f := range fields {
parts := strings.Split(f.name, ".")
childName := parts[1]
padding := strings.Repeat(" ", maxLen-len(childName))
if f.fieldType == "metadata" {
// Metadata fields go in defaults block as direct references
defaults = append(defaults, fmt.Sprintf("\t%s:%s context.output.%s", childName, padding, f.sourcePath))
} else {
// Typed fields get default value in defaults block
defaults = append(defaults, fmt.Sprintf("\t%s:%s %s", childName, padding, f.defaultExpr()))
// Add conditional override
conditionals = append(conditionals, fmt.Sprintf("\tif context.output.%s != _|_ {\n\t\t%s: context.output.%s\n\t}", f.sourcePath, childName, f.sourcePath))
}
}
if len(conditionals) == 0 {
return fmt.Sprintf("%s: {\n%s\n}", parent, strings.Join(defaults, "\n"))
}
return fmt.Sprintf("%s: {\n%s\n} & {\n%s\n}", parent, strings.Join(defaults, "\n"), strings.Join(conditionals, "\n"))
}
// RawCUE sets raw CUE for complex health policies that don't fit the builder pattern.
func (h *HealthBuilder) RawCUE(cue string) *HealthBuilder {
h.rawCUE = cue
return h
}
func (h *HealthBuilder) buildMetadataField(f *StatusField) string {
pathParts := strings.Split(f.name, ".")
if len(pathParts) == 2 {
return fmt.Sprintf(`%s: {
%s: context.output.%s
}`, pathParts[0], pathParts[1], f.sourcePath)
}
return fmt.Sprintf("%s: context.output.%s", f.name, f.sourcePath)
}
// --- Predefined status/health builders for common workloads ---
// DaemonSetStatus returns a pre-configured status builder for DaemonSet.
func DaemonSetStatus() *StatusBuilder {
return Status().
IntField("ready.replicas", "status.numberReady", 0).
IntField("desired.replicas", "status.desiredNumberScheduled", 0).
Message(`Ready:\(ready.replicas)/\(desired.replicas)`)
}
// DaemonSetHealth returns a pre-configured health builder for DaemonSet.
func DaemonSetHealth() *HealthBuilder {
return Health().
IntField("ready.replicas", "status.numberReady", 0).
IntField("desired.replicas", "status.desiredNumberScheduled", 0).
IntField("current.replicas", "status.currentNumberScheduled", 0).
IntField("updated.replicas", "status.updatedNumberScheduled", 0).
MetadataField("generation.metadata", "metadata.generation").
IntField("generation.observed", "status.observedGeneration", 0).
HealthyWhen(
StatusEq("desired.replicas", "ready.replicas"),
StatusEq("desired.replicas", "updated.replicas"),
StatusEq("desired.replicas", "current.replicas"),
StatusOr(StatusEq("generation.observed", "generation.metadata"), "generation.observed > generation.metadata"),
)
}
// DeploymentStatus returns a pre-configured status builder for Deployment.
// Matches the original CUE structure which uses spec.replicas for desired count.
func DeploymentStatus() *StatusBuilder {
return Status().
IntField("ready.readyReplicas", "status.readyReplicas", 0).
Message(`Ready:\(ready.readyReplicas)/\(context.output.spec.replicas)`)
}
// DeploymentHealth returns a pre-configured health builder for Deployment.
// Uses consolidated structure with all fields in a single "ready" block,
// the _isHealth intermediate pattern, and annotation-based health disable.
func DeploymentHealth() *HealthBuilder {
return Health().
IntField("ready.updatedReplicas", "status.updatedReplicas", 0).
IntField("ready.readyReplicas", "status.readyReplicas", 0).
IntField("ready.replicas", "status.replicas", 0).
IntField("ready.observedGeneration", "status.observedGeneration", 0).
HealthyWhen(
StatusEq("context.output.spec.replicas", "ready.readyReplicas"),
StatusEq("context.output.spec.replicas", "ready.updatedReplicas"),
StatusEq("context.output.spec.replicas", "ready.replicas"),
StatusOr(StatusEq("ready.observedGeneration", "context.output.metadata.generation"), "ready.observedGeneration > context.output.metadata.generation"),
).
WithDefault().
WithDisableAnnotation("app.oam.dev/disable-health-check")
}
// StatefulSetStatus returns a pre-configured status builder for StatefulSet.
func StatefulSetStatus() *StatusBuilder {
return Status().
IntField("ready.replicas", "status.readyReplicas", 0).
IntField("desired.replicas", "status.replicas", 0).
Message(`Ready:\(ready.replicas)/\(desired.replicas)`)
}
// StatefulSetHealth returns a pre-configured health builder for StatefulSet.
func StatefulSetHealth() *HealthBuilder {
return Health().
IntField("ready.replicas", "status.readyReplicas", 0).
IntField("updated.replicas", "status.updatedReplicas", 0).
IntField("desired.replicas", "status.replicas", 0).
MetadataField("generation.metadata", "metadata.generation").
IntField("generation.observed", "status.observedGeneration", 0).
HealthyWhen(
StatusEq("desired.replicas", "ready.replicas"),
StatusEq("desired.replicas", "updated.replicas"),
StatusOr(StatusEq("generation.observed", "generation.metadata"), "generation.observed > generation.metadata"),
)
}
// JobHealth returns a pre-configured health builder for Job.
func JobHealth() *HealthBuilder {
return Health().
IntField("succeeded", "status.succeeded", 0).
HealthyWhen("succeeded == context.output.spec.parallelism")
}
// CronJobHealth returns a pre-configured health builder for CronJob.
func CronJobHealth() *HealthBuilder {
return Health().
HealthyWhen("true") // CronJob is always considered healthy if it exists
}
// --- Composable Health Expression Methods on HealthBuilder ---
// These methods allow building health checks using composable expressions
// that are then converted to CUE via HealthyWhenExpr().
// Condition creates an expression to check a status condition.
// Example: Health().Condition("Ready").IsTrue()
func (h *HealthBuilder) Condition(condType string) *ConditionExpr {
return &ConditionExpr{
condType: condType,
expectedStatus: "True", // default
}
}
// Field creates an expression builder for a field path.
// Example: Health().Field("status.replicas").Gt(0)
func (h *HealthBuilder) Field(path string) *HealthFieldExpr {
return &HealthFieldExpr{path: path}
}
// FieldRef creates a reference to another field for field-to-field comparisons.
// Example: Health().Field("status.readyReplicas").Eq(Health().FieldRef("spec.replicas"))
func (h *HealthBuilder) FieldRef(path string) *HealthFieldRefExpr {
return &HealthFieldRefExpr{path: path}
}
// Phase creates an expression to check if status.phase matches any of the given phases.
// Example: Health().Phase("Running", "Succeeded")
func (h *HealthBuilder) Phase(phases ...string) HealthExpression {
return &phaseExpr{
fieldPath: "context.output.status.phase",
phases: phases,
}
}
// PhaseField creates an expression to check a custom phase field path.
// Example: Health().PhaseField("status.currentPhase", "Active", "Ready")
func (h *HealthBuilder) PhaseField(path string, phases ...string) HealthExpression {
return &phaseExpr{
fieldPath: "context.output." + path,
phases: phases,
}
}
// Exists checks if a field exists (is not _|_).
// Example: Health().Exists("status.loadBalancer.ingress")
func (h *HealthBuilder) Exists(path string) HealthExpression {
return &existsExpr{path: path, negate: false}
}
// NotExists checks if a field does not exist (is _|_).
// Example: Health().NotExists("status.error")
func (h *HealthBuilder) NotExists(path string) HealthExpression {
return &existsExpr{path: path, negate: true}
}
// And combines multiple health expressions with AND.
// All expressions must be true for the health check to pass.
// Example: Health().And(expr1, expr2, expr3)
func (h *HealthBuilder) And(exprs ...HealthExpression) HealthExpression {
return &andExpr{exprs: exprs}
}
// Or combines multiple health expressions with OR.
// Any expression being true makes the health check pass.
// Example: Health().Or(expr1, expr2)
func (h *HealthBuilder) Or(exprs ...HealthExpression) HealthExpression {
return &orExpr{exprs: exprs}
}
// Not negates a health expression.
// Example: Health().Not(Health().Condition("Stalled").IsTrue())
func (h *HealthBuilder) Not(expr HealthExpression) HealthExpression {
return &notExpr{expr: expr}
}
// Always returns a health expression that is always true.
// Use this when resource existence is the only health criteria.
// Example: Health().Always()
func (h *HealthBuilder) Always() HealthExpression {
return &alwaysExpr{}
}
// AllTrue checks if all specified conditions have status "True".
// Example: Health().AllTrue("Ready", "Synced")
func (h *HealthBuilder) AllTrue(condTypes ...string) HealthExpression {
exprs := make([]HealthExpression, len(condTypes))
for i, ct := range condTypes {
exprs[i] = h.Condition(ct).IsTrue()
}
return h.And(exprs...)
}
// AnyTrue checks if any of the specified conditions have status "True".
// Example: Health().AnyTrue("Ready", "Available")
func (h *HealthBuilder) AnyTrue(condTypes ...string) HealthExpression {
exprs := make([]HealthExpression, len(condTypes))
for i, ct := range condTypes {
exprs[i] = h.Condition(ct).IsTrue()
}
return h.Or(exprs...)
}
// HealthyWhenExpr sets the health condition using a composable HealthExpression.
// This generates the appropriate preamble and isHealth expression.
// Example: Health().HealthyWhenExpr(Health().Condition("Ready").IsTrue())
func (h *HealthBuilder) HealthyWhenExpr(expr HealthExpression) *HealthBuilder {
h.rawCUE = HealthPolicy(expr)
return h
}
// Policy generates a complete healthPolicy CUE block from a HealthExpression.
// This is useful for generating standalone health policies without setting them on the builder.
// Example: Health().Policy(Health().Condition("Ready").IsTrue())
func (h *HealthBuilder) Policy(expr HealthExpression) string {
return HealthPolicy(expr)
}