1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 23:46:56 +00:00
kyverno/pkg/engine/jsonContext.go
Jim Bugwadia e8e3b93a5f
api server lookups (#1514)
* initial commit for api server lookups

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* initial commit for API server lookups

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* Enhancing dockerfiles (multi-stage) of kyverno components and adding non-root user to the docker images (#1495)

* Dockerfile refactored

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* Adding non-root commands to docker images and enhanced the dockerfiles

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* changing base image to scratch

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* Minor typo fix

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* changing dockerfiles to use /etc/passwd to use non-root user'

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* minor typo

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* minor typo

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* revert cli image name (#1507)

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* Refactor resourceCache; Reduce throttling requests (background controller) (#1500)

* skip sending API request for filtered resource

* fix PR comment

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* fixes https://github.com/kyverno/kyverno/issues/1490

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* fix bug - namespace is not returned properly

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* reduce throttling - list resource using lister

* refactor resource cache

* fix test

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* fix label selector

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* fix build failure

Signed-off-by: Shuting Zhao <shutting06@gmail.com>
Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* fix merge issues

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* fix unit test

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* add nil check for API client

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

Co-authored-by: Raj Babu Das <mail.rajdas@gmail.com>
Co-authored-by: shuting <shutting06@gmail.com>
2021-02-01 12:59:13 -08:00

190 lines
5.2 KiB
Go

package engine
import (
"encoding/json"
"errors"
"fmt"
"github.com/go-logr/logr"
"github.com/jmespath/go-jmespath"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/resourcecache"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic/dynamiclister"
)
// LoadContext - Fetches and adds external data to the Context.
func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, resCache resourcecache.ResourceCache, ctx *PolicyContext) error {
if len(contextEntries) == 0 {
return nil
}
// get GVR Cache for "configmaps"
// can get cache for other resources if the informers are enabled in resource cache
gvrC, ok := resCache.GetGVRCache("ConfigMap")
if !ok {
return errors.New("configmaps GVR Cache not found")
}
lister := gvrC.Lister()
for _, entry := range contextEntries {
if entry.ConfigMap != nil {
if err := loadConfigMap(entry, lister, ctx.JSONContext); err != nil {
return err
}
} else if entry.APICall != nil {
if err := loadAPIData(logger, entry, ctx); err != nil {
return err
}
}
}
return nil
}
func loadAPIData(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyContext) error {
jsonData, err := fetchAPIData(entry, ctx)
if err != nil {
return err
}
if entry.APICall.JMESPath == "" {
err = ctx.JSONContext.AddJSON(jsonData)
if err != nil {
return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err)
}
return nil
}
results, err := applyJMESPath(entry.APICall.JMESPath, jsonData)
if err != nil {
return fmt.Errorf("failed to apply JMESPath for context entry %v: %v", entry, err)
}
contextNamedData := make(map[string]interface{})
contextNamedData[entry.Name] = results
contextData, err := json.Marshal(contextNamedData)
if err != nil {
return fmt.Errorf("failed to marshall data %v for context entry %v: %v", contextNamedData, entry, err)
}
err = ctx.JSONContext.AddJSON(contextData)
if err != nil {
return fmt.Errorf("failed to add JMESPath (%s) results to context, error: %v", entry.APICall.JMESPath, err)
}
logger.Info("added APICall context entry", "data", contextNamedData)
return nil
}
func applyJMESPath(jmesPath string, jsonData []byte) (interface{}, error) {
jp, err := jmespath.Compile(jmesPath)
if err != nil {
return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", jmesPath, err)
}
var data interface{}
err = json.Unmarshal(jsonData, &data)
if err != nil {
return nil, fmt.Errorf("failed to unmarshall JSON: %s, error: %v", string(jsonData), err)
}
return jp.Search(data)
}
func fetchAPIData(entry kyverno.ContextEntry, ctx *PolicyContext) ([]byte, error) {
if entry.APICall == nil {
return nil, fmt.Errorf("missing APICall in context entry %v", entry)
}
p, err := NewAPIPath(entry.APICall.URLPath)
if err != nil {
return nil, fmt.Errorf("failed to build API path for %v: %v", entry, err)
}
var jsonData []byte
if p.Name != "" {
jsonData, err = loadResource(ctx, p)
if err != nil {
return nil, fmt.Errorf("failed to add resource with urlPath: %s: %v", p, err)
}
} else {
jsonData, err = loadResourceList(ctx, p)
if err != nil {
return nil, fmt.Errorf("failed to add resource list with urlPath: %s, error: %v", p, err)
}
}
return jsonData, nil
}
func loadResourceList(ctx *PolicyContext, p *APIPath) ([]byte, error) {
l, err := ctx.Client.ListResource(p.Version, p.ResourceType, p.Namespace, nil)
if err != nil {
return nil, err
}
return l.MarshalJSON()
}
func loadResource(ctx *PolicyContext, p *APIPath) ([]byte, error) {
if ctx.Client == nil {
return nil, fmt.Errorf("API client is not available")
}
r, err := ctx.Client.GetResource(p.Version, p.ResourceType, p.Namespace, p.Name)
if err != nil {
return nil, err
}
return r.MarshalJSON()
}
func loadConfigMap(entry kyverno.ContextEntry, lister dynamiclister.Lister, ctx *context.Context) error {
data, err := fetchConfigMap(entry, lister)
if err != nil {
return fmt.Errorf("failed to retrieve config map for context entry %v: %v", entry, err)
}
err = ctx.AddJSON(data)
if err != nil {
return fmt.Errorf("failed to add config map for context entry %v: %v", entry, err)
}
return nil
}
func fetchConfigMap(entry kyverno.ContextEntry, lister dynamiclister.Lister) ([]byte, error) {
contextData := make(map[string]interface{})
name := entry.ConfigMap.Name
namespace := entry.ConfigMap.Namespace
if namespace == "" {
namespace = "default"
}
key := fmt.Sprintf("%s/%s", namespace, name)
obj, err := lister.Get(key)
if err != nil {
return nil, fmt.Errorf("failed to read configmap %s/%s from cache: %v", namespace, name, err)
}
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return nil, fmt.Errorf("failed to convert configmap %s/%s: %v", namespace, name, err)
}
// extract configmap data
contextData["data"] = unstructuredObj["data"]
contextData["metadata"] = unstructuredObj["metadata"]
contextNamedData := make(map[string]interface{})
contextNamedData[entry.Name] = contextData
data, err := json.Marshal(contextNamedData)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal configmap %s/%s: %v", namespace, name, err)
}
return data, nil
}