1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 09:26:54 +00:00
kyverno/pkg/engine/mutate/mutation.go
Jim Bugwadia a9fef256c7
updates for foreach and mutate (#2891)
* updates for foreach and mutate

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

* allow tests to pass on Windows

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

* fix tests

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

* fix linter check

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

* add elementIndex variable

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

* fmt

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

* fix jsonResult usage

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

* add mutate validation and fix error in validate.foreach

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

* format

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

* update message

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

* do not skip validation for all array entries when one is skipped

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

* add foreach tests

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

* fix fmt

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

* fix format errors

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

* remove unused declarations

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

* revert namespaceWithLabelYaml

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

* fix mutate of element list

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

* update CRDs

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

* Update api/kyverno/v1/policy_types.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/forceMutate.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/forceMutate.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/forceMutate.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/mutation.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/mutation.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/mutation.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/validate/validate.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update pkg/engine/validate/validate.go

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update test/cli/test/custom-functions/policy.yaml

Co-authored-by: Steven E. Harris <seh@panix.com>

* Update test/cli/test/foreach/policies.yaml

Co-authored-by: Steven E. Harris <seh@panix.com>

* accept review comments and format

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

* add comments to strategicMergePatch buffer

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

* load context and evaluate preconditions foreach element

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

* add test for foreach mutate context and precondition

* precondition testcase

* address review comments

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

* update message

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

* format

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

Co-authored-by: Steven E. Harris <seh@panix.com>
Co-authored-by: Vyankatesh Kudtarkar <vyankateshkd@gmail.com>
2022-01-05 09:36:33 +08:00

128 lines
4 KiB
Go

package mutate
import (
"encoding/json"
"fmt"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/common"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/variables"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
type Response struct {
Status response.RuleStatus
PatchedResource unstructured.Unstructured
Patches [][]byte
Message string
}
func newErrorResponse(msg string, err error) *Response {
return newResponse(response.RuleStatusError, unstructured.Unstructured{}, nil, fmt.Sprintf("%s: %v", msg, err))
}
func newResponse(status response.RuleStatus, resource unstructured.Unstructured, patches [][]byte, msg string) *Response {
return &Response{
Status: status,
PatchedResource: resource,
Patches: patches,
Message: msg,
}
}
func Mutate(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger) *Response {
updatedRule, err := variables.SubstituteAllInRule(logger, ctx, *rule)
if err != nil {
return newErrorResponse("variable substitution failed", err)
}
m := updatedRule.Mutation
patcher := NewPatcher(updatedRule.Name, m.PatchStrategicMerge, m.PatchesJSON6902, resource, ctx, logger)
if patcher == nil {
return newResponse(response.RuleStatusError, resource, nil, "empty mutate rule")
}
resp, patchedResource := patcher.Patch()
if resp.Status != response.RuleStatusPass {
return newResponse(resp.Status, resource, nil, resp.Message)
}
if resp.Patches == nil {
return newResponse(response.RuleStatusSkip, resource, nil, "no patches applied")
}
if err := ctx.AddResourceAsObject(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 {
fe, err := substituteAllInForEach(foreach, ctx, logger)
if err != nil {
return newErrorResponse("variable substitution failed", err)
}
patcher := NewPatcher(name, fe.PatchStrategicMerge, fe.PatchesJSON6902, resource, ctx, logger)
if patcher == nil {
return newResponse(response.RuleStatusError, unstructured.Unstructured{}, nil, "no patches found")
}
resp, patchedResource := patcher.Patch()
if resp.Status != response.RuleStatusPass {
return newResponse(resp.Status, unstructured.Unstructured{}, nil, resp.Message)
}
if resp.Patches == nil {
return newResponse(response.RuleStatusSkip, unstructured.Unstructured{}, nil, "no patches applied")
}
if err := ctx.AddResourceAsObject(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) {
jsonObj, err := common.ToMap(fe)
if err != nil {
return nil, err
}
data, err := variables.SubstituteAll(logger, ctx, jsonObj)
if err != nil {
return nil, err
}
bytes, err := json.Marshal(data)
if err != nil {
return nil, err
}
var updatedForEach kyverno.ForEachMutation
if err := json.Unmarshal(bytes, &updatedForEach); err != nil {
return nil, err
}
return &updatedForEach, nil
}
func NewPatcher(name string, strategicMergePatch apiextensions.JSON, jsonPatch string, r unstructured.Unstructured, ctx *context.Context, logger logr.Logger) patch.Patcher {
if strategicMergePatch != nil {
return patch.NewPatchStrategicMerge(name, strategicMergePatch, r, ctx, logger)
}
if len(jsonPatch) > 0 {
return patch.NewPatchesJSON6902(name, jsonPatch, r, logger)
}
return nil
}