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

fix: implement cel context lib correctly (#11983)

* fix: implement cel context lib correctly

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* more changes

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2025-01-23 12:02:33 +01:00 committed by GitHub
parent e481ec4231
commit 144bf436ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 92 additions and 6 deletions

View file

@ -74,7 +74,7 @@ func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineRespo
func (e *engine) handlePolicy(ctx context.Context, policy CompiledPolicy, resource *unstructured.Unstructured, namespace *unstructured.Unstructured) PolicyResponse { func (e *engine) handlePolicy(ctx context.Context, policy CompiledPolicy, resource *unstructured.Unstructured, namespace *unstructured.Unstructured) PolicyResponse {
var rules []engineapi.RuleResponse var rules []engineapi.RuleResponse
results, err := policy.CompiledPolicy.Evaluate(ctx, resource, namespace) results, err := policy.CompiledPolicy.Evaluate(ctx, resource, namespace, nil)
// TODO: error is about match conditions here ? // TODO: error is about match conditions here ?
if err != nil { if err != nil {
rules = handlers.WithResponses(engineapi.RuleError("evaluation", engineapi.Validation, "failed to load context", err, nil)) rules = handlers.WithResponses(engineapi.RuleError("evaluation", engineapi.Validation, "failed to load context", err, nil))

View file

@ -0,0 +1,49 @@
package context
import (
"testing"
"github.com/google/cel-go/cel"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
type ctx struct {
GetConfigMapFunc func(string, string) (unstructured.Unstructured, error)
}
func (mock *ctx) GetConfigMap(ns string, n string) (unstructured.Unstructured, error) {
return mock.GetConfigMapFunc(ns, n)
}
func Test_impl_get_configmap_string_string(t *testing.T) {
opts := Lib()
base, err := cel.NewEnv(opts)
assert.NoError(t, err)
assert.NotNil(t, base)
options := []cel.EnvOption{
cel.Variable("context", ContextType),
}
env, err := base.Extend(options...)
assert.NoError(t, err)
assert.NotNil(t, env)
ast, issues := env.Compile(`context.GetConfigMap("foo","bar")`)
assert.Nil(t, issues)
assert.NotNil(t, ast)
prog, err := env.Program(ast)
assert.NoError(t, err)
assert.NotNil(t, prog)
called := false
data := map[string]any{
"context": Context{&ctx{
GetConfigMapFunc: func(string, string) (unstructured.Unstructured, error) {
called = true
return unstructured.Unstructured{}, nil
},
}},
}
out, _, err := prog.Eval(data)
assert.NoError(t, err)
assert.NotNil(t, out)
assert.True(t, called)
}

View file

@ -1,10 +1,15 @@
package context package context
import ( import (
"reflect"
"github.com/google/cel-go/cel" "github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types"
"github.com/google/cel-go/ext"
) )
const libraryName = "kyverno.context"
type lib struct{} type lib struct{}
func Lib() cel.EnvOption { func Lib() cel.EnvOption {
@ -13,11 +18,12 @@ func Lib() cel.EnvOption {
} }
func (*lib) LibraryName() string { func (*lib) LibraryName() string {
return "kyverno.context" return libraryName
} }
func (c *lib) CompileOptions() []cel.EnvOption { func (c *lib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{ return []cel.EnvOption{
ext.NativeTypes(reflect.TypeFor[Context]()),
c.extendEnv, c.extendEnv,
} }
} }

View file

@ -0,0 +1,20 @@
package context
import (
"testing"
"github.com/google/cel-go/cel"
"github.com/stretchr/testify/assert"
)
func TestLib(t *testing.T) {
opts := Lib()
env, err := cel.NewEnv(opts)
assert.NoError(t, err)
assert.NotNil(t, env)
}
func Test_lib_LibraryName(t *testing.T) {
var l lib
assert.Equal(t, libraryName, l.LibraryName())
}

View file

@ -5,8 +5,12 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
var ContextType = types.NewObjectType("context.Context") var ContextType = types.NewOpaqueType("context.Context")
type Context interface { type ContextInterface interface {
GetConfigMap(string, string) (unstructured.Unstructured, error) GetConfigMap(string, string) (unstructured.Unstructured, error)
} }
type Context struct {
ContextInterface
}

View file

@ -6,6 +6,7 @@ import (
"github.com/google/cel-go/cel" "github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/ref"
contextlib "github.com/kyverno/kyverno/pkg/cel/libs/context"
"github.com/kyverno/kyverno/pkg/cel/utils" "github.com/kyverno/kyverno/pkg/cel/utils"
"go.uber.org/multierr" "go.uber.org/multierr"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1" admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
@ -24,7 +25,7 @@ type EvaluationResult struct {
} }
type CompiledPolicy interface { type CompiledPolicy interface {
Evaluate(context.Context, resource, namespace) ([]EvaluationResult, error) Evaluate(context.Context, resource, namespace, contextlib.ContextInterface) ([]EvaluationResult, error)
} }
type compiledPolicy struct { type compiledPolicy struct {
@ -35,7 +36,12 @@ type compiledPolicy struct {
auditAnnotations map[string]cel.Program auditAnnotations map[string]cel.Program
} }
func (p *compiledPolicy) Evaluate(ctx context.Context, resource resource, namespace namespace) ([]EvaluationResult, error) { func (p *compiledPolicy) Evaluate(
ctx context.Context,
resource resource,
namespace namespace,
context contextlib.ContextInterface,
) ([]EvaluationResult, error) {
match, err := p.match(ctx, resource, namespace) match, err := p.match(ctx, resource, namespace)
if err != nil { if err != nil {
return nil, err return nil, err
@ -52,6 +58,7 @@ func (p *compiledPolicy) Evaluate(ctx context.Context, resource resource, namesp
NamespaceObjectKey: nsData, NamespaceObjectKey: nsData,
ObjectKey: resource.UnstructuredContent(), ObjectKey: resource.UnstructuredContent(),
VariablesKey: vars, VariablesKey: vars,
ContextKey: contextlib.Context{ContextInterface: context},
} }
for name, variable := range p.variables { for name, variable := range p.variables {
vars.Append(name, func(*lazy.MapValue) ref.Val { vars.Append(name, func(*lazy.MapValue) ref.Val {