1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 09:26:54 +00:00
kyverno/pkg/cel/policy/policy.go
Charles-Edouard Brétéché 54a8a53205
feat: add validating policy engine api wrapper (#11963)
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
2025-01-21 15:59:34 +08:00

117 lines
2.8 KiB
Go

package policy
import (
"context"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/kyverno/kyverno/pkg/cel/utils"
"go.uber.org/multierr"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apiserver/pkg/cel/lazy"
)
type (
resource = *unstructured.Unstructured
namespace = *unstructured.Unstructured
)
type CompiledPolicy interface {
Evaluate(context.Context, resource, namespace) (bool, error)
}
type compiledPolicy struct {
failurePolicy admissionregistrationv1.FailurePolicyType
matchConditions []cel.Program
variables map[string]cel.Program
validations []cel.Program
auditAnnotations map[string]cel.Program
}
func (p *compiledPolicy) Evaluate(ctx context.Context, resource resource, namespace namespace) (bool, error) {
match, err := p.match(ctx, resource, namespace)
if err != nil {
return false, err
}
if !match {
return true, nil
}
var nsData map[string]any
if namespace != nil {
nsData = namespace.UnstructuredContent()
}
variables := func() map[string]any {
vars := lazy.NewMapValue(VariablesType)
data := map[string]any{
NamespaceObjectKey: nsData,
ObjectKey: resource.UnstructuredContent(),
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 _, rule := range p.validations {
out, _, err := rule.Eval(data)
// check error
if err != nil {
return false, err
}
response, err := utils.ConvertToNative[bool](out)
// check error
if err != nil {
return false, err
}
// if response is false, return
if !response {
return false, nil
}
}
return true, nil
}
func (p *compiledPolicy) match(ctx context.Context, resource resource, namespace namespace) (bool, error) {
var nsData map[string]any
if namespace != nil {
nsData = namespace.UnstructuredContent()
}
data := map[string]any{
NamespaceObjectKey: nsData,
ObjectKey: resource.UnstructuredContent(),
}
var errs []error
for _, matchCondition := range p.matchConditions {
// evaluate the condition
out, _, err := matchCondition.ContextEval(ctx, data)
// check error
if err != nil {
errs = append(errs, err)
continue
}
// try to convert to a bool
result, err := utils.ConvertToNative[bool](out)
// check error
if err != nil {
errs = append(errs, err)
continue
}
// if condition is false, skip
if !result {
return false, nil
}
}
return true, multierr.Combine(errs...)
}