Refactor common CEL eval functions into shared pkg (#1003)
Scorecard supply-chain security / Scorecard analysis (push) Failing after 1m10s
Post / coverage (push) Failing after 30m9s
Post / images (amd64) (push) Failing after 3m38s
Post / images (arm64) (push) Failing after 2m50s
Post / image manifest (push) Has been skipped
Post / trigger clusteradm e2e (push) Has been skipped
Close stale issues and PRs / stale (push) Successful in 35s

Signed-off-by: Ben Perry <bhperry94@gmail.com>
This commit is contained in:
Ben Perry
2025-05-26 14:36:04 +00:00
committed by GitHub
parent 5e51b1fb9f
commit f13599ffdb
2 changed files with 88 additions and 68 deletions
+71
View File
@@ -0,0 +1,71 @@
package helpers
import (
"context"
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types/ref"
"k8s.io/klog/v2"
)
// EvaluateSingleExpression evaluates one CEL expression and handles its cost accounting.
// Returns (evalResult, newBudget) if evaluation succeeds, otherwise (nil, -1 or remaining budget).
func EvaluateSingleExpression(
ctx context.Context,
program cel.Program,
budget int64,
expression string,
input any,
) (ref.Val, int64) {
logger := klog.FromContext(ctx)
// Evaluate the expression
evalResult, evalDetails, err := program.ContextEval(ctx, input)
// Cost calculation
ok, rtCost := CostCalculation(ctx, evalDetails, budget, expression)
if !ok {
return nil, -1
}
remainingBudget := budget - rtCost
// Handle evaluation error
if err != nil {
logger.Info("Expression evaluation failed", "rule", expression, "cluster", "err", err)
return nil, remainingBudget
}
return evalResult, remainingBudget
}
// CostCalculation processes the cost details of an evaluation
func CostCalculation(ctx context.Context, evalDetails *cel.EvalDetails, budget int64, expression string) (bool, int64) {
logger := klog.FromContext(ctx)
// Check if cost details are available
if evalDetails == nil {
logger.Info("Runtime cost calculation failed: no evaluation details",
"rule", expression)
return false, -1
}
rtCost := evalDetails.ActualCost()
if rtCost == nil {
logger.Info("Runtime cost calculation failed: no cost information",
"rule", expression)
return false, -1
}
// Validate cost against budget
if *rtCost > math.MaxInt64 || int64(*rtCost) > budget {
logger.Info("Cost budget exceeded",
"rule", expression,
"cost", *rtCost,
"budget", budget)
return false, -1
}
// Safe to convert since we checked for overflow
return true, int64(*rtCost) //nolint:gosec
}
+17 -68
View File
@@ -2,10 +2,10 @@ package helpers
import (
"context"
"math"
"time"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types/ref"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
celconfig "k8s.io/apiserver/pkg/apis/cel"
apiservercel "k8s.io/apiserver/pkg/cel"
@@ -16,6 +16,7 @@ import (
ocmcelcommon "open-cluster-management.io/sdk-go/pkg/cel/common"
ocmcellibrary "open-cluster-management.io/sdk-go/pkg/cel/library"
commonhelpers "open-cluster-management.io/ocm/pkg/common/helpers"
"open-cluster-management.io/ocm/pkg/placement/controllers/metrics"
)
@@ -126,8 +127,10 @@ func (c *CELSelector) Validate(ctx context.Context, cluster *clusterapiv1.Manage
// evaluateAllExpressions evaluates each CEL expression in sequence.
// Returns (true, remainingBudget) if all expressions succeed, otherwise (false, budget at failure).
func (c *CELSelector) evaluateAllExpressions(ctx context.Context, cluster *unstructured.Unstructured, budget int64) (bool, int64) {
ctx = context.WithValue(ctx, "cluster", cluster.GetName())
logger := klog.FromContext(ctx)
remainingBudget := budget
input := map[string]any{"managedCluster": cluster.Object}
for i, compiled := range c.compilationResult {
// Validate program compilation
@@ -137,8 +140,14 @@ func (c *CELSelector) evaluateAllExpressions(ctx context.Context, cluster *unstr
}
// Evaluate single expression
ok, newBudget := c.evaluateSingleExpression(ctx, compiled.Program, cluster, remainingBudget, i)
if !ok {
evalResult, newBudget := commonhelpers.EvaluateSingleExpression(
ctx,
compiled.Program,
remainingBudget,
c.celExpressions[i],
input,
)
if !c.isResultValid(evalResult) {
return false, newBudget
}
remainingBudget = newBudget
@@ -152,70 +161,10 @@ func (c *CELSelector) isProgramValid(compiled CompilationResult) bool {
return compiled.Program != nil && compiled.Error == nil
}
// evaluateSingleExpression evaluates one CEL expression and handles its cost accounting.
// Returns (true, newBudget) if evaluation succeeds, otherwise (false, -1 or remaining budget).
func (c *CELSelector) evaluateSingleExpression(
ctx context.Context,
program cel.Program,
cluster *unstructured.Unstructured,
budget int64,
index int,
) (bool, int64) {
logger := klog.FromContext(ctx)
// Evaluate the expression
evalResult, evalDetails, err := program.ContextEval(ctx, map[string]interface{}{
"managedCluster": cluster.Object,
})
// Cost calculation
ok, rtCost := c.costCalculation(ctx, evalDetails, budget, index)
if !ok {
return false, -1
func (c *CELSelector) isResultValid(evalResult ref.Val) bool {
if evalResult == nil {
return false
}
remainingBudget := budget - rtCost
// Handle evaluation error
if err != nil {
logger.Info("Expression evaluation failed", "rule", c.celExpressions[index], "cluster", cluster.GetName(), "err", err)
return false, remainingBudget
}
// Check expression result
if value, ok := evalResult.Value().(bool); !ok || !value {
return false, remainingBudget
}
return true, remainingBudget
}
// costCalculation processes the cost details of an evaluation
func (c *CELSelector) costCalculation(ctx context.Context, evalDetails *cel.EvalDetails, budget int64, index int) (bool, int64) {
logger := klog.FromContext(ctx)
// Check if cost details are available
if evalDetails == nil {
logger.Info("Runtime cost calculation failed: no evaluation details",
"rule", c.celExpressions[index])
return false, -1
}
rtCost := evalDetails.ActualCost()
if rtCost == nil {
logger.Info("Runtime cost calculation failed: no cost information",
"rule", c.celExpressions[index])
return false, -1
}
// Validate cost against budget
if *rtCost > math.MaxInt64 || int64(*rtCost) > budget {
logger.Info("Cost budget exceeded",
"rule", c.celExpressions[index],
"cost", *rtCost,
"budget", budget)
return false, -1
}
// Safe to convert since we checked for overflow
return true, int64(*rtCost) //nolint:gosec
value, ok := evalResult.Value().(bool)
return value && ok
}