1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-20 23:02:36 +00:00

feat: add imagedata cel lib (#12442)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2025-03-19 10:03:23 +01:00 committed by GitHub
parent d3305512d4
commit 2bb687550c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 279 additions and 214 deletions

View file

@ -206,7 +206,7 @@ func (e *engine) matchPolicy(policy CompiledValidatingPolicy, attr admission.Att
return false, -1, nil
}
func (e *engine) handlePolicy(ctx context.Context, policy CompiledValidatingPolicy, jsonPayload interface{}, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context policy.ContextInterface) ValidatingPolicyResponse {
func (e *engine) handlePolicy(ctx context.Context, policy CompiledValidatingPolicy, jsonPayload any, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context policy.ContextInterface) ValidatingPolicyResponse {
response := ValidatingPolicyResponse{
Actions: policy.Actions,
Policy: policy.Policy,

View file

@ -28,41 +28,34 @@ func Test_impl_get_globalreference_string_string(t *testing.T) {
prog, err := env.Program(ast)
assert.NoError(t, err)
assert.NotNil(t, prog)
tests := []struct {
name string
gctxStoreData map[string]store.Entry
expectedValue any
expectedError string
}{
{
name: "global context entry not found",
gctxStoreData: map[string]store.Entry{},
expectedError: "global context entry not found",
}{{
name: "global context entry not found",
gctxStoreData: map[string]store.Entry{},
expectedError: "global context entry not found",
}, {
name: "global context entry returns error",
gctxStoreData: map[string]store.Entry{
"foo": &resource.MockEntry{Err: errors.New("get entry error")},
},
{
name: "global context entry returns error",
gctxStoreData: map[string]store.Entry{
"foo": &resource.MockEntry{Err: errors.New("get entry error")},
},
expectedError: "get entry error",
expectedError: "get entry error",
}, {
name: "global context entry returns string",
gctxStoreData: map[string]store.Entry{
"foo": &resource.MockEntry{Data: "stringValue"},
},
{
name: "global context entry returns string",
gctxStoreData: map[string]store.Entry{
"foo": &resource.MockEntry{Data: "stringValue"},
},
expectedValue: "stringValue",
expectedValue: "stringValue",
}, {
name: "global context entry returns map",
gctxStoreData: map[string]store.Entry{
"foo": &resource.MockEntry{Data: map[string]any{"key": "value"}},
},
{
name: "global context entry returns map",
gctxStoreData: map[string]store.Entry{
"foo": &resource.MockEntry{Data: map[string]interface{}{"key": "value"}},
},
expectedValue: map[string]interface{}{"key": "value"},
},
}
expectedValue: map[string]any{"key": "value"},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockStore := &resource.MockGctxStore{Data: tt.gctxStoreData}
@ -78,7 +71,6 @@ func Test_impl_get_globalreference_string_string(t *testing.T) {
}},
}
out, _, err := prog.Eval(data)
if tt.expectedError != "" {
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedError)

View file

@ -14,25 +14,15 @@ import (
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
type HttpInterface interface {
Get(url string, headers map[string]string) (map[string]any, error)
Post(url string, data map[string]any, headers map[string]string) (map[string]any, error)
Client(caBundle string) (HttpInterface, error)
}
type ClientInterface interface {
type clientInterface interface {
Do(*http.Request) (*http.Response, error)
}
type HTTP struct {
HttpInterface
type httpProvider struct {
client clientInterface
}
type HttpProvider struct {
client ClientInterface
}
func (r *HttpProvider) Get(url string, headers map[string]string) (map[string]any, error) {
func (r *httpProvider) Get(url string, headers map[string]string) (map[string]any, error) {
req, err := http.NewRequestWithContext(context.TODO(), "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
@ -43,12 +33,11 @@ func (r *HttpProvider) Get(url string, headers map[string]string) (map[string]an
return r.executeRequest(r.client, req)
}
func (r *HttpProvider) Post(url string, data map[string]any, headers map[string]string) (map[string]any, error) {
func (r *httpProvider) Post(url string, data map[string]any, headers map[string]string) (map[string]any, error) {
body, err := buildRequestData(data)
if err != nil {
return nil, fmt.Errorf("failed to encode request data: %v", err)
}
req, err := http.NewRequestWithContext(context.TODO(), "POST", url, body)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
@ -59,31 +48,26 @@ func (r *HttpProvider) Post(url string, data map[string]any, headers map[string]
return r.executeRequest(r.client, req)
}
func (r *HttpProvider) executeRequest(client ClientInterface, req *http.Request) (map[string]any, error) {
func (r *httpProvider) executeRequest(client clientInterface, req *http.Request) (map[string]any, error) {
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("HTTP %s", resp.Status)
}
body := make(map[string]any)
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
return nil, fmt.Errorf("Unable to decode JSON body %v", err)
}
return body, nil
}
func (r *HttpProvider) Client(caBundle string) (HttpInterface, error) {
func (r *httpProvider) Client(caBundle string) (HttpInterface, error) {
if caBundle == "" {
return r, nil
}
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM([]byte(caBundle)); !ok {
return nil, fmt.Errorf("failed to parse PEM CA bundle for APICall")
@ -94,7 +78,7 @@ func (r *HttpProvider) Client(caBundle string) (HttpInterface, error) {
MinVersion: tls.VersionTLS12,
},
}
return &HttpProvider{
return &httpProvider{
client: &http.Client{
Transport: tracing.Transport(transport, otelhttp.WithFilter(tracing.RequestFilterIsInSpan)),
},
@ -106,14 +90,5 @@ func buildRequestData(data map[string]any) (io.Reader, error) {
if err := json.NewEncoder(buffer).Encode(data); err != nil {
return nil, fmt.Errorf("failed to encode HTTP POST data %v: %w", data, err)
}
return buffer, nil
}
func NewHTTP() HTTP {
return HTTP{
HttpInterface: &HttpProvider{
client: http.DefaultClient,
},
}
}

View file

@ -55,7 +55,7 @@ func Test_impl_get_request(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, prog)
out, _, err := prog.Eval(map[string]any{
"http": HTTP{&HttpProvider{
"http": HTTP{&httpProvider{
client: testClient{
doFunc: func(req *http.Request) (*http.Response, error) {
assert.Equal(t, req.URL.String(), "http://localhost:8080")
@ -90,7 +90,7 @@ func Test_impl_get_request_with_headers(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, prog)
out, _, err := prog.Eval(map[string]any{
"http": HTTP{&HttpProvider{
"http": HTTP{&httpProvider{
client: testClient{
doFunc: func(req *http.Request) (*http.Response, error) {
assert.Equal(t, req.URL.String(), "http://localhost:8080")
@ -126,7 +126,7 @@ func Test_impl_post_request(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, prog)
out, _, err := prog.Eval(map[string]any{
"http": HTTP{&HttpProvider{
"http": HTTP{&httpProvider{
client: testClient{
doFunc: func(req *http.Request) (*http.Response, error) {
assert.Equal(t, req.URL.String(), "http://localhost:8080")
@ -165,7 +165,7 @@ func Test_impl_post_request_with_headers(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, prog)
out, _, err := prog.Eval(map[string]any{
"http": HTTP{&HttpProvider{
"http": HTTP{&httpProvider{
client: testClient{
doFunc: func(req *http.Request) (*http.Response, error) {
assert.Equal(t, req.URL.String(), "http://localhost:8080")
@ -207,9 +207,9 @@ func Test_impl_http_client_string(t *testing.T) {
assert.NotNil(t, prog)
out, _, err := prog.Eval(map[string]any{
"pem": pemExample,
"http": HTTP{&HttpProvider{}},
"http": HTTP{&httpProvider{}},
})
assert.NoError(t, err)
reqProvider := out.Value().(*HttpProvider)
reqProvider := out.Value().(*httpProvider)
assert.NotNil(t, reqProvider)
}

View file

@ -12,8 +12,6 @@ import (
const libraryName = "kyverno.http"
var HTTPType = types.DynType
type lib struct{}
func Lib() cel.EnvOption {

View file

@ -0,0 +1,27 @@
package http
import (
"net/http"
"github.com/google/cel-go/common/types"
)
var HTTPType = types.NewOpaqueType("http.HTTP")
type HttpInterface interface {
Get(url string, headers map[string]string) (map[string]any, error)
Post(url string, data map[string]any, headers map[string]string) (map[string]any, error)
Client(caBundle string) (HttpInterface, error)
}
type HTTP struct {
HttpInterface
}
func NewHTTP() HTTP {
return HTTP{
HttpInterface: &httpProvider{
client: http.DefaultClient,
},
}
}

View file

@ -7,6 +7,8 @@ import (
"github.com/google/go-containerregistry/pkg/name"
)
const libraryName = "kyverno.image"
func ImageLib() cel.EnvOption {
return cel.Lib(imageLib)
}
@ -16,7 +18,7 @@ var imageLib = &imageLibType{}
type imageLibType struct{}
func (*imageLibType) LibraryName() string {
return "kyverno.Image"
return libraryName
}
func (*imageLibType) Types() []*cel.Type {

View file

@ -66,7 +66,7 @@ func testImageLib(t *testing.T, expr string, expectResult ref.Val, expectRuntime
if err != nil {
t.Fatalf("%v", err)
}
res, _, err := prog.Eval(map[string]interface{}{})
res, _, err := prog.Eval(map[string]any{})
if len(expectRuntimeErrPattern) > 0 {
if err == nil {
t.Fatalf("no runtime error thrown. Expected: %v", expectRuntimeErrPattern)

View file

@ -24,7 +24,7 @@ type Image struct {
ImageReference
}
func (v Image) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
func (v Image) ConvertToNative(typeDesc reflect.Type) (any, error) {
if reflect.TypeOf(v.ImageReference).AssignableTo(typeDesc) {
return v.ImageReference, nil
}
@ -55,6 +55,6 @@ func (v Image) Type() ref.Type {
return ImageType
}
func (v Image) Value() interface{} {
func (v Image) Value() any {
return v.ImageReference
}

View file

@ -0,0 +1,26 @@
package imagedata
import (
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/kyverno/kyverno/pkg/cel/utils"
)
type impl struct {
types.Adapter
}
func (c *impl) get_imagedata_string(ctx ref.Val, image ref.Val) ref.Val {
if self, err := utils.ConvertToNative[Context](ctx); err != nil {
return types.WrapErr(err)
} else if image, err := utils.ConvertToNative[string](image); err != nil {
return types.WrapErr(err)
} else {
globalRef, err := self.GetImageData(image)
if err != nil {
// Errors are not expected here since Parse is a more lenient parser than ParseRequestURI.
return types.NewErr("failed to get image data: %v", err)
}
return c.NativeToValue(globalRef)
}
}

View file

@ -0,0 +1,58 @@
package imagedata
import (
"context"
"encoding/json"
"strings"
"testing"
"github.com/google/cel-go/cel"
"github.com/kyverno/kyverno/pkg/cel/libs/resource"
"github.com/kyverno/kyverno/pkg/imageverification/imagedataloader"
"github.com/stretchr/testify/assert"
)
func Test_impl_get_imagedata_string(t *testing.T) {
opts := Lib()
base, err := cel.NewEnv(opts)
assert.NoError(t, err)
assert.NotNil(t, base)
options := []cel.EnvOption{
cel.Variable("imagedata", ContextType),
}
env, err := base.Extend(options...)
assert.NoError(t, err)
assert.NotNil(t, env)
ast, issues := env.Compile(`imagedata.Get("ghcr.io/kyverno/kyverno:latest").resolvedImage`)
assert.Nil(t, issues)
assert.NotNil(t, ast)
prog, err := env.Program(ast)
assert.NoError(t, err)
assert.NotNil(t, prog)
data := map[string]any{
"imagedata": Context{&resource.MockCtx{
GetImageDataFunc: func(image string) (map[string]any, error) {
idl, err := imagedataloader.New(nil)
assert.NoError(t, err)
data, err := idl.FetchImageData(context.TODO(), image)
if err != nil {
return nil, err
}
raw, err := json.Marshal(data.Data())
if err != nil {
return nil, err
}
var apiData map[string]any
err = json.Unmarshal(raw, &apiData)
if err != nil {
return nil, err
}
return apiData, nil
},
},
}}
out, _, err := prog.Eval(data)
assert.NoError(t, err)
resolvedImg := out.Value().(string)
assert.True(t, strings.HasPrefix(resolvedImg, "ghcr.io/kyverno/kyverno:latest@sha256:"))
}

View file

@ -0,0 +1,58 @@
package imagedata
import (
"reflect"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/ext"
)
const libraryName = "kyverno.imagedata"
type lib struct{}
func Lib() cel.EnvOption {
// create the cel lib env option
return cel.Lib(&lib{})
}
func (*lib) LibraryName() string {
return libraryName
}
func (c *lib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{
ext.NativeTypes(reflect.TypeFor[Context]()),
c.extendEnv,
}
}
func (*lib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func (c *lib) extendEnv(env *cel.Env) (*cel.Env, error) {
// create implementation, recording the envoy types aware adapter
impl := impl{
Adapter: env.CELTypeAdapter(),
}
// build our function overloads
libraryDecls := map[string][]cel.FunctionOpt{
"Get": {
cel.MemberOverload(
"imagedata_get_string",
[]*cel.Type{ContextType, types.StringType},
types.DynType,
cel.BinaryBinding(impl.get_imagedata_string),
),
},
}
// create env options corresponding to our function overloads
options := []cel.EnvOption{}
for name, overloads := range libraryDecls {
options = append(options, cel.Function(name, overloads...))
}
// extend environment with our function overloads
return env.Extend(options...)
}

View file

@ -0,0 +1,20 @@
package imagedata
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

@ -0,0 +1,15 @@
package imagedata
import (
"github.com/google/cel-go/common/types"
)
var ContextType = types.NewOpaqueType("imagedata.Context")
type ContextInterface interface {
GetImageData(string) (map[string]any, error)
}
type Context struct {
ContextInterface
}

View file

@ -10,21 +10,6 @@ type impl struct {
types.Adapter
}
func (c *impl) get_imagedata_string(ctx ref.Val, image ref.Val) ref.Val {
if self, err := utils.ConvertToNative[Context](ctx); err != nil {
return types.WrapErr(err)
} else if image, err := utils.ConvertToNative[string](image); err != nil {
return types.WrapErr(err)
} else {
globalRef, err := self.GetImageData(image)
if err != nil {
// Errors are not expected here since Parse is a more lenient parser than ParseRequestURI.
return types.NewErr("failed to get image data: %v", err)
}
return c.NativeToValue(globalRef)
}
}
func (c *impl) list_resources_string_string_string(args ...ref.Val) ref.Val {
if self, err := utils.ConvertToNative[Context](args[0]); err != nil {
return types.WrapErr(err)

View file

@ -1,62 +1,13 @@
package resource
import (
"context"
"encoding/json"
"strings"
"testing"
"github.com/google/cel-go/cel"
"github.com/kyverno/kyverno/pkg/imageverification/imagedataloader"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func Test_impl_get_imagedata_string(t *testing.T) {
opts := Lib()
base, err := cel.NewEnv(opts)
assert.NoError(t, err)
assert.NotNil(t, base)
options := []cel.EnvOption{
cel.Variable("resource", ContextType),
}
env, err := base.Extend(options...)
assert.NoError(t, err)
assert.NotNil(t, env)
ast, issues := env.Compile(`resource.GetImageData("ghcr.io/kyverno/kyverno:latest").resolvedImage`)
assert.Nil(t, issues)
assert.NotNil(t, ast)
prog, err := env.Program(ast)
assert.NoError(t, err)
assert.NotNil(t, prog)
data := map[string]any{
"resource": Context{&MockCtx{
GetImageDataFunc: func(image string) (map[string]interface{}, error) {
idl, err := imagedataloader.New(nil)
assert.NoError(t, err)
data, err := idl.FetchImageData(context.TODO(), image)
if err != nil {
return nil, err
}
raw, err := json.Marshal(data.Data())
if err != nil {
return nil, err
}
apiData := map[string]interface{}{}
err = json.Unmarshal(raw, &apiData)
if err != nil {
return nil, err
}
return apiData, nil
},
},
}}
out, _, err := prog.Eval(data)
assert.NoError(t, err)
resolvedImg := out.Value().(string)
assert.True(t, strings.HasPrefix(resolvedImg, "ghcr.io/kyverno/kyverno:latest@sha256:"))
}
func Test_impl_get_resource_string_string_string_string(t *testing.T) {
opts := Lib()
base, err := cel.NewEnv(opts)

View file

@ -6,7 +6,6 @@ import (
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/ext"
apiservercel "k8s.io/apiserver/pkg/cel"
)
const libraryName = "kyverno.resource"
@ -18,12 +17,6 @@ func Lib() cel.EnvOption {
return cel.Lib(&lib{})
}
func Types() []*apiservercel.DeclType {
return []*apiservercel.DeclType{
imageDataType,
}
}
func (*lib) LibraryName() string {
return libraryName
}
@ -64,14 +57,6 @@ func (c *lib) extendEnv(env *cel.Env) (*cel.Env, error) {
cel.FunctionBinding(impl.get_resource_string_string_string_string),
),
},
"GetImageData": {
cel.MemberOverload(
"resource_getimagedata_string",
[]*cel.Type{ContextType, types.StringType},
types.DynType,
cel.BinaryBinding(impl.get_imagedata_string),
),
},
}
// create env options corresponding to our function overloads
options := []cel.EnvOption{}

View file

@ -8,7 +8,7 @@ import (
// MOCK FOR TESTING
type MockCtx struct {
GetGlobalReferenceFunc func(string, string) (any, error)
GetImageDataFunc func(string) (map[string]interface{}, error)
GetImageDataFunc func(string) (map[string]any, error)
ListResourcesFunc func(string, string, string) (*unstructured.UnstructuredList, error)
GetResourceFunc func(string, string, string, string) (*unstructured.Unstructured, error)
}
@ -17,7 +17,7 @@ func (mock *MockCtx) GetGlobalReference(n, p string) (any, error) {
return mock.GetGlobalReferenceFunc(n, p)
}
func (mock *MockCtx) GetImageData(n string) (map[string]interface{}, error) {
func (mock *MockCtx) GetImageData(n string) (map[string]any, error) {
return mock.GetImageDataFunc(n)
}

View file

@ -3,16 +3,11 @@ package resource
import (
"github.com/google/cel-go/common/types"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apiservercel "k8s.io/apiserver/pkg/cel"
)
var (
ContextType = types.NewOpaqueType("resource.Context")
imageDataType = BuildImageDataType()
)
var ContextType = types.NewOpaqueType("resource.Context")
type ContextInterface interface {
GetImageData(string) (map[string]interface{}, error)
ListResources(apiVersion, resource, namespace string) (*unstructured.UnstructuredList, error)
GetResource(apiVersion, resource, namespace, name string) (*unstructured.Unstructured, error)
}
@ -20,31 +15,3 @@ type ContextInterface interface {
type Context struct {
ContextInterface
}
func BuildImageDataType() *apiservercel.DeclType {
f := make([]*apiservercel.DeclField, 0)
f = append(f,
field("image", apiservercel.StringType, true),
field("resolvedImage", apiservercel.StringType, true),
field("registry", apiservercel.StringType, true),
field("repository", apiservercel.StringType, true),
field("tag", apiservercel.StringType, false),
field("digest", apiservercel.StringType, false),
field("imageIndex", apiservercel.DynType, false),
field("manifest", apiservercel.DynType, true),
field("config", apiservercel.DynType, true),
)
return apiservercel.NewObjectType("imageData", fields(f...))
}
func field(name string, declType *apiservercel.DeclType, required bool) *apiservercel.DeclField {
return apiservercel.NewDeclField(name, declType, required, nil, nil)
}
func fields(fields ...*apiservercel.DeclField) map[string]*apiservercel.DeclField {
result := make(map[string]*apiservercel.DeclField, len(fields))
for _, f := range fields {
result[f.Name] = f
}
return result
}

View file

@ -10,6 +10,7 @@ import (
vpolautogen "github.com/kyverno/kyverno/pkg/cel/autogen"
"github.com/kyverno/kyverno/pkg/cel/libs/globalcontext"
"github.com/kyverno/kyverno/pkg/cel/libs/http"
"github.com/kyverno/kyverno/pkg/cel/libs/imagedata"
"github.com/kyverno/kyverno/pkg/cel/libs/resource"
"github.com/kyverno/kyverno/pkg/cel/libs/user"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
@ -20,6 +21,7 @@ import (
const (
GlobalContextKey = "globalcontext"
HttpKey = "http"
ImageDataKey = "imagedata"
NamespaceObjectKey = "namespaceObject"
ObjectKey = "object"
OldObjectKey = "oldObject"
@ -110,10 +112,10 @@ func (c *compiler) compileForKubernetes(policy *policiesv1alpha1.ValidatingPolic
}
var declTypes []*apiservercel.DeclType
declTypes = append(declTypes, NamespaceType, RequestType)
declTypes = append(declTypes, resource.Types()...)
options := []cel.EnvOption{
cel.Variable(GlobalContextKey, globalcontext.ContextType),
cel.Variable(HttpKey, http.HTTPType),
cel.Variable(ImageDataKey, imagedata.ContextType),
cel.Variable(NamespaceObjectKey, NamespaceType.CelType()),
cel.Variable(ObjectKey, cel.DynType),
cel.Variable(OldObjectKey, cel.DynType),
@ -132,7 +134,7 @@ func (c *compiler) compileForKubernetes(policy *policiesv1alpha1.ValidatingPolic
panic(err)
}
options = append(options, declOptions...)
options = append(options, resource.Lib(), http.Lib(), user.Lib())
options = append(options, globalcontext.Lib(), http.Lib(), imagedata.Lib(), resource.Lib(), user.Lib())
// TODO: params, authorizer, authorizer.requestResource ?
env, err := base.Extend(options...)
if err != nil {

View file

@ -66,7 +66,7 @@ func (cp *contextProvider) GetGlobalReference(name, projection string) (any, err
if err != nil {
return nil, err
}
apiData := map[string]interface{}{}
apiData := map[string]any{}
err = json.Unmarshal(raw, &apiData)
if err != nil {
return nil, err
@ -75,7 +75,7 @@ func (cp *contextProvider) GetGlobalReference(name, projection string) (any, err
}
}
func (cp *contextProvider) GetImageData(image string) (map[string]interface{}, error) {
func (cp *contextProvider) GetImageData(image string) (map[string]any, error) {
// TODO: get image credentials from image verification policies?
data, err := cp.imagedata.FetchImageData(context.TODO(), image)
if err != nil {
@ -89,7 +89,7 @@ func isLikelyKubernetesObject(data any) bool {
return false
}
if m, ok := data.(map[string]interface{}); ok {
if m, ok := data.(map[string]any); ok {
_, hasAPIVersion := m["apiVersion"]
_, hasKind := m["kind"]
return hasAPIVersion && hasKind

View file

@ -43,7 +43,7 @@ func (cp *FakeContextProvider) GetGlobalReference(string, string) (any, error) {
panic("not implemented")
}
func (cp *FakeContextProvider) GetImageData(string) (map[string]interface{}, error) {
func (cp *FakeContextProvider) GetImageData(string) (map[string]any, error) {
panic("not implemented")
}

View file

@ -9,9 +9,10 @@ import (
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
policiesv1alpha1 "github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1"
globalcontextlib "github.com/kyverno/kyverno/pkg/cel/libs/globalcontext"
"github.com/kyverno/kyverno/pkg/cel/libs/globalcontext"
"github.com/kyverno/kyverno/pkg/cel/libs/http"
resourcelib "github.com/kyverno/kyverno/pkg/cel/libs/resource"
"github.com/kyverno/kyverno/pkg/cel/libs/imagedata"
"github.com/kyverno/kyverno/pkg/cel/libs/resource"
"github.com/kyverno/kyverno/pkg/cel/utils"
"go.uber.org/multierr"
admissionv1 "k8s.io/api/admission/v1"
@ -33,12 +34,13 @@ type EvaluationResult struct {
}
type ContextInterface interface {
resourcelib.ContextInterface
globalcontextlib.ContextInterface
globalcontext.ContextInterface
imagedata.ContextInterface
resource.ContextInterface
}
type CompiledPolicy interface {
Evaluate(context.Context, interface{}, admission.Attributes, *admissionv1.AdmissionRequest, runtime.Object, ContextInterface, int) (*EvaluationResult, error)
Evaluate(context.Context, any, admission.Attributes, *admissionv1.AdmissionRequest, runtime.Object, ContextInterface, int) (*EvaluationResult, error)
}
type CompiledValidation struct {
@ -71,17 +73,17 @@ type compiledPolicy struct {
}
type evaluationData struct {
Namespace interface{}
Object interface{}
OldObject interface{}
Request interface{}
Namespace any
Object any
OldObject any
Request any
Context ContextInterface
Variables *lazy.MapValue
}
func (p *compiledPolicy) Evaluate(
ctx context.Context,
json interface{},
json any,
attr admission.Attributes,
request *admissionv1.AdmissionRequest,
namespace runtime.Object,
@ -98,7 +100,7 @@ func (p *compiledPolicy) Evaluate(
func (p *compiledPolicy) evaluateJson(
ctx context.Context,
json interface{},
json any,
) (*EvaluationResult, error) {
data := evaluationData{
Object: json,
@ -168,13 +170,14 @@ func (p *compiledPolicy) evaluateWithData(
vars := lazy.NewMapValue(VariablesType)
dataNew := map[string]any{
ResourceKey: resourcelib.Context{ContextInterface: data.Context},
GlobalContextKey: globalcontextlib.Context{ContextInterface: data.Context},
GlobalContextKey: globalcontext.Context{ContextInterface: data.Context},
HttpKey: http.NewHTTP(),
ImageDataKey: imagedata.Context{ContextInterface: data.Context},
NamespaceObjectKey: data.Namespace,
ObjectKey: data.Object,
OldObjectKey: data.OldObject,
RequestKey: data.Request,
ResourceKey: resource.Context{ContextInterface: data.Context},
VariablesKey: vars,
}
for name, variable := range variables {
@ -313,7 +316,7 @@ func (p *compiledPolicy) match(
}
}
func convertObjectToUnstructured(obj interface{}) (*unstructured.Unstructured, error) {
func convertObjectToUnstructured(obj any) (*unstructured.Unstructured, error) {
if obj == nil || reflect.ValueOf(obj).IsNil() {
return &unstructured.Unstructured{Object: nil}, nil
}
@ -324,7 +327,7 @@ func convertObjectToUnstructured(obj interface{}) (*unstructured.Unstructured, e
return &unstructured.Unstructured{Object: ret}, nil
}
func objectToResolveVal(r runtime.Object) (interface{}, error) {
func objectToResolveVal(r runtime.Object) (any, error) {
if r == nil || reflect.ValueOf(r).IsNil() {
return nil, nil
}

View file

@ -2,12 +2,12 @@ package policy
import "encoding/json"
func getValue(data any) (map[string]interface{}, error) {
func getValue(data any) (map[string]any, error) {
raw, err := json.Marshal(data)
if err != nil {
return nil, err
}
apiData := map[string]interface{}{}
apiData := map[string]any{}
err = json.Unmarshal(raw, &apiData)
if err != nil {
return nil, err

View file

@ -6,6 +6,7 @@ import (
engine "github.com/kyverno/kyverno/pkg/cel"
"github.com/kyverno/kyverno/pkg/cel/libs/globalcontext"
"github.com/kyverno/kyverno/pkg/cel/libs/http"
"github.com/kyverno/kyverno/pkg/cel/libs/imagedata"
"github.com/kyverno/kyverno/pkg/cel/libs/imageverify"
"github.com/kyverno/kyverno/pkg/cel/libs/resource"
"github.com/kyverno/kyverno/pkg/cel/libs/user"
@ -79,7 +80,7 @@ func (c *compiler) Compile(ivpolicy *policiesv1alpha1.ImageVerificationPolicy) (
for _, declType := range declTypes {
options = append(options, cel.Types(declType.CelType()))
}
options = append(options, imageverify.Lib(c.ictx, ivpolicy, c.lister), resource.Lib(), http.Lib(), user.Lib())
options = append(options, globalcontext.Lib(), http.Lib(), imagedata.Lib(), imageverify.Lib(c.ictx, ivpolicy, c.lister), resource.Lib(), user.Lib())
env, err := base.Extend(options...)
if err != nil {
return nil, append(allErrs, field.InternalError(nil, err))

View file

@ -12,7 +12,7 @@ spec:
variables:
- name: image
expression: >-
resource.GetImageData("ghcr.io/kyverno/kyverno:latest")
imagedata.Get("ghcr.io/kyverno/kyverno:latest")
- name: accept
expression: >-
variables.image != null