1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00

feat: introduce evaluation results in cel engine (#11971)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2025-01-21 17:41:45 +01:00 committed by GitHub
parent 07a23746d8
commit 9d11e8f98c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 64 additions and 45 deletions

View file

@ -2,9 +2,11 @@ package engine
import ( import (
"context" "context"
"fmt"
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
"github.com/kyverno/kyverno/pkg/cel/policy" "github.com/kyverno/kyverno/pkg/cel/policy"
"github.com/kyverno/kyverno/pkg/cel/utils"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers" "github.com/kyverno/kyverno/pkg/engine/handlers"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
@ -72,14 +74,23 @@ func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineRespo
func (e *engine) handlePolicy(ctx context.Context, policy policy.CompiledPolicy, resource *unstructured.Unstructured, namespace *unstructured.Unstructured) PolicyResponse { func (e *engine) handlePolicy(ctx context.Context, policy policy.CompiledPolicy, resource *unstructured.Unstructured, namespace *unstructured.Unstructured) PolicyResponse {
var rules []engineapi.RuleResponse var rules []engineapi.RuleResponse
ok, err := policy.Evaluate(ctx, resource, namespace) results, err := policy.Evaluate(ctx, resource, namespace)
// TODO: evaluation should be per rule // TODO: error is about match conditions here ?
if err != nil { if err != nil {
rules = handlers.WithResponses(engineapi.RuleError("todo", engineapi.Validation, "failed to load context", err, nil)) rules = handlers.WithResponses(engineapi.RuleError("evaluation", engineapi.Validation, "failed to load context", err, nil))
} else if ok {
rules = handlers.WithResponses(engineapi.RulePass("todo", engineapi.Validation, "success", nil))
} else { } else {
rules = handlers.WithResponses(engineapi.RuleFail("todo", engineapi.Validation, "failure", nil)) for index, result := range results {
ruleName := fmt.Sprintf("rule-%d", index)
if result.Error != nil {
rules = append(rules, *engineapi.RuleError(ruleName, engineapi.Validation, "error", err, nil))
} else if result, err := utils.ConvertToNative[bool](result.Result); err != nil {
rules = append(rules, *engineapi.RuleError(ruleName, engineapi.Validation, "conversion error", err, nil))
} else if result {
rules = append(rules, *engineapi.RulePass(ruleName, engineapi.Validation, "success", nil))
} else {
rules = append(rules, *engineapi.RuleFail(ruleName, engineapi.Validation, "failure", nil))
}
}
} }
return PolicyResponse{ return PolicyResponse{
// TODO // TODO

View file

@ -18,8 +18,13 @@ type (
namespace = *unstructured.Unstructured namespace = *unstructured.Unstructured
) )
type EvaluationResult struct {
Result ref.Val
Error error
}
type CompiledPolicy interface { type CompiledPolicy interface {
Evaluate(context.Context, resource, namespace) (bool, error) Evaluate(context.Context, resource, namespace) ([]EvaluationResult, error)
} }
type compiledPolicy struct { type compiledPolicy struct {
@ -30,57 +35,60 @@ type compiledPolicy struct {
auditAnnotations map[string]cel.Program auditAnnotations map[string]cel.Program
} }
func (p *compiledPolicy) Evaluate(ctx context.Context, resource resource, namespace namespace) (bool, error) { func (p *compiledPolicy) Evaluate(ctx context.Context, resource resource, namespace namespace) ([]EvaluationResult, error) {
match, err := p.match(ctx, resource, namespace) match, err := p.match(ctx, resource, namespace)
if err != nil { if err != nil {
return false, err return nil, err
} }
if !match { if !match {
return true, nil return nil, nil
} }
var nsData map[string]any var nsData map[string]any
if namespace != nil { if namespace != nil {
nsData = namespace.UnstructuredContent() nsData = namespace.UnstructuredContent()
} }
variables := func() map[string]any { vars := lazy.NewMapValue(VariablesType)
vars := lazy.NewMapValue(VariablesType) data := map[string]any{
data := map[string]any{ NamespaceObjectKey: nsData,
NamespaceObjectKey: nsData, ObjectKey: resource.UnstructuredContent(),
ObjectKey: resource.UnstructuredContent(), VariablesKey: vars,
VariablesKey: vars,
}
for name, variable := range p.variables {
vars.Append(name, func(*lazy.MapValue) ref.Val {
out, _, err := variable.Eval(data)
if out != nil {
return out
}
if err != nil {
return types.WrapErr(err)
}
return nil
})
}
return data
} }
data := variables() for name, variable := range p.variables {
vars.Append(name, func(*lazy.MapValue) ref.Val {
out, _, err := variable.Eval(data)
if out != nil {
return out
}
if err != nil {
return types.WrapErr(err)
}
return nil
})
}
results := make([]EvaluationResult, 0, len(p.validations))
for _, rule := range p.validations { for _, rule := range p.validations {
out, _, err := rule.Eval(data) out, _, err := rule.Eval(data)
// check error results = append(results, EvaluationResult{
if err != nil { Result: out,
return false, err Error: err,
} })
response, err := utils.ConvertToNative[bool](out) // // check error
// check error // if err != nil {
if err != nil { // results = append(results, EvaluationResult{
return false, err // Error: err,
} // })
// if response is false, return // }
if !response { // response, err := utils.ConvertToNative[bool](out)
return false, nil // // check error
} // if err != nil {
// return false, err
// }
// // if response is false, return
// if !response {
// return false, nil
// }
} }
return true, nil return results, nil
} }
func (p *compiledPolicy) match(ctx context.Context, resource resource, namespace namespace) (bool, error) { func (p *compiledPolicy) match(ctx context.Context, resource resource, namespace namespace) (bool, error) {