func CanAutoGen(policy *kyverno.ClusterPolicy, log logr.Logger) (applyAutoGen bool, controllers string) {
	for _, rule := range policy.Spec.Rules {
		match := rule.MatchResources
		exclude := rule.ExcludeResources
		if match.ResourceDescription.Name != "" || match.ResourceDescription.Selector != nil || match.ResourceDescription.Annotations != nil ||
			exclude.ResourceDescription.Name != "" || exclude.ResourceDescription.Selector != nil || exclude.ResourceDescription.Annotations != nil {
			log.V(3).Info("skip generating rule on pod controllers: Name / Selector in resource description may not be applicable.", "rule", rule.Name)
			return false, "none"
		}
		if isKindOtherthanPod(match.Kinds) || isKindOtherthanPod(exclude.Kinds) {
			return false, "none"
		}
		for _, value := range match.Any {
			if isKindOtherthanPod(value.Kinds) {
				return false, "none"
			}
			if value.Name != "" || value.Selector != nil || value.Annotations != nil {
				log.V(3).Info("skip generating rule on pod controllers: Name / Selector in match any block is not be applicable.", "rule", rule.Name)
				return false, "none"
			}
		}
		for _, value := range match.All {
			if isKindOtherthanPod(value.Kinds) {
				return false, "none"
			}
			if value.Name != "" || value.Selector != nil || value.Annotations != nil {
				log.V(3).Info("skip generating rule on pod controllers: Name / Selector in match all block is not be applicable.", "rule", rule.Name)
				return false, "none"
			}
		}
		for _, value := range exclude.Any {
			if isKindOtherthanPod(value.Kinds) {
				return false, "none"
			}
			if value.Name != "" || value.Selector != nil || value.Annotations != nil {
				log.V(3).Info("skip generating rule on pod controllers: Name / Selector in exclude any block is not be applicable.", "rule", rule.Name)
				return false, "none"
			}
		}
		for _, value := range exclude.All {
			if isKindOtherthanPod(value.Kinds) {
				return false, "none"
			}
			if value.Name != "" || value.Selector != nil || value.Annotations != nil {
				log.V(3).Info("skip generating rule on pod controllers: Name / Selector in exclud all block is not be applicable.", "rule", rule.Name)
				return false, "none"
			}
		}
		if rule.Mutation.Patches != nil || rule.Mutation.PatchesJSON6902 != "" ||
			rule.Validation.Deny != nil || rule.HasGenerate() {
			return false, "none"
		}
	}
	return true, engine.PodControllers
}
CanAutoGen checks whether the rule(s) (in policy) can be applied to Pod controllers
returns controllers as:
- "none" if:
- name or selector is defined
- mixed kinds (Pod + pod controller) is defined
- mutate.Patches/mutate.PatchesJSON6902/validate.deny/generate rule is defined
- otherwise it returns all pod controllers
package policymutation
import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	jsonpatch "github.com/evanphx/json-patch/v5"
	"github.com/go-logr/logr"
	kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
	"github.com/kyverno/kyverno/pkg/common"
	"github.com/kyverno/kyverno/pkg/engine"
	"github.com/kyverno/kyverno/pkg/engine/variables"
	"github.com/kyverno/kyverno/pkg/utils"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
)
func GenerateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, []string) {
	var patches [][]byte
	var updateMsgs []string
	if patch, updateMsg := defaultvalidationFailureAction(policy, log); patch != nil {
		patches = append(patches, patch)
		updateMsgs = append(updateMsgs, updateMsg)
	}
	if patch, updateMsg := defaultBackgroundFlag(policy, log); patch != nil {
		patches = append(patches, patch)
		updateMsgs = append(updateMsgs, updateMsg)
	}
	if patch, updateMsg := defaultFailurePolicy(policy, 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...)
	convertPatch, errs := convertPatchToJSON6902(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, convertPatch...)
	formatedGVK, errs := checkForGVKFormatPatch(policy, log)
	if len(errs) > 0 {
		var errMsgs []string
		for _, err := range errs {
			errMsgs = append(errMsgs, err.Error())
			log.Error(err, "failed to format the kind")
		}
		updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
	}
	patches = append(patches, formatedGVK...)
	overlaySMPPatches, errs := convertOverlayToStrategicMerge(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, overlaySMPPatches...)
	return utils.JoinPatches(patches), updateMsgs
}
func checkForGVKFormatPatch(policy *kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
	patches = make([][]byte, 0)
	for i, rule := range policy.Spec.Rules {
		patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/resources/kinds", strconv.Itoa(i)), rule.MatchResources.Kinds, log)
		if err == nil && patchByte != nil {
			patches = append(patches, patchByte)
		} else if err != nil {
			errs = append(errs, fmt.Errorf("failed to GVK for rule '%s/%s/%d/match': %v", policy.Name, rule.Name, i, err))
		}
		for j, matchAll := range rule.MatchResources.All {
			patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/all/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(j)), matchAll.ResourceDescription.Kinds, log)
			if err == nil && patchByte != nil {
				patches = append(patches, patchByte)
			} else if err != nil {
				errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/match/all/%d': %v", policy.Name, rule.Name, i, j, err))
			}
		}
		for k, matchAny := range rule.MatchResources.Any {
			patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/any/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(k)), matchAny.ResourceDescription.Kinds, log)
			if err == nil && patchByte != nil {
				patches = append(patches, patchByte)
			} else if err != nil {
				errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/match/any/%d': %v", policy.Name, rule.Name, i, k, err))
			}
		}
		patchByte, err = convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/resources/kinds", strconv.Itoa(i)), rule.ExcludeResources.Kinds, log)
		if err == nil && patchByte != nil {
			patches = append(patches, patchByte)
		} else if err != nil {
			errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude': %v", policy.Name, rule.Name, i, err))
		}
		for j, excludeAll := range rule.ExcludeResources.All {
			patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/all/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(j)), excludeAll.ResourceDescription.Kinds, log)
			if err == nil && patchByte != nil {
				patches = append(patches, patchByte)
			} else if err != nil {
				errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude/all/%d': %v", policy.Name, rule.Name, i, j, err))
			}
		}
		for k, excludeAny := range rule.ExcludeResources.Any {
			patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/any/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(k)), excludeAny.ResourceDescription.Kinds, log)
			if err == nil && patchByte != nil {
				patches = append(patches, patchByte)
			} else if err != nil {
				errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude/any/%d': %v", policy.Name, rule.Name, i, k, err))
			}
		}
	}
	return patches, errs
}
func convertGVKForKinds(path string, kinds []string, log logr.Logger) ([]byte, error) {
	kindList := []string{}
	for _, k := range kinds {
		gvk := common.GetFormatedKind(k)
		if gvk == k {
			continue
		}
		kindList = append(kindList, gvk)
	}
	if len(kindList) == 0 {
		return nil, nil
	}
	p, err := buildReplaceJsonPatch(path, kindList)
	log.V(4).WithName("convertGVKForKinds").Info("generated patch", "patch", string(p))
	return p, err
}
func buildReplaceJsonPatch(path string, kindList []string) ([]byte, error) {
	jsonPatch := struct {
		Path  string   `json:"path"`
		Op    string   `json:"op"`
		Value []string `json:"value"`
	}{
		path,
		"replace",
		kindList,
	}
	return json.Marshal(jsonPatch)
}
func convertPatchToJSON6902(policy *kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
	patches = make([][]byte, 0)
	for i, rule := range policy.Spec.Rules {
		if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
			if len(rule.Mutation.Patches) > 0 {
				mutation := rule.Mutation
				patchesJSON6902 := ""
				for _, patch := range mutation.Patches {
					patchesJSON6902 += fmt.Sprintf("- path : %s\n  op : %s\n  value: %s\n", patch.Path, patch.Operation, patch.Value)
				}
				mutation.PatchesJSON6902 = patchesJSON6902
				mutation.Patches = []kyverno.Patch{}
				jsonPatch := struct {
					Path  string            `json:"path"`
					Op    string            `json:"op"`
					Value *kyverno.Mutation `json:"value"`
				}{
					fmt.Sprintf("/spec/rules/%s/mutate", strconv.Itoa(i)),
					"replace",
					&mutation,
				}
				patchByte, err := json.Marshal(jsonPatch)
				if err != nil {
					errs = append(errs, fmt.Errorf("failed to convert patch to patchesJson6902 for policy '%s': %v", policy.Name, err))
				}
				patches = append(patches, patchByte)
			}
		}
	}
	return patches, errs
}
func convertOverlayToStrategicMerge(policy *kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
	patches = make([][]byte, 0)
	if len(policy.Spec.Rules) == 0 {
		return patches, []error{
			errors.New("a policy should have at least one rule"),
		}
	}
	for i, rule := range policy.Spec.Rules {
		if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
			if !reflect.DeepEqual(rule.Mutation.Overlay, kyverno.Mutation{}.Overlay) {
				mutation := rule.Mutation
				mutation.PatchStrategicMerge = mutation.Overlay
				var a interface{}
				mutation.Overlay = a
				jsonPatch := struct {
					Path  string            `json:"path"`
					Op    string            `json:"op"`
					Value *kyverno.Mutation `json:"value"`
				}{
					fmt.Sprintf("/spec/rules/%s/mutate", strconv.Itoa(i)),
					"replace",
					&mutation,
				}
				patchByte, err := json.Marshal(jsonPatch)
				if err != nil {
					errs = append(errs, fmt.Errorf("failed to convert overlay to patchStrategicMerge for policy '%s': %v", policy.Name, err))
				}
				patches = append(patches, patchByte)
			}
		}
	}
	return patches, errs
}
func defaultBackgroundFlag(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
	defaultVal := true
	if policy.Spec.Background == nil {
		log.V(4).Info("setting default value", "spec.background", true)
		jsonPatch := struct {
			Path  string `json:"path"`
			Op    string `json:"op"`
			Value *bool  `json:"value"`
		}{
			"/spec/background",
			"add",
			&defaultVal,
		}
		patchByte, err := json.Marshal(jsonPatch)
		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(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
	Audit := common.Audit
	if policy.Spec.ValidationFailureAction == "" {
		log.V(4).Info("setting default value", "spec.validationFailureAction", Audit)
		jsonPatch := struct {
			Path  string `json:"path"`
			Op    string `json:"op"`
			Value string `json:"value"`
		}{
			"/spec/validationFailureAction",
			"add",
			Audit,
		}
		patchByte, err := json.Marshal(jsonPatch)
		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(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
	failurePolicy := string(kyverno.Fail)
	if policy.Spec.FailurePolicy == nil {
		log.V(4).Info("setting default value", "spec.failurePolicy", failurePolicy)
		jsonPatch := struct {
			Path  string `json:"path"`
			Op    string `json:"op"`
			Value string `json:"value"`
		}{
			"/spec/failurePolicy",
			"add",
			string(kyverno.Fail),
		}
		patchByte, err := json.Marshal(jsonPatch)
		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, ""
}
func GeneratePodControllerRule(policy kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
	applyAutoGen, desiredControllers := CanAutoGen(&policy, log)
	ann := policy.GetAnnotations()
	actualControllers, ok := ann[engine.PodControllersAnnotation]
	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.Name, err))
		} else {
			patches = append(patches, annPatch)
		}
	} else {
		if !applyAutoGen {
			actualControllers = desiredControllers
		}
	}
	if actualControllers == "none" {
		return patches, nil
	}
	log.V(3).Info("auto generating rule for pod controllers", "controllers", actualControllers)
	p, err := generateRulePatches(policy, actualControllers, log)
	patches = append(patches, p...)
	errs = append(errs, err...)
	return
}