1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 23:46:56 +00:00
kyverno/pkg/engine/variables/evaluate.go
Jim Bugwadia 07be2d9d72
lazy evaluate vars in conditions (#7238)
* lazy evaluate vars in conditions

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* remove unnecessary conversion

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* fix test

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* Update test/conformance/kuttl/validate/clusterpolicy/standard/variables/lazyload/conditions/03-manifests.yaml

Signed-off-by: shuting <shutting06@gmail.com>

* Update test/conformance/kuttl/validate/clusterpolicy/standard/variables/lazyload/README.md

Signed-off-by: shuting <shutting06@gmail.com>

* added error check in test

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

---------

Signed-off-by: Jim Bugwadia <jim@nirmata.com>
Signed-off-by: shuting <shutting06@gmail.com>
Co-authored-by: shuting <shutting06@gmail.com>
Co-authored-by: kyverno-bot <104836976+kyverno-bot@users.noreply.github.com>
2023-05-20 21:06:54 +00:00

119 lines
4.4 KiB
Go

package variables
import (
"fmt"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/variables/operator"
stringutils "github.com/kyverno/kyverno/pkg/utils/strings"
)
// Evaluate evaluates the condition
func Evaluate(logger logr.Logger, ctx context.EvalInterface, condition kyvernov1.Condition) (bool, string, error) {
key, err := SubstituteAllInPreconditions(logger, ctx, condition.GetKey())
if err != nil {
return false, "", fmt.Errorf("failed to substitute variables in condition key: %w", err)
}
value, err := SubstituteAllInPreconditions(logger, ctx, condition.GetValue())
if err != nil {
return false, "", fmt.Errorf("failed to substitute variables in condition value: %w", err)
}
handler := operator.CreateOperatorHandler(logger, ctx, condition.Operator)
if handler == nil {
return false, "", fmt.Errorf("failed to create handler for condition operator: %w", err)
}
return handler.Evaluate(key, value), condition.Message, nil
}
// EvaluateConditions evaluates all the conditions present in a slice, in a backwards compatible way
func EvaluateConditions(log logr.Logger, ctx context.EvalInterface, conditions interface{}) (bool, string, error) {
switch typedConditions := conditions.(type) {
case kyvernov1.AnyAllConditions:
return evaluateAnyAllConditions(log, ctx, typedConditions)
case []kyvernov1.Condition: // backwards compatibility
return evaluateOldConditions(log, ctx, typedConditions)
}
return false, "", fmt.Errorf("invalid condition")
}
func EvaluateAnyAllConditions(log logr.Logger, ctx context.EvalInterface, conditions []kyvernov1.AnyAllConditions) (bool, string, error) {
var conditionTrueMessages []string
for _, c := range conditions {
if val, msg, err := evaluateAnyAllConditions(log, ctx, c); err != nil {
return false, "", err
} else if !val {
return false, msg, nil
} else {
conditionTrueMessages = append(conditionTrueMessages, msg)
}
}
return true, stringutils.JoinNonEmpty(conditionTrueMessages, ";"), nil
}
// evaluateAnyAllConditions evaluates multiple conditions as a logical AND (all) or OR (any) operation depending on the conditions
func evaluateAnyAllConditions(log logr.Logger, ctx context.EvalInterface, conditions kyvernov1.AnyAllConditions) (bool, string, error) {
anyConditions, allConditions := conditions.AnyConditions, conditions.AllConditions
anyConditionsResult, allConditionsResult := true, true
var conditionFalseMessages []string
var conditionTrueMessages []string
// update the anyConditionsResult if they are present
if anyConditions != nil {
anyConditionsResult = false
for _, condition := range anyConditions {
if val, msg, err := Evaluate(log, ctx, condition); err != nil {
return false, "", err
} else if val {
anyConditionsResult = true
conditionTrueMessages = append(conditionTrueMessages, msg)
break
} else {
conditionFalseMessages = append(conditionFalseMessages, msg)
}
}
if !anyConditionsResult {
log.V(3).Info("no condition passed for 'any' block", "any", anyConditions)
}
}
// update the allConditionsResult if they are present
for _, condition := range allConditions {
if val, msg, err := Evaluate(log, ctx, condition); err != nil {
return false, "", err
} else if !val {
allConditionsResult = false
conditionFalseMessages = append(conditionFalseMessages, msg)
log.V(3).Info("a condition failed in 'all' block", "condition", condition, "message", msg)
break
} else {
conditionTrueMessages = append(conditionTrueMessages, msg)
}
}
finalResult := anyConditionsResult && allConditionsResult
if finalResult {
return finalResult, stringutils.JoinNonEmpty(conditionTrueMessages, "; "), nil
}
return finalResult, stringutils.JoinNonEmpty(conditionFalseMessages, "; "), nil
}
// evaluateOldConditions evaluates multiple conditions when those conditions are provided in the old manner i.e. without 'any' or 'all'
func evaluateOldConditions(log logr.Logger, ctx context.EvalInterface, conditions []kyvernov1.Condition) (bool, string, error) {
var conditionTrueMessages []string
for _, condition := range conditions {
if val, msg, err := Evaluate(log, ctx, condition); err != nil {
return false, "", err
} else if !val {
return false, msg, nil
} else {
conditionTrueMessages = append(conditionTrueMessages, msg)
}
}
return true, stringutils.JoinNonEmpty(conditionTrueMessages, ";"), nil
}