From 24266601d88c21d5c4846b67faefcc210d2f695c Mon Sep 17 00:00:00 2001 From: Yann Hamon Date: Sun, 11 May 2025 00:37:03 +0200 Subject: [PATCH] WIP --- pkg/cache/cache.go | 4 ++-- pkg/cache/inmemory.go | 8 +++---- pkg/cache/ondisk.go | 6 +++--- pkg/loader/file.go | 2 +- pkg/loader/http.go | 2 +- pkg/validator/validator.go | 43 ++++++++++++++++++++++++++++---------- 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 8a21800..5fdafdc 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -1,6 +1,6 @@ package cache type Cache interface { - Get(key string) ([]byte, error) - Set(key string, schema []byte) error + Get(key string) (any, error) + Set(key string, schema any) error } diff --git a/pkg/cache/inmemory.go b/pkg/cache/inmemory.go index df98e91..026a511 100644 --- a/pkg/cache/inmemory.go +++ b/pkg/cache/inmemory.go @@ -10,18 +10,18 @@ import ( // - This cache caches the parsed Schemas type inMemory struct { sync.RWMutex - schemas map[string][]byte + schemas map[string]any } // New creates a new cache for downloaded schemas func NewInMemoryCache() Cache { return &inMemory{ - schemas: make(map[string][]byte), + schemas: make(map[string]any), } } // Get retrieves the JSON schema given a resource signature -func (c *inMemory) Get(key string) ([]byte, error) { +func (c *inMemory) Get(key string) (any, error) { c.RLock() defer c.RUnlock() schema, ok := c.schemas[key] @@ -34,7 +34,7 @@ func (c *inMemory) Get(key string) ([]byte, error) { } // Set adds a JSON schema to the schema cache -func (c *inMemory) Set(key string, schema []byte) error { +func (c *inMemory) Set(key string, schema any) error { c.Lock() defer c.Unlock() c.schemas[key] = schema diff --git a/pkg/cache/ondisk.go b/pkg/cache/ondisk.go index dd489c9..1c16621 100644 --- a/pkg/cache/ondisk.go +++ b/pkg/cache/ondisk.go @@ -27,7 +27,7 @@ func cachePath(folder, key string) string { } // Get retrieves the JSON schema given a resource signature -func (c *onDisk) Get(key string) ([]byte, error) { +func (c *onDisk) Get(key string) (any, error) { c.RLock() defer c.RUnlock() @@ -41,12 +41,12 @@ func (c *onDisk) Get(key string) ([]byte, error) { } // Set adds a JSON schema to the schema cache -func (c *onDisk) Set(key string, schema []byte) error { +func (c *onDisk) Set(key string, schema any) error { c.Lock() defer c.Unlock() if _, err := os.Stat(cachePath(c.folder, key)); os.IsNotExist(err) { - return os.WriteFile(cachePath(c.folder, key), schema, 0644) + return os.WriteFile(cachePath(c.folder, key), schema.([]byte), 0644) } return nil } diff --git a/pkg/loader/file.go b/pkg/loader/file.go index 6166b21..076324e 100644 --- a/pkg/loader/file.go +++ b/pkg/loader/file.go @@ -26,7 +26,7 @@ func (l FileLoader) Load(url string) (any, error) { } if l.cache != nil { if cached, err := l.cache.Get(path); err == nil { - return jsonschema.UnmarshalJSON(bytes.NewReader(cached)) + return jsonschema.UnmarshalJSON(bytes.NewReader(cached.([]byte))) } } diff --git a/pkg/loader/http.go b/pkg/loader/http.go index 0596e0b..69e4430 100644 --- a/pkg/loader/http.go +++ b/pkg/loader/http.go @@ -21,7 +21,7 @@ type HTTPURLLoader struct { func (l *HTTPURLLoader) Load(url string) (any, error) { if l.cache != nil { if cached, err := l.cache.Get(url); err == nil { - return jsonschema.UnmarshalJSON(bytes.NewReader(cached)) + return jsonschema.UnmarshalJSON(bytes.NewReader(cached.([]byte))) } } diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index 2e8e362..7806940 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -113,10 +113,10 @@ func New(schemaLocations []string, opts Opts) (Validator, error) { } return &v{ - opts: opts, - schemaDownload: downloadSchema, - schemaCache: cache.NewInMemoryCache(), - regs: registries, + opts: opts, + schemaDownload: downloadSchema, + schemaMemoryCache: cache.NewInMemoryCache(), + regs: registries, loader: jsonschema.SchemeURLLoader{ "file": jsonschema.FileLoader{}, "http": httpLoader, @@ -126,11 +126,16 @@ func New(schemaLocations []string, opts Opts) (Validator, error) { } type v struct { - opts Opts - schemaCache cache.Cache - schemaDownload func(registries []registry.Registry, loader jsonschema.SchemeURLLoader, kind, version, k8sVersion string) (*jsonschema.Schema, error) - regs []registry.Registry - loader jsonschema.SchemeURLLoader + opts Opts + schemaDiskCache cache.Cache + schemaMemoryCache cache.Cache + schemaDownload func(registries []registry.Registry, loader jsonschema.SchemeURLLoader, kind, version, k8sVersion string) (*jsonschema.Schema, error) + regs []registry.Registry + loader jsonschema.SchemeURLLoader +} + +func key(resourceKind, resourceAPIVersion, k8sVersion string) string { + return fmt.Sprintf("%s-%s-%s", resourceKind, resourceAPIVersion, k8sVersion) } // ValidateResource validates a single resource. This allows to validate @@ -188,9 +193,25 @@ func (val *v) ValidateResource(res resource.Resource) Result { return Result{Resource: res, Err: fmt.Errorf("prohibited resource kind %s", sig.Kind), Status: Error} } + cached := false var schema *jsonschema.Schema - if schema, err = val.schemaDownload(val.regs, val.loader, sig.Kind, sig.Version, val.opts.KubernetesVersion); err != nil { - return Result{Resource: res, Err: err, Status: Error} + + if val.schemaMemoryCache != nil { + s, err := val.schemaMemoryCache.Get(key(sig.Kind, sig.Version, val.opts.KubernetesVersion)) + if err == nil { + cached = true + schema = s.(*jsonschema.Schema) + } + } + + if !cached { + if schema, err = val.schemaDownload(val.regs, val.loader, sig.Kind, sig.Version, val.opts.KubernetesVersion); err != nil { + return Result{Resource: res, Err: err, Status: Error} + } + + if val.schemaMemoryCache != nil { + val.schemaMemoryCache.Set(key(sig.Kind, sig.Version, val.opts.KubernetesVersion), schema) + } } if schema == nil {