diff --git a/pkg/engine/context/context.go b/pkg/engine/context/context.go index 67eac25c1a..59e24fe16c 100644 --- a/pkg/engine/context/context.go +++ b/pkg/engine/context/context.go @@ -5,43 +5,19 @@ import ( "strings" "sync" - "github.com/pkg/errors" - jsonpatch "github.com/evanphx/json-patch/v5" - "github.com/go-logr/logr" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" + pkgcommon "github.com/kyverno/kyverno/pkg/common" + "github.com/pkg/errors" admissionv1 "k8s.io/api/admission/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/controller-runtime/pkg/log" ) -//Interface to manage context operations -type Interface interface { +var logger = log.Log.WithName("context") - // AddRequest marshals and adds the admission request to the context - AddRequest(request *admissionv1.AdmissionRequest) error - - // AddJSON merges the json with context - AddJSON(dataRaw []byte) error - - // AddResource merges resource json under request.object - AddResource(dataRaw []byte) error - - // AddUserInfo merges userInfo json under kyverno.userInfo - AddUserInfo(userInfo kyverno.UserInfo) error - - // AddServiceAccount merges ServiceAccount types - AddServiceAccount(userName string) error - - // AddNamespace merges resource json under request.namespace - AddNamespace(namespace string) error - - EvalInterface -} - -//EvalInterface is used to query and inspect context data +// EvalInterface is used to query and inspect context data type EvalInterface interface { - // Query accepts a JMESPath expression and returns matching data Query(query string) (interface{}, error) @@ -52,178 +28,128 @@ type EvalInterface interface { HasChanged(jmespath string) (bool, error) } -//Context stores the data resources as JSON -type Context struct { +// Interface to manage context operations +type Interface interface { + // AddRequest marshals and adds the admission request to the context + AddRequest(request *admissionv1.AdmissionRequest) error + + // AddVariable adds a variable to the context + AddVariable(key, value string) error + + // AddContextEntry adds a context entry to the context + AddContextEntry(name string, dataRaw []byte) error + + // AddResource merges resource json under request.object + AddResource(data map[string]interface{}) error + + // AddOldResource merges resource json under request.oldObject + AddOldResource(data map[string]interface{}) error + + // AddUserInfo merges userInfo json under kyverno.userInfo + AddUserInfo(userInfo kyverno.RequestInfo) error + + // AddServiceAccount merges ServiceAccount types + AddServiceAccount(userName string) error + + // AddNamespace merges resource json under request.namespace + AddNamespace(namespace string) error + + // AddElement adds element info to the context + AddElement(data map[string]interface{}, index int) error + + // AddImageInfo adds image info to the context + AddImageInfo(info *ImageInfo) error + + // AddImageInfos adds image infos to the context + AddImageInfos(resource *unstructured.Unstructured) error + + // ImageInfo returns image infos present in the context + ImageInfo() *Images + + // Checkpoint creates a copy of the current internal state and pushes it into a stack of stored states. + Checkpoint() + + // Restore sets the internal state to the last checkpoint, and removes the checkpoint. + Restore() + + // Reset sets the internal state to the last checkpoint, but does not remove the checkpoint. + Reset() + + EvalInterface + + // AddJSON merges the json with context + addJSON(dataRaw []byte) error +} + +// Context stores the data resources as JSON +type context struct { mutex sync.RWMutex jsonRaw []byte jsonRawCheckpoints [][]byte images *Images - log logr.Logger } -//NewContext returns a new context -func NewContext() *Context { - ctx := Context{ - jsonRaw: []byte(`{}`), // empty json struct - log: log.Log.WithName("context"), +// NewContext returns a new context +func NewContext() Interface { + return NewContextFromRaw([]byte(`{}`)) +} + +// NewContextFromRaw returns a new context initialized with raw data +func NewContextFromRaw(raw []byte) Interface { + ctx := context{ + jsonRaw: raw, jsonRawCheckpoints: make([][]byte, 0), } - return &ctx } -// AddJSON merges json data -func (ctx *Context) AddJSON(dataRaw []byte) error { - var err error +// addJSON merges json data +func (ctx *context) addJSON(dataRaw []byte) error { ctx.mutex.Lock() defer ctx.mutex.Unlock() - - ctx.jsonRaw, err = jsonpatch.MergeMergePatches(ctx.jsonRaw, dataRaw) + json, err := jsonpatch.MergeMergePatches(ctx.jsonRaw, dataRaw) if err != nil { return errors.Wrap(err, "failed to merge JSON data") } - + ctx.jsonRaw = json return nil } -// AddJSONObject merges json data -func (ctx *Context) AddJSONObject(jsonData interface{}) error { - jsonBytes, err := json.Marshal(jsonData) - if err != nil { - return err - } - - return ctx.AddJSON(jsonBytes) -} - // AddRequest adds an admission request to context -func (ctx *Context) AddRequest(request *admissionv1.AdmissionRequest) error { - modifiedResource := struct { - Request interface{} `json:"request"` - }{ - Request: request, - } - - objRaw, err := json.Marshal(modifiedResource) - if err != nil { - ctx.log.Error(err, "failed to marshal the request") - return err - } - - return ctx.AddJSON(objRaw) +func (ctx *context) AddRequest(request *admissionv1.AdmissionRequest) error { + return addToContext(ctx, request, "request") } -//AddResource data at path: request.object -func (ctx *Context) AddResource(dataRaw []byte) error { +func (ctx *context) AddVariable(key, value string) error { + return ctx.addJSON(pkgcommon.VariableToJSON(key, value)) +} - // unmarshal the resource struct +func (ctx *context) AddContextEntry(name string, dataRaw []byte) error { var data interface{} if err := json.Unmarshal(dataRaw, &data); err != nil { - ctx.log.Error(err, "failed to unmarshal the resource") + logger.Error(err, "failed to unmarshal the resource") return err } - - modifiedResource := struct { - Request interface{} `json:"request"` - }{ - Request: struct { - Object interface{} `json:"object"` - }{ - Object: data, - }, - } - - objRaw, err := json.Marshal(modifiedResource) - if err != nil { - ctx.log.Error(err, "failed to marshal the resource") - return err - } - return ctx.AddJSON(objRaw) + return addToContext(ctx, data, name) } -//AddResourceInOldObject data at path: request.oldObject -func (ctx *Context) AddResourceInOldObject(dataRaw []byte) error { - - // unmarshal the resource struct - var data interface{} - if err := json.Unmarshal(dataRaw, &data); err != nil { - ctx.log.Error(err, "failed to unmarshal the resource") - return err - } - - modifiedResource := struct { - Request interface{} `json:"request"` - }{ - Request: struct { - OldObject interface{} `json:"oldObject"` - }{ - OldObject: data, - }, - } - - objRaw, err := json.Marshal(modifiedResource) - if err != nil { - ctx.log.Error(err, "failed to marshal the resource") - return err - } - return ctx.AddJSON(objRaw) +// AddResource data at path: request.object +func (ctx *context) AddResource(data map[string]interface{}) error { + return addToContext(ctx, data, "request", "object") } -func (ctx *Context) AddResourceAsObject(data interface{}) error { - return ctx.addToRequest(data, "object") +// AddOldResource data at path: request.oldObject +func (ctx *context) AddOldResource(data map[string]interface{}) error { + return addToContext(ctx, data, "request", "oldObject") } -func (ctx *Context) ReplaceResourceAsObject(data interface{}) error { - if err := ctx.addToRequest(nil, "object"); err != nil { - return err - } - - return ctx.addToRequest(data, "object") +// AddUserInfo adds userInfo at path request.userInfo +func (ctx *context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error { + return addToContext(ctx, userRequestInfo, "request") } -func (ctx *Context) ReplaceResourceAsOldObject(data interface{}) error { - if err := ctx.addToRequest(nil, "oldObject"); err != nil { - return err - } - - return ctx.addToRequest(data, "oldObject") -} - -func (ctx *Context) addToRequest(data interface{}, tag string) error { - requestData := make(map[string]interface{}) - requestData[tag] = data - request := map[string]interface{}{ - "request": requestData, - } - - objRaw, err := json.Marshal(request) - if err != nil { - ctx.log.Error(err, "failed to marshal the resource") - return err - } - - return ctx.AddJSON(objRaw) -} - -//AddUserInfo adds userInfo at path request.userInfo -func (ctx *Context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error { - modifiedResource := struct { - Request interface{} `json:"request"` - }{ - Request: userRequestInfo, - } - - objRaw, err := json.Marshal(modifiedResource) - if err != nil { - ctx.log.Error(err, "failed to marshal the UserInfo") - return err - } - ctx.log.V(4).Info("Adding user info logs", "userRequestInfo", userRequestInfo) - return ctx.AddJSON(objRaw) -} - -//AddServiceAccount removes prefix 'system:serviceaccount:' and namespace, then loads only SA name and SA namespace -func (ctx *Context) AddServiceAccount(userName string) error { +// AddServiceAccount removes prefix 'system:serviceaccount:' and namespace, then loads only SA name and SA namespace +func (ctx *context) AddServiceAccount(userName string) error { saPrefix := "system:serviceaccount:" var sa string saName := "" @@ -239,7 +165,6 @@ func (ctx *Context) AddServiceAccount(userName string) error { saName = groups[1] saNamespace = groups[0] } - saNameObj := struct { SA string `json:"serviceAccountName"` }{ @@ -247,10 +172,10 @@ func (ctx *Context) AddServiceAccount(userName string) error { } saNameRaw, err := json.Marshal(saNameObj) if err != nil { - ctx.log.Error(err, "failed to marshal the SA") + logger.Error(err, "failed to marshal the SA") return err } - if err := ctx.AddJSON(saNameRaw); err != nil { + if err := ctx.addJSON(saNameRaw); err != nil { return err } @@ -261,103 +186,85 @@ func (ctx *Context) AddServiceAccount(userName string) error { } saNsRaw, err := json.Marshal(saNsObj) if err != nil { - ctx.log.Error(err, "failed to marshal the SA namespace") + logger.Error(err, "failed to marshal the SA namespace") return err } - if err := ctx.AddJSON(saNsRaw); err != nil { + if err := ctx.addJSON(saNsRaw); err != nil { return err } - ctx.log.V(4).Info("Adding service account", "service account name", saName, "service account namespace", saNamespace) + logger.V(4).Info("Adding service account", "service account name", saName, "service account namespace", saNamespace) return nil } // AddNamespace merges resource json under request.namespace -func (ctx *Context) AddNamespace(namespace string) error { - modifiedResource := struct { - Request interface{} `json:"request"` - }{ - Request: struct { - Namespace string `json:"namespace"` - }{ - Namespace: namespace, - }, - } - - objRaw, err := json.Marshal(modifiedResource) - if err != nil { - ctx.log.Error(err, "failed to marshal the resource") - return err - } - - return ctx.AddJSON(objRaw) +func (ctx *context) AddNamespace(namespace string) error { + return addToContext(ctx, namespace, "request", "namespace") } -func (ctx *Context) AddImageInfo(resource *unstructured.Unstructured) error { - initContainersImgs, containersImgs, ephemeralContainersImgs := extractImageInfo(resource, ctx.log) +func (ctx *context) AddElement(data map[string]interface{}, index int) error { + data = map[string]interface{}{ + "element": data, + "elementIndex": index, + } + return addToContext(ctx, data) +} + +func (ctx *context) AddImageInfo(info *ImageInfo) error { + data := map[string]interface{}{ + "image": info.String(), + "registry": info.Registry, + "path": info.Path, + "name": info.Name, + "tag": info.Tag, + "digest": info.Digest, + } + return addToContext(ctx, data, "image") +} + +func (ctx *context) AddImageInfos(resource *unstructured.Unstructured) error { + initContainersImgs, containersImgs, ephemeralContainersImgs := extractImageInfo(resource, logger) if len(initContainersImgs) == 0 && len(containersImgs) == 0 && len(ephemeralContainersImgs) == 0 { return nil } - images := newImages(initContainersImgs, containersImgs, ephemeralContainersImgs) - if images == nil { - return nil - } - - ctx.images = images - imagesTag := struct { - Images interface{} `json:"images"` - }{ - Images: images, - } - - objRaw, err := json.Marshal(imagesTag) - if err != nil { - return err - } - - return ctx.AddJSON(objRaw) + ctx.images = &images + return addToContext(ctx, images, "images") } -func (ctx *Context) ImageInfo() *Images { +func (ctx *context) ImageInfo() *Images { return ctx.images } // Checkpoint creates a copy of the current internal state and // pushes it into a stack of stored states. -func (ctx *Context) Checkpoint() { +func (ctx *context) Checkpoint() { ctx.mutex.Lock() defer ctx.mutex.Unlock() - jsonRawCheckpoint := make([]byte, len(ctx.jsonRaw)) copy(jsonRawCheckpoint, ctx.jsonRaw) - ctx.jsonRawCheckpoints = append(ctx.jsonRawCheckpoints, jsonRawCheckpoint) } // Restore sets the internal state to the last checkpoint, and removes the checkpoint. -func (ctx *Context) Restore() { +func (ctx *context) Restore() { ctx.reset(true) } // Reset sets the internal state to the last checkpoint, but does not remove the checkpoint. -func (ctx *Context) Reset() { +func (ctx *context) Reset() { ctx.reset(false) } -func (ctx *Context) reset(remove bool) { +func (ctx *context) reset(remove bool) { ctx.mutex.Lock() defer ctx.mutex.Unlock() - if len(ctx.jsonRawCheckpoints) == 0 { return } - n := len(ctx.jsonRawCheckpoints) - 1 jsonRawCheckpoint := ctx.jsonRawCheckpoints[n] - ctx.jsonRaw = make([]byte, len(jsonRawCheckpoint)) copy(ctx.jsonRaw, jsonRawCheckpoint) - if remove { ctx.jsonRawCheckpoints = ctx.jsonRawCheckpoints[:n] } diff --git a/pkg/engine/context/context_test.go b/pkg/engine/context/context_test.go index 10f172824f..b72bbe68e7 100644 --- a/pkg/engine/context/context_test.go +++ b/pkg/engine/context/context_test.go @@ -55,7 +55,7 @@ func Test_addResourceAndUserContext(t *testing.T) { var expectedResult string ctx := NewContext() - err = ctx.AddResource(rawResource) + err = AddResource(ctx, rawResource) if err != nil { t.Error(err) } diff --git a/pkg/engine/context/evaluate.go b/pkg/engine/context/evaluate.go index 9a836eea99..dd30fa2b27 100644 --- a/pkg/engine/context/evaluate.go +++ b/pkg/engine/context/evaluate.go @@ -11,61 +11,46 @@ import ( jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath" ) -//Query the JSON context with JMESPATH search path -func (ctx *Context) Query(query string) (interface{}, error) { +// Query the JSON context with JMESPATH search path +func (ctx *context) Query(query string) (interface{}, error) { query = strings.TrimSpace(query) if query == "" { return nil, fmt.Errorf("invalid query (nil)") } - - var emptyResult interface{} - // compile the query queryPath, err := jmespath.New(query) if err != nil { - ctx.log.Error(err, "incorrect query", "query", query) - return emptyResult, fmt.Errorf("incorrect query %s: %v", query, err) + logger.Error(err, "incorrect query", "query", query) + return nil, fmt.Errorf("incorrect query %s: %v", query, err) } - // search ctx.mutex.RLock() defer ctx.mutex.RUnlock() - var data interface{} if err := json.Unmarshal(ctx.jsonRaw, &data); err != nil { - return emptyResult, errors.Wrap(err, "failed to unmarshal context") + return nil, errors.Wrap(err, "failed to unmarshal context") } - result, err := queryPath.Search(data) if err != nil { - return emptyResult, errors.Wrap(err, "JMESPath query failed") + return nil, errors.Wrap(err, "JMESPath query failed") } - return result, nil } -func (ctx *Context) HasChanged(jmespath string) (bool, error) { +func (ctx *context) HasChanged(jmespath string) (bool, error) { objData, err := ctx.Query("request.object." + jmespath) if err != nil { return false, errors.Wrap(err, "failed to query request.object") } - if objData == nil { return false, fmt.Errorf("request.object.%s not found", jmespath) } - oldObjData, err := ctx.Query("request.oldObject." + jmespath) if err != nil { return false, errors.Wrap(err, "failed to query request.object") } - if oldObjData == nil { return false, fmt.Errorf("request.oldObject.%s not found", jmespath) } - - if reflect.DeepEqual(objData, oldObjData) { - return false, nil - } - - return true, nil + return !reflect.DeepEqual(objData, oldObjData), nil } diff --git a/pkg/engine/context/evaluate_test.go b/pkg/engine/context/evaluate_test.go index 37ed26bd9b..9816cf5ae2 100644 --- a/pkg/engine/context/evaluate_test.go +++ b/pkg/engine/context/evaluate_test.go @@ -55,7 +55,7 @@ func TestMissingObject(t *testing.T) { assert.Error(t, err) } -func createTestContext(obj, oldObj string) *Context { +func createTestContext(obj, oldObj string) Interface { request := &admissionv1.AdmissionRequest{} request.Operation = "UPDATE" request.Object.Raw = []byte(obj) diff --git a/pkg/engine/context/imageutils.go b/pkg/engine/context/imageutils.go index a74dd674e9..8d1ee2d505 100644 --- a/pkg/engine/context/imageutils.go +++ b/pkg/engine/context/imageutils.go @@ -13,7 +13,6 @@ import ( ) type ImageInfo struct { - // Registry is the URL address of the image registry e.g. `docker.io` Registry string `json:"registry,omitempty"` @@ -43,124 +42,96 @@ func (i *ImageInfo) String() string { } type ContainerImage struct { - Name string - Image *ImageInfo + ImageInfo + Name string } type Images struct { - InitContainers map[string]*ImageInfo `json:"initContainers,omitempty"` - Containers map[string]*ImageInfo `json:"containers"` - EphemeralContainers map[string]*ImageInfo `json:"ephemeralContainers"` + InitContainers map[string]ImageInfo `json:"initContainers,omitempty"` + Containers map[string]ImageInfo `json:"containers"` + EphemeralContainers map[string]ImageInfo `json:"ephemeralContainers"` } -func newImages(initContainersImgs, containersImgs, ephemeralContainersImgs []*ContainerImage) *Images { - initContainers := make(map[string]*ImageInfo) +func newImages(initContainersImgs, containersImgs, ephemeralContainersImgs []ContainerImage) Images { + initContainers := make(map[string]ImageInfo) for _, resource := range initContainersImgs { - initContainers[resource.Name] = resource.Image + initContainers[resource.Name] = resource.ImageInfo } - - containers := make(map[string]*ImageInfo) + containers := make(map[string]ImageInfo) for _, resource := range containersImgs { - containers[resource.Name] = resource.Image + containers[resource.Name] = resource.ImageInfo } - - ephemeralContainers := make(map[string]*ImageInfo) + ephemeralContainers := make(map[string]ImageInfo) for _, resource := range ephemeralContainersImgs { - ephemeralContainers[resource.Name] = resource.Image + ephemeralContainers[resource.Name] = resource.ImageInfo } - - return &Images{ + return Images{ InitContainers: initContainers, Containers: containers, EphemeralContainers: ephemeralContainers, } } -func extractImageInfo(resource *unstructured.Unstructured, log logr.Logger) (initContainersImgs, containersImgs, ephemeralContainersImgs []*ContainerImage) { - logger := log.WithName("extractImageInfo").WithValues("kind", resource.GetKind(), "ns", resource.GetNamespace(), "name", resource.GetName()) +type imageExtractor struct { + fields []string +} - for _, tag := range []string{"initContainers", "containers", "ephemeralContainers"} { - switch resource.GetKind() { - case "Pod": - if containers, ok, _ := unstructured.NestedSlice(resource.UnstructuredContent(), "spec", tag); ok { - if tag == "initContainers" { - initContainersImgs = extractImageInfos(containers, initContainersImgs, "/spec/initContainers", logger) - } else if tag == "containers" { - containersImgs = extractImageInfos(containers, containersImgs, "/spec/containers", logger) - } else { - ephemeralContainersImgs = extractImageInfos(containers, ephemeralContainersImgs, "/spec/ephemeralContainers", logger) - } - } - - case "CronJob": - if containers, ok, _ := unstructured.NestedSlice(resource.UnstructuredContent(), "spec", "jobTemplate", "spec", "template", "spec", tag); ok { - if tag == "initContainers" { - initContainersImgs = extractImageInfos(containers, initContainersImgs, "/spec/jobTemplate/spec/template/spec/initContainers", logger) - } else if tag == "containers" { - containersImgs = extractImageInfos(containers, containersImgs, "/spec/jobTemplate/spec/template/spec/containers", logger) - } else { - ephemeralContainersImgs = extractImageInfos(containers, ephemeralContainersImgs, "/spec/jobTemplate/spec/template/spec/ephemeralContainers", logger) - } - } - - // handles "Deployment", "DaemonSet", "Job", "StatefulSet", and custom controllers with the same pattern - default: - if containers, ok, _ := unstructured.NestedSlice(resource.UnstructuredContent(), "spec", "template", "spec", tag); ok { - if tag == "initContainers" { - initContainersImgs = extractImageInfos(containers, initContainersImgs, "/spec/template/spec/initContainers", logger) - } else if tag == "containers" { - containersImgs = extractImageInfos(containers, containersImgs, "/spec/template/spec/containers", logger) - } else { - ephemeralContainersImgs = extractImageInfos(containers, ephemeralContainersImgs, "/spec/template/spec/ephemeralContainers", logger) - } - } - } +func (i imageExtractor) extract(tag string, resource *unstructured.Unstructured) []ContainerImage { + f := append(i.fields[:len(i.fields):len(i.fields)], tag) + if containers, ok, _ := unstructured.NestedSlice(resource.UnstructuredContent(), f...); ok { + return extractImageInfos(containers, "/"+strings.Join(f, "/")) } + return nil +} +var extractors = map[string]imageExtractor{ + "Pod": {[]string{"spec"}}, + "CronJob": {[]string{"spec", "jobTemplate", "spec", "template", "spec"}}, + "Deployment": {[]string{"spec", "template", "spec"}}, + "DaemonSet": {[]string{"spec", "template", "spec"}}, + "Job": {[]string{"spec", "template", "spec"}}, + "StatefulSet": {[]string{"spec", "template", "spec"}}, +} + +func extractImageInfo(resource *unstructured.Unstructured, log logr.Logger) (initContainersImgs, containersImgs, ephemeralContainersImgs []ContainerImage) { + extractor := extractors[resource.GetKind()] + initContainersImgs = extractor.extract("initContainers", resource) + containersImgs = extractor.extract("containers", resource) + ephemeralContainersImgs = extractor.extract("ephemeralContainers", resource) return } -func extractImageInfos(containers []interface{}, images []*ContainerImage, jsonPath string, log logr.Logger) []*ContainerImage { +func extractImageInfos(containers []interface{}, jsonPath string) []ContainerImage { img, err := convertToImageInfo(containers, jsonPath) if err != nil { - log.Error(err, "failed to extract image info", "element", containers) + logger.Error(err, "failed to extract image info", "element", containers) } - - return append(images, img...) + return img } -func convertToImageInfo(containers []interface{}, jsonPath string) (images []*ContainerImage, err error) { +func convertToImageInfo(containers []interface{}, jsonPath string) (images []ContainerImage, err error) { var errs []string var index = 0 for _, ctr := range containers { if container, ok := ctr.(map[string]interface{}); ok { var name, image string - name = container["name"].(string) if _, ok := container["image"]; ok { image = container["image"].(string) } - jp := strings.Join([]string{jsonPath, strconv.Itoa(index), "image"}, "/") imageInfo, err := newImageInfo(image, jp) if err != nil { errs = append(errs, err.Error()) continue } - - images = append(images, &ContainerImage{ - Name: name, - Image: imageInfo, - }) + images = append(images, ContainerImage{*imageInfo, name}) } - index++ } - if len(errs) == 0 { return images, nil } - return images, errors.Errorf("%s", strings.Join(errs, ";")) } @@ -170,27 +141,22 @@ func newImageInfo(image, jsonPointer string) (*ImageInfo, error) { if err != nil { return nil, errors.Wrapf(err, "bad image: %s", image) } - var registry, path, name, tag, digest string if named, ok := ref.(reference.Named); ok { registry = reference.Domain(named) path = reference.Path(named) name = path[strings.LastIndex(path, "/")+1:] } - if tagged, ok := ref.(reference.Tagged); ok { tag = tagged.Tag() } - if digested, ok := ref.(reference.Digested); ok { digest = digested.Digest().String() } - // set default tag - the domain is set via addDefaultDomain before parsing if digest == "" && tag == "" { tag = "latest" } - return &ImageInfo{ Registry: registry, Name: name, @@ -206,41 +172,34 @@ func addDefaultDomain(name string) string { if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost" && strings.ToLower(name[:i]) == name[:i]) { return "docker.io/" + name } - return name } // MutateResourceWithImageInfo will set images to their canonical form so that they can be compared // in a predictable manner. This sets the default registry as `docker.io` and the tag as `latest` if // these are missing. -func MutateResourceWithImageInfo(raw []byte, ctx *Context) error { +func MutateResourceWithImageInfo(raw []byte, ctx Interface) error { images := ctx.ImageInfo() if images == nil { return nil } - buildJSONPatch := func(op, path, value string) []byte { p := fmt.Sprintf(`{ "op": "%s", "path": "%s", "value":"%s" }`, op, path, value) return []byte(p) } - var patches [][]byte for _, info := range images.Containers { patches = append(patches, buildJSONPatch("replace", info.JSONPointer, info.String())) } - for _, info := range images.InitContainers { patches = append(patches, buildJSONPatch("replace", info.JSONPointer, info.String())) } - for _, info := range images.EphemeralContainers { patches = append(patches, buildJSONPatch("replace", info.JSONPointer, info.String())) } - patchedResource, err := engineutils.ApplyPatches(raw, patches) if err != nil { return err } - - return ctx.AddResource(patchedResource) + return AddResource(ctx, patchedResource) } diff --git a/pkg/engine/context/imageutils_test.go b/pkg/engine/context/imageutils_test.go index 83030c8109..1b43eb7f7a 100644 --- a/pkg/engine/context/imageutils_test.go +++ b/pkg/engine/context/imageutils_test.go @@ -11,31 +11,31 @@ import ( func Test_extractImageInfo(t *testing.T) { tests := []struct { raw []byte - containers []*ContainerImage - initContainers []*ContainerImage - ephemeralContainers []*ContainerImage + containers []ContainerImage + initContainers []ContainerImage + ephemeralContainers []ContainerImage }{ { raw: []byte(`{"apiVersion": "v1","kind": "Pod","metadata": {"name": "myapp"},"spec": {"initContainers": [{"name": "init","image": "index.docker.io/busybox:v1.2.3"}],"containers": [{"name": "nginx","image": "nginx:latest"}], "ephemeralContainers": [{"name": "ephemeral", "image":"test/nginx:latest"}]}}`), - initContainers: []*ContainerImage{{Name: "init", Image: &ImageInfo{Registry: "index.docker.io", Name: "busybox", Path: "busybox", Tag: "v1.2.3", JSONPointer: "/spec/initContainers/0/image"}}}, - containers: []*ContainerImage{{Name: "nginx", Image: &ImageInfo{Registry: "docker.io", Name: "nginx", Path: "nginx", Tag: "latest", JSONPointer: "/spec/containers/0/image"}}}, - ephemeralContainers: []*ContainerImage{{Name: "ephemeral", Image: &ImageInfo{Registry: "docker.io", Name: "nginx", Path: "test/nginx", Tag: "latest", JSONPointer: "/spec/ephemeralContainers/0/image"}}}, + initContainers: []ContainerImage{{Name: "init", ImageInfo: ImageInfo{Registry: "index.docker.io", Name: "busybox", Path: "busybox", Tag: "v1.2.3", JSONPointer: "/spec/initContainers/0/image"}}}, + containers: []ContainerImage{{Name: "nginx", ImageInfo: ImageInfo{Registry: "docker.io", Name: "nginx", Path: "nginx", Tag: "latest", JSONPointer: "/spec/containers/0/image"}}}, + ephemeralContainers: []ContainerImage{{Name: "ephemeral", ImageInfo: ImageInfo{Registry: "docker.io", Name: "nginx", Path: "test/nginx", Tag: "latest", JSONPointer: "/spec/ephemeralContainers/0/image"}}}, }, { raw: []byte(`{"apiVersion": "v1","kind": "Pod","metadata": {"name": "myapp"},"spec": {"containers": [{"name": "nginx","image": "test/nginx:latest"}]}}`), - initContainers: []*ContainerImage{}, - containers: []*ContainerImage{{Name: "nginx", Image: &ImageInfo{Registry: "docker.io", Name: "nginx", Path: "test/nginx", Tag: "latest", JSONPointer: "/spec/containers/0/image"}}}, - ephemeralContainers: []*ContainerImage{}, + initContainers: []ContainerImage{}, + containers: []ContainerImage{{Name: "nginx", ImageInfo: ImageInfo{Registry: "docker.io", Name: "nginx", Path: "test/nginx", Tag: "latest", JSONPointer: "/spec/containers/0/image"}}}, + ephemeralContainers: []ContainerImage{}, }, { raw: []byte(`{"apiVersion": "apps/v1","kind": "Deployment","metadata": {"name": "myapp"},"spec": {"selector": {"matchLabels": {"app": "myapp"}},"template": {"metadata": {"labels": {"app": "myapp"}},"spec": {"initContainers": [{"name": "init","image": "fictional.registry.example:10443/imagename:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}],"containers": [{"name": "myapp","image": "fictional.registry.example:10443/imagename"}],"ephemeralContainers": [{"name": "ephemeral","image": "fictional.registry.example:10443/imagename:tag@sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}] }}}}`), - initContainers: []*ContainerImage{{Name: "init", Image: &ImageInfo{Registry: "fictional.registry.example:10443", Name: "imagename", Path: "imagename", Tag: "tag", JSONPointer: "/spec/template/spec/initContainers/0/image", Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}}}, - containers: []*ContainerImage{{Name: "myapp", Image: &ImageInfo{Registry: "fictional.registry.example:10443", Name: "imagename", Path: "imagename", Tag: "latest", JSONPointer: "/spec/template/spec/containers/0/image"}}}, - ephemeralContainers: []*ContainerImage{{Name: "ephemeral", Image: &ImageInfo{Registry: "fictional.registry.example:10443", Name: "imagename", Path: "imagename", Tag: "tag", JSONPointer: "/spec/template/spec/ephemeralContainers/0/image", Digest: "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}}}, + initContainers: []ContainerImage{{Name: "init", ImageInfo: ImageInfo{Registry: "fictional.registry.example:10443", Name: "imagename", Path: "imagename", Tag: "tag", JSONPointer: "/spec/template/spec/initContainers/0/image", Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}}}, + containers: []ContainerImage{{Name: "myapp", ImageInfo: ImageInfo{Registry: "fictional.registry.example:10443", Name: "imagename", Path: "imagename", Tag: "latest", JSONPointer: "/spec/template/spec/containers/0/image"}}}, + ephemeralContainers: []ContainerImage{{Name: "ephemeral", ImageInfo: ImageInfo{Registry: "fictional.registry.example:10443", Name: "imagename", Path: "imagename", Tag: "tag", JSONPointer: "/spec/template/spec/ephemeralContainers/0/image", Digest: "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}}}, }, { raw: []byte(`{"apiVersion": "batch/v1beta1","kind": "CronJob","metadata": {"name": "hello"},"spec": {"schedule": "*/1 * * * *","jobTemplate": {"spec": {"template": {"spec": {"containers": [{"name": "hello","image": "test.example.com/test/my-app:v2"}]}}}}}}`), - containers: []*ContainerImage{{Name: "hello", Image: &ImageInfo{Registry: "test.example.com", Name: "my-app", Path: "test/my-app", Tag: "v2", JSONPointer: "/spec/jobTemplate/spec/template/spec/containers/0/image"}}}, + containers: []ContainerImage{{Name: "hello", ImageInfo: ImageInfo{Registry: "test.example.com", Name: "my-app", Path: "test/my-app", Tag: "v2", JSONPointer: "/spec/jobTemplate/spec/template/spec/containers/0/image"}}}, }, } diff --git a/pkg/engine/context/utils.go b/pkg/engine/context/utils.go new file mode 100644 index 0000000000..8b51daa83e --- /dev/null +++ b/pkg/engine/context/utils.go @@ -0,0 +1,64 @@ +package context + +import ( + "encoding/json" +) + +// AddJSONObject merges json data +func AddJSONObject(ctx Interface, data interface{}) error { + jsonBytes, err := json.Marshal(data) + if err != nil { + return err + } + return ctx.addJSON(jsonBytes) +} + +func AddResource(ctx Interface, dataRaw []byte) error { + var data map[string]interface{} + if err := json.Unmarshal(dataRaw, &data); err != nil { + logger.Error(err, "failed to unmarshal the resource") + return err + } + return ctx.AddResource(data) +} + +func ReplaceResource(ctx Interface, data map[string]interface{}) error { + if err := ctx.AddResource(nil); err != nil { + return err + } + return ctx.AddResource(data) +} + +func AddOldResource(ctx Interface, dataRaw []byte) error { + var data map[string]interface{} + if err := json.Unmarshal(dataRaw, &data); err != nil { + logger.Error(err, "failed to unmarshal the resource") + return err + } + return ctx.AddOldResource(data) +} + +func ReplaceOldResource(ctx Interface, data map[string]interface{}) error { + if err := ctx.AddOldResource(nil); err != nil { + return err + } + return ctx.AddOldResource(data) +} + +func addToContext(ctx *context, data interface{}, tags ...string) error { + dataRaw, err := json.Marshal(push(data, tags...)) + if err != nil { + logger.Error(err, "failed to marshal the resource") + return err + } + return ctx.addJSON(dataRaw) +} + +func push(data interface{}, tags ...string) interface{} { + for i := len(tags) - 1; i >= 0; i-- { + data = map[string]interface{}{ + tags[i]: data, + } + } + return data +} diff --git a/pkg/engine/forceMutate.go b/pkg/engine/forceMutate.go index 5956245378..010f53fd94 100644 --- a/pkg/engine/forceMutate.go +++ b/pkg/engine/forceMutate.go @@ -14,7 +14,7 @@ import ( // ForceMutate does not check any conditions, it simply mutates the given resource // It is used to validate mutation logic, and for tests. -func ForceMutate(ctx *context.Context, policy kyverno.PolicyInterface, resource unstructured.Unstructured) (unstructured.Unstructured, error) { +func ForceMutate(ctx context.Interface, policy kyverno.PolicyInterface, resource unstructured.Unstructured) (unstructured.Unstructured, error) { logger := log.Log.WithName("EngineForceMutate").WithValues("policy", policy.GetName(), "kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName()) diff --git a/pkg/engine/forceMutate_test.go b/pkg/engine/forceMutate_test.go index f20ac44424..7e8f257c8a 100644 --- a/pkg/engine/forceMutate_test.go +++ b/pkg/engine/forceMutate_test.go @@ -100,7 +100,7 @@ func Test_ForceMutateSubstituteVars(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(rawResource) + err = context.AddResource(ctx, rawResource) assert.NilError(t, err) mutatedResource, err := ForceMutate(ctx, &policy, *resourceUnstructured) @@ -205,7 +205,7 @@ func Test_ForceMutateSubstituteVarsWithPatchesJson6902(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(rawResource) + err = context.AddResource(ctx, rawResource) assert.NilError(t, err) mutatedResource, err := ForceMutate(ctx, &policy, *resourceUnstructured) @@ -291,7 +291,7 @@ func Test_ForceMutateSubstituteVarsWithPatchStrategicMerge(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(rawResource) + err = context.AddResource(ctx, rawResource) assert.NilError(t, err) mutatedResource, err := ForceMutate(ctx, &policy, *resourceUnstructured) diff --git a/pkg/engine/imageVerify.go b/pkg/engine/imageVerify.go index aae53700b4..2ee6b198e8 100644 --- a/pkg/engine/imageVerify.go +++ b/pkg/engine/imageVerify.go @@ -125,7 +125,7 @@ type imageVerifier struct { resp *response.EngineResponse } -func (iv *imageVerifier) verify(imageVerify *v1.ImageVerification, images map[string]*context.ImageInfo) { +func (iv *imageVerifier) verify(imageVerify *v1.ImageVerification, images map[string]context.ImageInfo) { imagePattern := imageVerify.Image for _, imageInfo := range images { @@ -167,7 +167,7 @@ func getSignatureRepository(imageVerify *v1.ImageVerification) string { return repository } -func (iv *imageVerifier) verifySignature(imageVerify *v1.ImageVerification, imageInfo *context.ImageInfo) (*response.RuleResponse, string) { +func (iv *imageVerifier) verifySignature(imageVerify *v1.ImageVerification, imageInfo context.ImageInfo) (*response.RuleResponse, string) { image := imageInfo.String() iv.logger.Info("verifying image", "image", image) @@ -219,7 +219,7 @@ func (iv *imageVerifier) verifySignature(imageVerify *v1.ImageVerification, imag return ruleResp, digest } -func (iv *imageVerifier) patchDigest(imageInfo *context.ImageInfo, digest string, ruleResp *response.RuleResponse) { +func (iv *imageVerifier) patchDigest(imageInfo context.ImageInfo, digest string, ruleResp *response.RuleResponse) { if imageInfo.Digest == "" { patch, err := makeAddDigestPatch(imageInfo, digest) if err != nil { @@ -231,7 +231,7 @@ func (iv *imageVerifier) patchDigest(imageInfo *context.ImageInfo, digest string } } -func makeAddDigestPatch(imageInfo *context.ImageInfo, digest string) ([]byte, error) { +func makeAddDigestPatch(imageInfo context.ImageInfo, digest string) ([]byte, error) { var patch = make(map[string]interface{}) patch["op"] = "replace" patch["path"] = imageInfo.JSONPointer @@ -239,7 +239,7 @@ func makeAddDigestPatch(imageInfo *context.ImageInfo, digest string) ([]byte, er return json.Marshal(patch) } -func (iv *imageVerifier) attestImage(imageVerify *v1.ImageVerification, imageInfo *context.ImageInfo) *response.RuleResponse { +func (iv *imageVerifier) attestImage(imageVerify *v1.ImageVerification, imageInfo context.ImageInfo) *response.RuleResponse { image := imageInfo.String() start := time.Now() @@ -291,7 +291,7 @@ func buildStatementMap(statements []map[string]interface{}) map[string][]map[str return results } -func (iv *imageVerifier) checkAttestations(a *v1.Attestation, s map[string]interface{}, img *context.ImageInfo) (bool, error) { +func (iv *imageVerifier) checkAttestations(a *v1.Attestation, s map[string]interface{}, img context.ImageInfo) (bool, error) { if len(a.Conditions) == 0 { return true, nil } @@ -304,22 +304,11 @@ func (iv *imageVerifier) checkAttestations(a *v1.Attestation, s map[string]inter return false, fmt.Errorf("failed to extract predicate from statement: %v", s) } - if err := iv.policyContext.JSONContext.AddJSONObject(predicate); err != nil { + if err := context.AddJSONObject(iv.policyContext.JSONContext, predicate); err != nil { return false, errors.Wrapf(err, fmt.Sprintf("failed to add Statement to the context %v", s)) } - imgMap := map[string]interface{}{ - "image": map[string]interface{}{ - "image": img.String(), - "registry": img.Registry, - "path": img.Path, - "name": img.Name, - "tag": img.Tag, - "digest": img.Digest, - }, - } - - if err := iv.policyContext.JSONContext.AddJSONObject(imgMap); err != nil { + if err := iv.policyContext.JSONContext.AddImageInfo(&img); err != nil { return false, errors.Wrapf(err, fmt.Sprintf("failed to add image to the context %v", s)) } diff --git a/pkg/engine/imageVerify_test.go b/pkg/engine/imageVerify_test.go index 7eb3dd3d41..df4b9cb14d 100644 --- a/pkg/engine/imageVerify_test.go +++ b/pkg/engine/imageVerify_test.go @@ -164,7 +164,7 @@ func buildContext(t *testing.T, policy, resource string) *PolicyContext { resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -174,7 +174,7 @@ func buildContext(t *testing.T, policy, resource string) *PolicyContext { JSONContext: ctx, NewResource: *resourceUnstructured} - if err := ctx.AddImageInfo(resourceUnstructured); err != nil { + if err := ctx.AddImageInfos(resourceUnstructured); err != nil { t.Errorf("unable to add image info to variables context: %v", err) t.Fail() } diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go index 5b00de2a42..97eb0a641c 100644 --- a/pkg/engine/jsonContext.go +++ b/pkg/engine/jsonContext.go @@ -9,7 +9,6 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" - pkgcommon "github.com/kyverno/kyverno/pkg/common" jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/kyverno/store" @@ -44,9 +43,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, ctx value = string(newVal) } - jsonData := pkgcommon.VariableToJSON(key, value) - - if err := ctx.JSONContext.AddJSON(jsonData); err != nil { + if err := ctx.JSONContext.AddVariable(key, value); err != nil { return err } } @@ -81,7 +78,11 @@ func loadImageData(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyCo if err != nil { return err } - if err := ctx.JSONContext.AddJSONObject(imageData); err != nil { + jsonBytes, err := json.Marshal(imageData) + if err != nil { + return err + } + if err := ctx.JSONContext.AddContextEntry(entry.Name, jsonBytes); err != nil { return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err) } return nil @@ -110,9 +111,7 @@ func fetchImageData(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyC return nil, fmt.Errorf("failed to apply JMESPath (%s) results to context entry %s, error: %v", entry.ImageRegistry.JMESPath, entry.Name, err) } } - return map[string]interface{}{ - entry.Name: imageData, - }, nil + return imageData, nil } func fetchImageDataMap(ref string) (interface{}, error) { @@ -169,7 +168,7 @@ func loadAPIData(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyCont } if entry.APICall.JMESPath == "" { - err = ctx.JSONContext.AddJSON(jsonData) + err = ctx.JSONContext.AddContextEntry(entry.Name, jsonData) if err != nil { return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err) } @@ -187,19 +186,17 @@ func loadAPIData(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyCont return err } - contextNamedData := make(map[string]interface{}) - contextNamedData[entry.Name] = results - contextData, err := json.Marshal(contextNamedData) + contextData, err := json.Marshal(results) if err != nil { - return fmt.Errorf("failed to marshall data %v for context entry %v: %v", contextNamedData, entry, err) + return fmt.Errorf("failed to marshall data %v for context entry %v: %v", contextData, entry, err) } - err = ctx.JSONContext.AddJSON(contextData) + err = ctx.JSONContext.AddContextEntry(entry.Name, 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) + logger.Info("added APICall context entry", "data", contextData) return nil } @@ -286,7 +283,7 @@ func loadConfigMap(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyCo return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err) } - err = ctx.JSONContext.AddJSON(data) + err = ctx.JSONContext.AddContextEntry(entry.Name, data) if err != nil { return fmt.Errorf("failed to add config map for context entry %s: %v", entry.Name, err) } @@ -324,9 +321,7 @@ func fetchConfigMap(logger logr.Logger, entry kyverno.ContextEntry, ctx *PolicyC // 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) + data, err := json.Marshal(contextData) if err != nil { return nil, fmt.Errorf("failed to unmarshal configmap %s/%s: %v", namespace, name, err) } diff --git a/pkg/engine/mutate/mutation.go b/pkg/engine/mutate/mutation.go index 6334f1bd47..ab55dae3df 100644 --- a/pkg/engine/mutate/mutation.go +++ b/pkg/engine/mutate/mutation.go @@ -35,7 +35,7 @@ func newResponse(status response.RuleStatus, resource unstructured.Unstructured, } } -func Mutate(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger) *Response { +func Mutate(rule *kyverno.Rule, ctx context.Interface, resource unstructured.Unstructured, logger logr.Logger) *Response { updatedRule, err := variables.SubstituteAllInRule(logger, ctx, *rule) if err != nil { return newErrorResponse("variable substitution failed", err) @@ -56,14 +56,14 @@ func Mutate(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unst return newResponse(response.RuleStatusSkip, resource, nil, "no patches applied") } - if err := ctx.AddResourceAsObject(patchedResource.Object); err != nil { + if err := ctx.AddResource(patchedResource.Object); err != nil { return newErrorResponse("failed to update patched resource in the JSON context", err) } return newResponse(response.RuleStatusPass, patchedResource, resp.Patches, resp.Message) } -func ForEach(name string, foreach *kyverno.ForEachMutation, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger) *Response { +func ForEach(name string, foreach *kyverno.ForEachMutation, ctx context.Interface, resource unstructured.Unstructured, logger logr.Logger) *Response { fe, err := substituteAllInForEach(foreach, ctx, logger) if err != nil { return newErrorResponse("variable substitution failed", err) @@ -83,14 +83,14 @@ func ForEach(name string, foreach *kyverno.ForEachMutation, ctx *context.Context return newResponse(response.RuleStatusSkip, unstructured.Unstructured{}, nil, "no patches applied") } - if err := ctx.AddResourceAsObject(patchedResource.Object); err != nil { + if err := ctx.AddResource(patchedResource.Object); err != nil { return newErrorResponse("failed to update patched resource in the JSON context", err) } return newResponse(response.RuleStatusPass, patchedResource, resp.Patches, resp.Message) } -func substituteAllInForEach(fe *kyverno.ForEachMutation, ctx *context.Context, logger logr.Logger) (*kyverno.ForEachMutation, error) { +func substituteAllInForEach(fe *kyverno.ForEachMutation, ctx context.Interface, logger logr.Logger) (*kyverno.ForEachMutation, error) { jsonObj, err := utils.ToMap(fe) if err != nil { return nil, err @@ -114,7 +114,7 @@ func substituteAllInForEach(fe *kyverno.ForEachMutation, ctx *context.Context, l return &updatedForEach, nil } -func NewPatcher(name string, strategicMergePatch apiextensions.JSON, jsonPatch string, r unstructured.Unstructured, ctx *context.Context, logger logr.Logger) patch.Patcher { +func NewPatcher(name string, strategicMergePatch apiextensions.JSON, jsonPatch string, r unstructured.Unstructured, ctx context.Interface, logger logr.Logger) patch.Patcher { if strategicMergePatch != nil { return patch.NewPatchStrategicMerge(name, strategicMergePatch, r, ctx, logger) } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 262b529538..9008910b02 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -61,7 +61,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) { resource, err := policyContext.JSONContext.Query("request.object") policyContext.JSONContext.Reset() if err == nil && resource != nil { - if err := ctx.AddResourceAsObject(resource.(map[string]interface{})); err != nil { + if err := ctx.AddResource(resource.(map[string]interface{})); err != nil { logger.Error(err, "unable to update resource object") } } else { diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index 03de8cced4..2ce34cc946 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -75,7 +75,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -155,7 +155,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -250,7 +250,7 @@ func Test_variableSubstitutionCLI(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -353,7 +353,7 @@ func Test_chained_rules(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResourceAsObject(resource.Object) + err = ctx.AddResource(resource.Object) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -362,7 +362,7 @@ func Test_chained_rules(t *testing.T) { NewResource: *resource, } - err = ctx.AddImageInfo(resource) + err = ctx.AddImageInfos(resource) assert.NilError(t, err) err = context.MutateResourceWithImageInfo(resourceRaw, ctx) @@ -447,7 +447,7 @@ func Test_precondition(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -544,7 +544,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -632,7 +632,7 @@ func Test_foreach(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResourceAsObject(resource.Object) + err = ctx.AddResource(resource.Object) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -641,7 +641,7 @@ func Test_foreach(t *testing.T) { NewResource: *resource, } - err = ctx.AddImageInfo(resource) + err = ctx.AddImageInfos(resource) assert.NilError(t, err) err = context.MutateResourceWithImageInfo(resourceRaw, ctx) @@ -739,7 +739,7 @@ func Test_foreach_element_mutation(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResourceAsObject(resource.Object) + err = ctx.AddResource(resource.Object) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -748,7 +748,7 @@ func Test_foreach_element_mutation(t *testing.T) { NewResource: *resource, } - err = ctx.AddImageInfo(resource) + err = ctx.AddImageInfos(resource) assert.NilError(t, err) err = context.MutateResourceWithImageInfo(resourceRaw, ctx) @@ -865,7 +865,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResourceAsObject(resource.Object) + err = ctx.AddResource(resource.Object) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -874,7 +874,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { NewResource: *resource, } - err = ctx.AddImageInfo(resource) + err = ctx.AddImageInfos(resource) assert.NilError(t, err) err = context.MutateResourceWithImageInfo(resourceRaw, ctx) @@ -992,7 +992,7 @@ func Test_foreach_order_mutation_(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResourceAsObject(resource.Object) + err = ctx.AddResource(resource.Object) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1001,7 +1001,7 @@ func Test_foreach_order_mutation_(t *testing.T) { NewResource: *resource, } - err = ctx.AddImageInfo(resource) + err = ctx.AddImageInfos(resource) assert.NilError(t, err) err = context.MutateResourceWithImageInfo(resourceRaw, ctx) diff --git a/pkg/engine/policyContext.go b/pkg/engine/policyContext.go index b7e4200365..23eaaa5e86 100644 --- a/pkg/engine/policyContext.go +++ b/pkg/engine/policyContext.go @@ -33,7 +33,7 @@ type PolicyContext struct { ExcludeResourceFunc func(kind, namespace, name string) bool // JSONContext is the variable context - JSONContext *context.Context + JSONContext context.Interface // NamespaceLabels stores the label of namespace to be processed by namespace selector NamespaceLabels map[string]string diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 601b570d96..273c4ac577 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -10,6 +10,7 @@ import ( kyverno "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/engine/common" + "github.com/kyverno/kyverno/pkg/engine/context" "github.com/pkg/errors" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" @@ -118,11 +119,11 @@ func validateOldObject(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) ctxCopy.NewResource = *ctxCopy.OldResource.DeepCopy() ctxCopy.OldResource = unstructured.Unstructured{} - if err := ctxCopy.JSONContext.ReplaceResourceAsObject(ctxCopy.NewResource.Object); err != nil { + if err := context.ReplaceResource(ctxCopy.JSONContext, ctxCopy.NewResource.Object); err != nil { return nil, errors.Wrapf(err, "failed to replace object in the JSON context") } - if err := ctxCopy.JSONContext.ReplaceResourceAsOldObject(ctxCopy.OldResource.Object); err != nil { + if err := context.ReplaceOldResource(ctxCopy.JSONContext, ctxCopy.OldResource.Object); err != nil { return nil, errors.Wrapf(err, "failed to replace old object in the JSON context") } @@ -322,22 +323,14 @@ func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, el if err != nil { return err } - - jsonData := map[string]interface{}{ - "element": data, - "elementIndex": elementIndex, - } - - if err := ctx.JSONContext.AddJSONObject(jsonData); err != nil { + if err := ctx.JSONContext.AddElement(data, elementIndex); err != nil { return errors.Wrapf(err, "failed to add element (%v) to JSON context", e) } - if elementScope { u := unstructured.Unstructured{} u.SetUnstructuredContent(data) ctx.Element = u } - return nil } diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index c2fff3757b..71349cbe37 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -1471,7 +1471,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1564,7 +1564,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1625,7 +1625,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1716,7 +1716,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1809,7 +1809,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1914,7 +1914,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing. assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -1967,7 +1967,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(test.resourceRaw) + err = context.AddResource(ctx, test.resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -2327,7 +2327,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) @@ -2416,7 +2416,7 @@ func Test_StringInDenyCondition(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) @@ -3002,7 +3002,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContext := &PolicyContext{ @@ -3066,7 +3066,7 @@ func Test_delete_ignore_pattern(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) policyContextCreate := &PolicyContext{ diff --git a/pkg/engine/variables/evaluate_test.go b/pkg/engine/variables/evaluate_test.go index d5596d017b..da2f8ca3f8 100644 --- a/pkg/engine/variables/evaluate_test.go +++ b/pkg/engine/variables/evaluate_test.go @@ -402,7 +402,7 @@ func Test_Eval_Equal_Var_Pass(t *testing.T) { // context ctx := context.NewContext() - err := ctx.AddResource(resourceRaw) + err := context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -444,7 +444,7 @@ func Test_Eval_Equal_Var_Fail(t *testing.T) { // context ctx := context.NewContext() - err := ctx.AddResource(resourceRaw) + err := context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } diff --git a/pkg/engine/variables/variables_test.go b/pkg/engine/variables/variables_test.go index a9e55b667d..0d62f5dd2c 100644 --- a/pkg/engine/variables/variables_test.go +++ b/pkg/engine/variables/variables_test.go @@ -76,7 +76,7 @@ func Test_variablesub1(t *testing.T) { } // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -166,7 +166,7 @@ func Test_variablesub_multiple(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -253,7 +253,7 @@ func Test_variablesubstitution(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -319,7 +319,7 @@ func Test_variableSubstitutionValue(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -377,7 +377,7 @@ func Test_variableSubstitutionValueOperatorNotEqual(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -436,7 +436,7 @@ func Test_variableSubstitutionValueFail(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -494,7 +494,7 @@ func Test_variableSubstitutionObject(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -558,7 +558,7 @@ func Test_variableSubstitutionObjectOperatorNotEqualFail(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -633,7 +633,7 @@ func Test_variableSubstitutionMultipleObject(t *testing.T) { // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } diff --git a/pkg/engine/variables/vars.go b/pkg/engine/variables/vars.go index 9516397dd5..38331c9916 100644 --- a/pkg/engine/variables/vars.go +++ b/pkg/engine/variables/vars.go @@ -189,7 +189,7 @@ func substituteAll(log logr.Logger, ctx context.EvalInterface, document interfac return substituteVars(log, ctx, document, resolver) } -func SubstituteAllForceMutate(log logr.Logger, ctx *context.Context, typedRule kyverno.Rule) (_ kyverno.Rule, err error) { +func SubstituteAllForceMutate(log logr.Logger, ctx context.Interface, typedRule kyverno.Rule) (_ kyverno.Rule, err error) { var rule interface{} rule, err = DocumentToUntyped(typedRule) diff --git a/pkg/engine/variables/vars_test.go b/pkg/engine/variables/vars_test.go index c92e215681..e9c443256b 100644 --- a/pkg/engine/variables/vars_test.go +++ b/pkg/engine/variables/vars_test.go @@ -65,7 +65,7 @@ func Test_subVars_success(t *testing.T) { } // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -126,7 +126,7 @@ func Test_subVars_failed(t *testing.T) { } // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) } @@ -220,7 +220,7 @@ func Test_subVars_with_JMESPath_At(t *testing.T) { assert.NilError(t, err) // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) output, err := SubstituteAll(log.Log, ctx, pattern) @@ -279,7 +279,7 @@ func Test_subVars_withRegexMatch(t *testing.T) { assert.NilError(t, err) // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) output, err := SubstituteAll(log.Log, ctx, pattern) @@ -309,7 +309,7 @@ func Test_subVars_withMerge(t *testing.T) { assert.NilError(t, err) // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) output, err := SubstituteAll(log.Log, ctx, pattern) @@ -352,7 +352,7 @@ func Test_subVars_withRegexReplaceAll(t *testing.T) { assert.NilError(t, err) // context ctx := context.NewContext() - err = ctx.AddResource(resourceRaw) + err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) output, err := SubstituteAll(log.Log, ctx, pattern) @@ -396,8 +396,7 @@ func Test_ReplacingPathWhenDeleting(t *testing.T) { if err != nil { t.Error(err) } - ctx := context.NewContext() - err = ctx.AddJSON(resourceRaw) + ctx := context.NewContextFromRaw(resourceRaw) assert.NilError(t, err) pattern, err = SubstituteAll(log.Log, ctx, pattern) @@ -432,8 +431,7 @@ func Test_ReplacingNestedVariableWhenDeleting(t *testing.T) { if err != nil { t.Error(err) } - ctx := context.NewContext() - err = ctx.AddJSON(resourceRaw) + ctx := context.NewContextFromRaw(resourceRaw) assert.NilError(t, err) pattern, err = SubstituteAll(log.Log, ctx, pattern) @@ -460,7 +458,7 @@ var resourceRaw = []byte(` func Test_SubstituteSuccess(t *testing.T) { ctx := context.NewContext() - assert.Assert(t, ctx.AddResource(resourceRaw)) + assert.Assert(t, context.AddResource(ctx, resourceRaw)) var pattern interface{} patternRaw := []byte(`"{{request.object.metadata.annotations.test}}"`) @@ -484,7 +482,7 @@ func Test_SubstituteSuccess(t *testing.T) { func Test_SubstituteRecursiveErrors(t *testing.T) { ctx := context.NewContext() - assert.Assert(t, ctx.AddResource(resourceRaw)) + assert.Assert(t, context.AddResource(ctx, resourceRaw)) var pattern interface{} patternRaw := []byte(`"{{request.object.metadata.{{request.object.metadata.annotations.test2}}}}"`) @@ -516,7 +514,7 @@ func Test_SubstituteRecursiveErrors(t *testing.T) { func Test_SubstituteRecursive(t *testing.T) { ctx := context.NewContext() - assert.Assert(t, ctx.AddResource(resourceRaw)) + assert.Assert(t, context.AddResource(ctx, resourceRaw)) var pattern interface{} patternRaw := []byte(`"{{request.object.metadata.{{request.object.metadata.annotations.test}}}}"`) @@ -633,9 +631,8 @@ func Test_variableSubstitution_array(t *testing.T) { err := json.Unmarshal(ruleRaw, &rule) assert.NilError(t, err) - ctx := context.NewContext() - ctx.AddJSON(configmapRaw) - ctx.AddResource(resourceRaw) + ctx := context.NewContextFromRaw(configmapRaw) + context.AddResource(ctx, resourceRaw) vars, err := SubstituteAllInRule(log.Log, ctx, rule) assert.NilError(t, err) @@ -681,7 +678,7 @@ func Test_SubstituteNull(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -710,7 +707,7 @@ func Test_SubstituteNullInString(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -739,7 +736,7 @@ func Test_SubstituteArray(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -768,7 +765,7 @@ func Test_SubstituteArrayInString(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -797,7 +794,7 @@ func Test_SubstituteInt(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -826,7 +823,7 @@ func Test_SubstituteIntInString(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -855,7 +852,7 @@ func Test_SubstituteBool(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -884,7 +881,7 @@ func Test_SubstituteBoolInString(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -913,7 +910,7 @@ func Test_SubstituteString(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -942,7 +939,7 @@ func Test_SubstituteStringInString(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(variableObject) + context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(log.Log, ctx, pattern) assert.NilError(t, err) @@ -993,7 +990,7 @@ func Test_ReferenceSubstitution(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(jsonRaw) + err = context.AddResource(ctx, jsonRaw) assert.NilError(t, err) actualDocument, err := SubstituteAll(log.Log, ctx, document) @@ -1137,7 +1134,7 @@ func Test_EscpReferenceSubstitution(t *testing.T) { assert.NilError(t, err) ctx := context.NewContext() - err = ctx.AddResource(jsonRaw) + err = context.AddResource(ctx, jsonRaw) assert.NilError(t, err) actualDocument, err := SubstituteAll(log.Log, ctx, document) @@ -1172,8 +1169,7 @@ func Test_ReplacingEscpNestedVariableWhenDeleting(t *testing.T) { if err != nil { t.Error(err) } - ctx := context.NewContext() - err = ctx.AddJSON(resourceRaw) + ctx := context.NewContextFromRaw(resourceRaw) assert.NilError(t, err) pattern, err = SubstituteAll(log.Log, ctx, pattern) diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go index 115a6f2511..4309e5e848 100644 --- a/pkg/generate/generate.go +++ b/pkg/generate/generate.go @@ -161,13 +161,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern return nil, false, err } - resourceRaw, err := resource.MarshalJSON() - if err != nil { - logger.Error(err, "failed to marshal resource") - return nil, false, err - } - - err = ctx.AddResource(resourceRaw) + err = ctx.AddResource(resource.Object) if err != nil { logger.Error(err, "failed to load resource in context") return nil, false, err @@ -185,7 +179,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern return nil, false, err } - if err := ctx.AddImageInfo(&resource); err != nil { + if err := ctx.AddImageInfos(&resource); err != nil { logger.Error(err, "unable to add image info to variables context") } diff --git a/pkg/kyverno/common/common.go b/pkg/kyverno/common/common.go index 70a6779684..063f933b48 100644 --- a/pkg/kyverno/common/common.go +++ b/pkg/kyverno/common/common.go @@ -21,7 +21,6 @@ import ( "github.com/go-logr/logr" v1 "github.com/kyverno/kyverno/api/kyverno/v1" report "github.com/kyverno/kyverno/api/policyreport/v1alpha2" - pkgcommon "github.com/kyverno/kyverno/pkg/common" client "github.com/kyverno/kyverno/pkg/dclient" "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/context" @@ -512,9 +511,9 @@ OuterLoop: ctx := context.NewContext() if operationIsDelete { - err = ctx.AddResourceInOldObject(resourceRaw) + err = context.AddOldResource(ctx, resourceRaw) } else { - err = ctx.AddResourceAsObject(updated_resource.Object) + err = context.AddResource(ctx, resourceRaw) } if err != nil { @@ -522,14 +521,13 @@ OuterLoop: } for key, value := range variables { - jsonData := pkgcommon.VariableToJSON(key, value) - err = ctx.AddJSON(jsonData) + err = ctx.AddVariable(key, value) if err != nil { log.Log.Error(err, "failed to add variable to context") } } - if err := ctx.AddImageInfo(resource); err != nil { + if err := ctx.AddImageInfos(resource); err != nil { if err != nil { log.Log.Error(err, "failed to add image variables to context") } diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go index bf9c7d0995..d50cb23002 100644 --- a/pkg/policy/apply.go +++ b/pkg/policy/apply.go @@ -38,7 +38,7 @@ func applyPolicy(policy kyverno.PolicyInterface, resource unstructured.Unstructu var err error ctx := context.NewContext() - err = ctx.AddResource(transformResource(resource)) + err = context.AddResource(ctx, transformResource(resource)) if err != nil { logger.Error(err, "failed to add transform resource to ctx") } @@ -48,7 +48,7 @@ func applyPolicy(policy kyverno.PolicyInterface, resource unstructured.Unstructu logger.Error(err, "failed to add namespace to ctx") } - if err := ctx.AddImageInfo(&resource); err != nil { + if err := ctx.AddImageInfos(&resource); err != nil { logger.Error(err, "unable to add image info to variables context") } @@ -72,7 +72,7 @@ func applyPolicy(policy kyverno.PolicyInterface, resource unstructured.Unstructu return engineResponses } -func mutation(policy kyverno.PolicyInterface, resource unstructured.Unstructured, log logr.Logger, jsonContext *context.Context, namespaceLabels map[string]string) (*response.EngineResponse, error) { +func mutation(policy kyverno.PolicyInterface, resource unstructured.Unstructured, log logr.Logger, jsonContext context.Interface, namespaceLabels map[string]string) (*response.EngineResponse, error) { policyContext := &engine.PolicyContext{ Policy: policy, diff --git a/pkg/webhooks/common.go b/pkg/webhooks/common.go index 12b825c394..b2198bf665 100644 --- a/pkg/webhooks/common.go +++ b/pkg/webhooks/common.go @@ -157,7 +157,7 @@ func excludeKyvernoResources(kind string) bool { } } -func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyverno.RequestInfo) (*enginectx.Context, error) { +func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyverno.RequestInfo) (enginectx.Interface, error) { ctx := enginectx.NewContext() if err := ctx.AddRequest(request); err != nil { return nil, errors.Wrap(err, "failed to load incoming request in context") diff --git a/pkg/webhooks/generation.go b/pkg/webhooks/generation.go index 1494a1fcf0..6d4bbf3606 100644 --- a/pkg/webhooks/generation.go +++ b/pkg/webhooks/generation.go @@ -45,7 +45,7 @@ func (ws *WebhookServer) applyGeneratePolicies(request *admissionv1.AdmissionReq func (ws *WebhookServer) handleGenerate( request *admissionv1.AdmissionRequest, policies []kyverno.PolicyInterface, - ctx *context.Context, + ctx context.Interface, userRequestInfo kyverno.RequestInfo, dynamicConfig config.Interface, admissionRequestTimestamp int64, diff --git a/pkg/webhooks/handlers.go b/pkg/webhooks/handlers.go index 4d5f46f224..412513c7be 100644 --- a/pkg/webhooks/handlers.go +++ b/pkg/webhooks/handlers.go @@ -195,7 +195,7 @@ func (ws *WebhookServer) resourceValidation(request *admissionv1.AdmissionReques if err != nil { return errorResponse(logger, err, "failed create parse resource") } - if err := ctx.AddImageInfo(&newResource); err != nil { + if err := ctx.AddImageInfos(&newResource); err != nil { return errorResponse(logger, err, "failed add image information to policy rule context") } policyContext := &engine.PolicyContext{ diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 6c6b8405b5..ab63fb2cdc 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -228,7 +228,7 @@ func (ws *WebhookServer) buildPolicyContext(request *admissionv1.AdmissionReques return nil, errors.Wrap(err, "failed to convert raw resource to unstructured format") } - if err := ctx.AddImageInfo(&resource); err != nil { + if err := ctx.AddImageInfos(&resource); err != nil { return nil, errors.Wrap(err, "failed to add image information to the policy rule context") } diff --git a/pkg/webhooks/validate_audit.go b/pkg/webhooks/validate_audit.go index 1da0bf17d0..33e9aa9eb6 100644 --- a/pkg/webhooks/validate_audit.go +++ b/pkg/webhooks/validate_audit.go @@ -181,7 +181,7 @@ func (h *auditHandler) process(request *admissionv1.AdmissionRequest) error { return errors.Wrap(err, "failed create parse resource") } - if err := ctx.AddImageInfo(&newResource); err != nil { + if err := ctx.AddImageInfos(&newResource); err != nil { return errors.Wrap(err, "failed add image information to policy rule context\"") }