1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 07:57:07 +00:00
kyverno/pkg/policymutation/policymutation.go
Jim Bugwadia 58337716c8
Fix merging JSON patches (#4202)
* fix merge of image verify and mutate patches

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

* update json patch merge logic

Signed-off-by: Jim Bugwadia <jim@nirmata.com>
2022-07-11 09:26:31 +05:30

169 lines
6.2 KiB
Go

package policymutation
import (
"fmt"
"strconv"
"strings"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/toggle"
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
)
// GenerateJSONPatchesForDefaults generates default JSON patches for
// - ValidationFailureAction
// - Background
// - auto-gen annotation and rules
func GenerateJSONPatchesForDefaults(policy kyvernov1.PolicyInterface, log logr.Logger) ([]byte, []string) {
var patches [][]byte
var updateMsgs []string
spec := policy.GetSpec()
// if autogenInternals is enabled, we don't mutate most of the policy fields
if !toggle.AutogenInternals() {
// default 'ValidationFailureAction'
if patch, updateMsg := defaultvalidationFailureAction(spec, log); patch != nil {
patches = append(patches, patch)
updateMsgs = append(updateMsgs, updateMsg)
}
// default 'Background'
if patch, updateMsg := defaultBackgroundFlag(spec, log); patch != nil {
patches = append(patches, patch)
updateMsgs = append(updateMsgs, updateMsg)
}
if patch, updateMsg := defaultFailurePolicy(spec, log); patch != nil {
patches = append(patches, patch)
updateMsgs = append(updateMsgs, updateMsg)
}
patch, errs := GeneratePodControllerRule(policy, log)
if len(errs) > 0 {
var errMsgs []string
for _, err := range errs {
errMsgs = append(errMsgs, err.Error())
log.Error(err, "failed to generate pod controller rule")
}
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
}
patches = append(patches, patch...)
}
return jsonutils.JoinPatches(patches...), updateMsgs
}
func defaultBackgroundFlag(spec *kyvernov1.Spec, log logr.Logger) ([]byte, string) {
// set 'Background' flag to 'true' if not specified
if spec.Background == nil {
defaultVal := true
log.V(4).Info("setting default value", "spec.background", true)
patchByte, err := jsonutils.MarshalPatchOperation("/spec/background", "add", &defaultVal)
if err != nil {
log.Error(err, "failed to set default value", "spec.background", true)
return nil, ""
}
log.V(3).Info("generated JSON Patch to set default", "spec.background", true)
return patchByte, fmt.Sprintf("default 'Background' to '%s'", strconv.FormatBool(true))
}
return nil, ""
}
func defaultvalidationFailureAction(spec *kyvernov1.Spec, log logr.Logger) ([]byte, string) {
// set ValidationFailureAction to "audit" if not specified
if spec.ValidationFailureAction == "" {
audit := kyvernov1.Audit
log.V(4).Info("setting default value", "spec.validationFailureAction", audit)
patchByte, err := jsonutils.MarshalPatchOperation("/spec/validationFailureAction", "add", audit)
if err != nil {
log.Error(err, "failed to default value", "spec.validationFailureAction", audit)
return nil, ""
}
log.V(3).Info("generated JSON Patch to set default", "spec.validationFailureAction", audit)
return patchByte, fmt.Sprintf("default 'ValidationFailureAction' to '%s'", audit)
}
return nil, ""
}
func defaultFailurePolicy(spec *kyvernov1.Spec, log logr.Logger) ([]byte, string) {
// set failurePolicy to Fail if not present
if spec.FailurePolicy == nil {
failurePolicy := string(kyvernov1.Fail)
log.V(4).Info("setting default value", "spec.failurePolicy", failurePolicy)
patchByte, err := jsonutils.MarshalPatchOperation("/spec/failurePolicy", "add", failurePolicy)
if err != nil {
log.Error(err, "failed to set default value", "spec.failurePolicy", failurePolicy)
return nil, ""
}
log.V(3).Info("generated JSON Patch to set default", "spec.failurePolicy", failurePolicy)
return patchByte, fmt.Sprintf("default failurePolicy to '%s'", failurePolicy)
}
return nil, ""
}
// podControllersKey annotation could be:
// scenario A: not exist, set default to "all", which generates on all pod controllers
// - if name / selector exist in resource description -> skip
// as these fields may not be applicable to pod controllers
// scenario B: "none", user explicitly disable this feature -> skip
// scenario C: some certain controllers that user set -> generate on defined controllers
// copy entire match / exclude block, it's users' responsibility to
// make sure all fields are applicable to pod controllers
// GeneratePodControllerRule returns two patches: rulePatches and annotation patch(if necessary)
func GeneratePodControllerRule(policy kyvernov1.PolicyInterface, log logr.Logger) (patches [][]byte, errs []error) {
spec := policy.GetSpec()
applyAutoGen, desiredControllers := autogen.CanAutoGen(spec)
if !applyAutoGen {
desiredControllers = "none"
}
ann := policy.GetAnnotations()
actualControllers, ok := ann[kyvernov1.PodControllersAnnotation]
// - scenario A
// - predefined controllers are invalid, overwrite the value
if !ok || !applyAutoGen {
actualControllers = desiredControllers
annPatch, err := defaultPodControllerAnnotation(ann, actualControllers)
if err != nil {
errs = append(errs, fmt.Errorf("failed to generate pod controller annotation for policy '%s': %v", policy.GetName(), err))
} else {
patches = append(patches, annPatch)
}
} else {
if !applyAutoGen {
actualControllers = desiredControllers
}
}
// scenario B
if actualControllers == "none" {
return patches, nil
}
log.V(3).Info("auto generating rule for pod controllers", "controllers", actualControllers)
p, err := autogen.GenerateRulePatches(spec, actualControllers)
patches = append(patches, p...)
errs = append(errs, err...)
return
}
// defaultPodControllerAnnotation inserts an annotation
// "pod-policies.kyverno.io/autogen-controllers=<controllers>" to policy
func defaultPodControllerAnnotation(ann map[string]string, controllers string) ([]byte, error) {
if ann == nil {
ann = make(map[string]string)
ann[kyvernov1.PodControllersAnnotation] = controllers
patchByte, err := jsonutils.MarshalPatchOperation("/metadata/annotations", "add", ann)
if err != nil {
return nil, err
}
return patchByte, nil
}
patchByte, err := jsonutils.MarshalPatchOperation("/metadata/annotations/pod-policies.kyverno.io~1autogen-controllers", "add", controllers)
if err != nil {
return nil, err
}
return patchByte, nil
}