1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/pkg/autogen/rule.go
Vyankatesh Kudtarkar b3a53f0658
fix PodExecOptions issue (#3373)
* fix PodExecOptions issue

* add note

* update comment
2022-03-11 15:09:32 +05:30

428 lines
14 KiB
Go

package autogen
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/utils"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)
// the kyvernoRule holds the temporary kyverno rule struct
// each field is a pointer to the actual object
// when serializing data, we would expect to drop the omitempty key
// otherwise (without the pointer), it will be set to empty value
// - an empty struct in this case, some may fail the schema validation
// may related to:
// https://github.com/kyverno/kyverno/pull/549#discussion_r360088556
// https://github.com/kyverno/kyverno/issues/568
type kyvernoRule struct {
Name string `json:"name"`
MatchResources *kyverno.MatchResources `json:"match"`
ExcludeResources *kyverno.ExcludeResources `json:"exclude,omitempty"`
Context *[]kyverno.ContextEntry `json:"context,omitempty"`
AnyAllConditions *apiextensions.JSON `json:"preconditions,omitempty"`
Mutation *kyverno.Mutation `json:"mutate,omitempty"`
Validation *kyverno.Validation `json:"validate,omitempty"`
VerifyImages []*kyverno.ImageVerification `json:"verifyImages,omitempty" yaml:"verifyImages,omitempty"`
}
func createRuleMap(rules []kyverno.Rule) map[string]kyvernoRule {
var ruleMap = make(map[string]kyvernoRule)
for _, rule := range rules {
var jsonFriendlyStruct kyvernoRule
jsonFriendlyStruct.Name = rule.Name
if !reflect.DeepEqual(rule.MatchResources, kyverno.MatchResources{}) {
jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy()
}
if !reflect.DeepEqual(rule.ExcludeResources, kyverno.ExcludeResources{}) {
jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy()
}
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
jsonFriendlyStruct.Mutation = rule.Mutation.DeepCopy()
}
if !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
jsonFriendlyStruct.Validation = rule.Validation.DeepCopy()
}
ruleMap[rule.Name] = jsonFriendlyStruct
}
return ruleMap
}
func generateRuleForControllers(rule kyverno.Rule, controllers string, log logr.Logger) *kyvernoRule {
logger := log.WithName("generateRuleForControllers")
if strings.HasPrefix(rule.Name, "autogen-") || controllers == "" {
logger.V(5).Info("skip generateRuleForControllers")
return nil
}
logger.V(3).Info("processing rule", "rulename", rule.Name)
match := rule.MatchResources
exclude := rule.ExcludeResources
matchResourceDescriptionsKinds := rule.MatchKinds()
excludeResourceDescriptionsKinds := rule.ExcludeKinds()
if !utils.ContainsKind(matchResourceDescriptionsKinds, "Pod") ||
(len(excludeResourceDescriptionsKinds) != 0 && !utils.ContainsKind(excludeResourceDescriptionsKinds, "Pod")) {
return nil
}
// Support backwards compatibility
skipAutoGeneration := false
var controllersValidated []string
if controllers == "all" {
skipAutoGeneration = true
} else if controllers != "none" && controllers != "all" {
controllersList := map[string]int{"DaemonSet": 1, "Deployment": 1, "Job": 1, "StatefulSet": 1}
for _, value := range strings.Split(controllers, ",") {
if _, ok := controllersList[value]; ok {
controllersValidated = append(controllersValidated, value)
}
}
if len(controllersValidated) > 0 {
skipAutoGeneration = true
}
}
if skipAutoGeneration {
if controllers == "all" {
controllers = "DaemonSet,Deployment,Job,StatefulSet"
} else {
controllers = strings.Join(controllersValidated, ",")
}
}
name := fmt.Sprintf("autogen-%s", rule.Name)
if len(name) > 63 {
name = name[:63]
}
controllerRule := &kyvernoRule{
Name: name,
MatchResources: match.DeepCopy(),
}
if len(rule.Context) > 0 {
controllerRule.Context = &rule.DeepCopy().Context
}
kyvernoAnyAllConditions, _ := utils.ApiextensionsJsonToKyvernoConditions(rule.GetAnyAllConditions())
switch typedAnyAllConditions := kyvernoAnyAllConditions.(type) {
case kyverno.AnyAllConditions:
if !reflect.DeepEqual(typedAnyAllConditions, kyverno.AnyAllConditions{}) {
controllerRule.AnyAllConditions = rule.DeepCopy().RawAnyAllConditions
}
case []kyverno.Condition:
if len(typedAnyAllConditions) > 0 {
controllerRule.AnyAllConditions = rule.DeepCopy().RawAnyAllConditions
}
}
if !reflect.DeepEqual(exclude, kyverno.ExcludeResources{}) {
controllerRule.ExcludeResources = exclude.DeepCopy()
}
// overwrite Kinds by pod controllers defined in the annotation
if len(rule.MatchResources.Any) > 0 {
rule := getAnyAllAutogenRule(controllerRule.MatchResources.Any, controllers)
controllerRule.MatchResources.Any = rule
} else if len(rule.MatchResources.All) > 0 {
rule := getAnyAllAutogenRule(controllerRule.MatchResources.All, controllers)
controllerRule.MatchResources.All = rule
} else {
controllerRule.MatchResources.Kinds = strings.Split(controllers, ",")
}
if len(rule.ExcludeResources.Any) > 0 {
rule := getAnyAllAutogenRule(controllerRule.ExcludeResources.Any, controllers)
controllerRule.ExcludeResources.Any = rule
} else if len(rule.ExcludeResources.All) > 0 {
rule := getAnyAllAutogenRule(controllerRule.ExcludeResources.All, controllers)
controllerRule.ExcludeResources.All = rule
} else {
if len(exclude.Kinds) != 0 {
controllerRule.ExcludeResources.Kinds = strings.Split(controllers, ",")
}
}
if target := rule.Mutation.GetPatchStrategicMerge(); target != nil {
newMutation := &kyverno.Mutation{}
newMutation.SetPatchStrategicMerge(
map[string]interface{}{
"spec": map[string]interface{}{
"template": target,
},
},
)
controllerRule.Mutation = newMutation.DeepCopy()
return controllerRule
}
if len(rule.Mutation.ForEachMutation) > 0 && rule.Mutation.ForEachMutation != nil {
var newForeachMutation []*kyverno.ForEachMutation
for _, foreach := range rule.Mutation.ForEachMutation {
temp := &kyverno.ForEachMutation{
List: foreach.List,
Context: foreach.Context,
AnyAllConditions: foreach.AnyAllConditions,
}
temp.SetPatchStrategicMerge(
map[string]interface{}{
"spec": map[string]interface{}{
"template": foreach.GetPatchStrategicMerge(),
},
},
)
newForeachMutation = append(newForeachMutation, temp)
}
controllerRule.Mutation = &kyverno.Mutation{
ForEachMutation: newForeachMutation,
}
return controllerRule
}
if target := rule.Validation.GetPattern(); target != nil {
newValidate := &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/template", "pattern"),
}
newValidate.SetPattern(
map[string]interface{}{
"spec": map[string]interface{}{
"template": target,
},
},
)
controllerRule.Validation = newValidate.DeepCopy()
return controllerRule
}
if rule.Validation.Deny != nil {
deny := &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/template", "deny"),
Deny: rule.Validation.Deny,
}
controllerRule.Validation = deny.DeepCopy()
return controllerRule
}
if rule.Validation.GetAnyPattern() != nil {
anyPatterns, err := rule.Validation.DeserializeAnyPattern()
if err != nil {
logger.Error(err, "failed to deserialize anyPattern, expect type array")
}
patterns := validateAnyPattern(anyPatterns)
controllerRule.Validation = &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/template", "anyPattern"),
}
controllerRule.Validation.SetAnyPattern(patterns)
return controllerRule
}
if len(rule.Validation.ForEachValidation) > 0 && rule.Validation.ForEachValidation != nil {
newForeachValidate := make([]*kyverno.ForEachValidation, len(rule.Validation.ForEachValidation))
for i, foreach := range rule.Validation.ForEachValidation {
newForeachValidate[i] = foreach
}
controllerRule.Validation = &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/template", "pattern"),
ForEachValidation: newForeachValidate,
}
return controllerRule
}
if rule.VerifyImages != nil {
newVerifyImages := make([]*kyverno.ImageVerification, len(rule.VerifyImages))
for i, vi := range rule.VerifyImages {
newVerifyImages[i] = vi.DeepCopy()
}
controllerRule.VerifyImages = newVerifyImages
return controllerRule
}
return nil
}
func generateCronJobRule(rule kyverno.Rule, controllers string, log logr.Logger) *kyvernoRule {
logger := log.WithName("handleCronJob")
hasCronJob := strings.Contains(controllers, PodControllerCronJob) || strings.Contains(controllers, "all")
if !hasCronJob {
return nil
}
logger.V(3).Info("generating rule for cronJob")
jobRule := generateRuleForControllers(rule, "Job", logger)
if jobRule == nil {
return nil
}
cronJobRule := jobRule
name := fmt.Sprintf("autogen-cronjob-%s", rule.Name)
if len(name) > 63 {
name = name[:63]
}
cronJobRule.Name = name
if len(jobRule.MatchResources.Any) > 0 {
rule := cronJobAnyAllAutogenRule(cronJobRule.MatchResources.Any)
cronJobRule.MatchResources.Any = rule
} else if len(jobRule.MatchResources.All) > 0 {
rule := cronJobAnyAllAutogenRule(cronJobRule.MatchResources.All)
cronJobRule.MatchResources.All = rule
} else {
cronJobRule.MatchResources.Kinds = []string{PodControllerCronJob}
}
if (jobRule.ExcludeResources) != nil && len(jobRule.ExcludeResources.Any) > 0 {
rule := cronJobAnyAllAutogenRule(cronJobRule.ExcludeResources.Any)
cronJobRule.ExcludeResources.Any = rule
} else if (jobRule.ExcludeResources) != nil && len(jobRule.ExcludeResources.All) > 0 {
rule := cronJobAnyAllAutogenRule(cronJobRule.ExcludeResources.All)
cronJobRule.ExcludeResources.All = rule
} else {
if (jobRule.ExcludeResources) != nil && (len(jobRule.ExcludeResources.Kinds) > 0) {
cronJobRule.ExcludeResources.Kinds = []string{PodControllerCronJob}
}
}
if jobRule.Mutation != nil {
if target := jobRule.Mutation.GetPatchStrategicMerge(); target != nil {
newMutation := &kyverno.Mutation{}
newMutation.SetPatchStrategicMerge(
map[string]interface{}{
"spec": map[string]interface{}{
"jobTemplate": target,
},
},
)
cronJobRule.Mutation = newMutation.DeepCopy()
return cronJobRule
}
}
if jobRule.Validation != nil {
if target := jobRule.Validation.GetPattern(); target != nil {
newValidate := &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/jobTemplate/spec/template", "pattern"),
}
newValidate.SetPattern(
map[string]interface{}{
"spec": map[string]interface{}{
"jobTemplate": target,
},
},
)
cronJobRule.Validation = newValidate.DeepCopy()
return cronJobRule
}
if jobRule.Validation.Deny != nil {
newValidate := &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/jobTemplate/spec/template", "pattern"),
Deny: jobRule.Validation.Deny,
}
cronJobRule.Validation = newValidate.DeepCopy()
return cronJobRule
}
if target := jobRule.Validation.GetAnyPattern(); target != nil {
var patterns []interface{}
anyPatterns, err := jobRule.Validation.DeserializeAnyPattern()
if err != nil {
logger.Error(err, "failed to deserialize anyPattern, expect type array")
}
for _, pattern := range anyPatterns {
newPattern := map[string]interface{}{
"spec": map[string]interface{}{
"jobTemplate": pattern,
},
}
patterns = append(patterns, newPattern)
}
cronJobRule.Validation = &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/jobTemplate/spec/template", "anyPattern"),
}
cronJobRule.Validation.SetAnyPattern(patterns)
return cronJobRule
}
if len(jobRule.Validation.ForEachValidation) > 0 && jobRule.Validation.ForEachValidation != nil {
newForeachValidate := make([]*kyverno.ForEachValidation, len(jobRule.Validation.ForEachValidation))
for i, foreach := range rule.Validation.ForEachValidation {
newForeachValidate[i] = foreach
}
cronJobRule.Validation = &kyverno.Validation{
Message: variables.FindAndShiftReferences(log, rule.Validation.Message, "spec/template", "pattern"),
ForEachValidation: newForeachValidate,
}
return cronJobRule
}
}
if jobRule.Mutation != nil && len(jobRule.Mutation.ForEachMutation) > 0 && jobRule.Mutation.ForEachMutation != nil {
var newForeachMutation []*kyverno.ForEachMutation
for _, foreach := range jobRule.Mutation.ForEachMutation {
temp := &kyverno.ForEachMutation{
List: foreach.List,
Context: foreach.Context,
AnyAllConditions: foreach.AnyAllConditions,
}
temp.SetPatchStrategicMerge(
map[string]interface{}{
"spec": map[string]interface{}{
"jobTemplate": foreach.GetPatchStrategicMerge(),
},
},
)
newForeachMutation = append(newForeachMutation, temp)
}
cronJobRule.Mutation = &kyverno.Mutation{
ForEachMutation: newForeachMutation,
}
return cronJobRule
}
if jobRule.VerifyImages != nil {
newVerifyImages := make([]*kyverno.ImageVerification, len(jobRule.VerifyImages))
for i, vi := range rule.VerifyImages {
newVerifyImages[i] = vi.DeepCopy()
}
cronJobRule.VerifyImages = newVerifyImages
return cronJobRule
}
return nil
}
func updateGenRuleByte(pbyte []byte, kind string, genRule kyvernoRule) (obj []byte) {
// TODO: do we need to unmarshall here ?
if err := json.Unmarshal(pbyte, &genRule); err != nil {
return obj
}
if kind == "Pod" {
obj = []byte(strings.Replace(string(pbyte), "request.object.spec", "request.object.spec.template.spec", -1))
}
if kind == "Cronjob" {
obj = []byte(strings.Replace(string(pbyte), "request.object.spec", "request.object.spec.jobTemplate.spec.template.spec", -1))
}
obj = []byte(strings.Replace(string(obj), "request.object.metadata", "request.object.spec.template.metadata", -1))
return obj
}