mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-25 00:58:48 +00:00
feat: add parse image reference function (#12317)
Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
43ddc8c31e
commit
4b4e6cc415
6 changed files with 89 additions and 9 deletions
pkg
cel
imageverification/imagedataloader
|
@ -62,6 +62,21 @@ func (c *impl) get_imagedata_string(ctx ref.Val, image ref.Val) ref.Val {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *impl) parse_imagereference_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 {
|
||||
parsedRef, err := self.ParseImageReference(image)
|
||||
if err != nil {
|
||||
// Errors are not expected here since Parse is a more lenient parser than ParseRequestURI.
|
||||
return types.NewErr("failed to parse image data: %v", err)
|
||||
}
|
||||
return c.NativeToValue(parsedRef)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *impl) list_resource_string(args ...ref.Val) ref.Val {
|
||||
if self, err := utils.ConvertToNative[Context](args[0]); err != nil {
|
||||
return types.WrapErr(err)
|
||||
|
|
|
@ -14,11 +14,12 @@ import (
|
|||
)
|
||||
|
||||
type ctx struct {
|
||||
GetConfigMapFunc func(string, string) (unstructured.Unstructured, error)
|
||||
GetGlobalReferenceFunc func(string, string) (any, error)
|
||||
GetImageDataFunc func(string) (*imagedataloader.ImageData, error)
|
||||
ListResourcesFunc func(string, string, string) (*unstructured.UnstructuredList, error)
|
||||
GetResourcesFunc func(string, string, string, string) (*unstructured.Unstructured, error)
|
||||
GetConfigMapFunc func(string, string) (unstructured.Unstructured, error)
|
||||
GetGlobalReferenceFunc func(string, string) (any, error)
|
||||
GetImageDataFunc func(string) (*imagedataloader.ImageData, error)
|
||||
ParseImageReferenceFunc func(string) (imagedataloader.ImageReference, error)
|
||||
ListResourcesFunc func(string, string, string) (*unstructured.UnstructuredList, error)
|
||||
GetResourcesFunc func(string, string, string, string) (*unstructured.Unstructured, error)
|
||||
}
|
||||
|
||||
func (mock *ctx) GetConfigMap(ns string, n string) (unstructured.Unstructured, error) {
|
||||
|
@ -33,6 +34,10 @@ func (mock *ctx) GetImageData(n string) (*imagedataloader.ImageData, error) {
|
|||
return mock.GetImageDataFunc(n)
|
||||
}
|
||||
|
||||
func (mock *ctx) ParseImageReference(n string) (imagedataloader.ImageReference, error) {
|
||||
return mock.ParseImageReferenceFunc(n)
|
||||
}
|
||||
|
||||
func (mock *ctx) ListResource(apiVersion, resource, namespace string) (*unstructured.UnstructuredList, error) {
|
||||
return mock.ListResourcesFunc(apiVersion, resource, namespace)
|
||||
}
|
||||
|
@ -226,6 +231,40 @@ func Test_impl_get_imagedata_string(t *testing.T) {
|
|||
assert.True(t, img.ImageIndex != nil)
|
||||
}
|
||||
|
||||
func Test_impl_parse_image_ref_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.ParseImageReference("ghcr.io/kyverno/kyverno:latest")`)
|
||||
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{
|
||||
"context": Context{&ctx{
|
||||
ParseImageReferenceFunc: func(image string) (imagedataloader.ImageReference, error) {
|
||||
idl, err := imagedataloader.New(nil)
|
||||
assert.NoError(t, err)
|
||||
return idl.ParseImageReference(image)
|
||||
},
|
||||
}},
|
||||
}
|
||||
out, _, err := prog.Eval(data)
|
||||
assert.NoError(t, err)
|
||||
img := out.Value().(imagedataloader.ImageReference)
|
||||
assert.Equal(t, img.Tag, "latest")
|
||||
assert.Equal(t, img.Identifier, "latest")
|
||||
assert.Equal(t, img.Image, "ghcr.io/kyverno/kyverno:latest")
|
||||
}
|
||||
|
||||
func Test_impl_get_resource_string(t *testing.T) {
|
||||
opts := Lib()
|
||||
base, err := cel.NewEnv(opts)
|
||||
|
|
|
@ -58,6 +58,9 @@ func (c *lib) extendEnv(env *cel.Env) (*cel.Env, error) {
|
|||
// TODO: should not use DynType in return
|
||||
cel.MemberOverload("get_imagedata_string", []*cel.Type{ContextType, types.StringType}, imageDataType.CelType(), cel.BinaryBinding(impl.get_imagedata_string)),
|
||||
},
|
||||
"ParseImageReference": {
|
||||
cel.MemberOverload("parse_image_reference_string", []*cel.Type{ContextType, types.StringType}, imageReferenceType.CelType(), cel.BinaryBinding(impl.parse_imagereference_string)),
|
||||
},
|
||||
"ListResource": {
|
||||
// TODO: should not use DynType in return
|
||||
cel.MemberOverload("list_resource_string", []*cel.Type{ContextType, types.StringType, types.StringType, types.StringType}, types.DynType, cel.FunctionBinding(impl.list_resource_string)),
|
||||
|
|
|
@ -8,15 +8,17 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ContextType = types.NewOpaqueType("context.Context")
|
||||
configMapType = BuildConfigMapType()
|
||||
imageDataType = BuildImageDataType()
|
||||
ContextType = types.NewOpaqueType("context.Context")
|
||||
configMapType = BuildConfigMapType()
|
||||
imageDataType = BuildImageDataType()
|
||||
imageReferenceType = BuildImageReferenceType()
|
||||
)
|
||||
|
||||
type ContextInterface interface {
|
||||
GetConfigMap(string, string) (unstructured.Unstructured, error)
|
||||
GetGlobalReference(string, string) (any, error)
|
||||
GetImageData(string) (*imagedataloader.ImageData, error)
|
||||
ParseImageReference(string) (imagedataloader.ImageReference, error)
|
||||
ListResource(apiVersion, resource, namespace string) (*unstructured.UnstructuredList, error)
|
||||
GetResource(apiVersion, resource, namespace, name string) (*unstructured.Unstructured, error)
|
||||
}
|
||||
|
@ -71,7 +73,7 @@ func BuildImageDataType() *apiservercel.DeclType {
|
|||
field("registry", apiservercel.StringType, true),
|
||||
field("repository", apiservercel.StringType, true),
|
||||
field("tag", apiservercel.StringType, false),
|
||||
field("digest", apiservercel.StringType, true),
|
||||
field("digest", apiservercel.StringType, false),
|
||||
field("imageIndex", apiservercel.DynType, false),
|
||||
field("manifest", apiservercel.DynType, true),
|
||||
field("config", apiservercel.DynType, true),
|
||||
|
@ -79,6 +81,20 @@ func BuildImageDataType() *apiservercel.DeclType {
|
|||
return apiservercel.NewObjectType("imageData", fields(f...))
|
||||
}
|
||||
|
||||
func BuildImageReferenceType() *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("identifier", apiservercel.StringType, true),
|
||||
)
|
||||
return apiservercel.NewObjectType("imageReference", fields(f...))
|
||||
}
|
||||
|
||||
func field(name string, declType *apiservercel.DeclType, required bool) *apiservercel.DeclField {
|
||||
return apiservercel.NewDeclField(name, declType, required, nil, nil)
|
||||
}
|
||||
|
|
|
@ -149,3 +149,7 @@ func (cp *contextProvider) GetResource(apiVersion, resource, namespace, name str
|
|||
|
||||
return resourceInteface.Get(context.TODO(), name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (cp *contextProvider) ParseImageReference(image string) (imagedataloader.ImageReference, error) {
|
||||
return cp.imagedata.ParseImageReference(image)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ type imagedatafetcher struct {
|
|||
|
||||
type Fetcher interface {
|
||||
FetchImageData(ctx context.Context, image string, options ...Option) (*ImageData, error)
|
||||
ParseImageReference(image string, options ...Option) (ImageReference, error)
|
||||
}
|
||||
|
||||
func New(lister k8scorev1.SecretInterface, opts ...Option) (*imagedatafetcher, error) {
|
||||
|
@ -51,6 +52,7 @@ func (i *imagedatafetcher) ParseImageReference(image string, options ...Option)
|
|||
img.Image = image
|
||||
img.Registry = ref.Context().RegistryStr()
|
||||
img.Repository = ref.Context().RepositoryStr()
|
||||
img.Identifier = ref.Identifier()
|
||||
|
||||
if _, ok := ref.(name.Tag); ok {
|
||||
img.Tag = ref.Identifier()
|
||||
|
@ -150,6 +152,7 @@ type ImageReference struct {
|
|||
ResolvedImage string `json:"resolvedImage,omitempty"`
|
||||
Registry string `json:"registry,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
Identifier string `json:"identifier,omitempty"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue