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
Charles-Edouard Brétéché ce21ac50b3
feat: add replicaset and replicationcontroller to autogen (#4975)
* fix: missing autogen rules in status

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* feat: add replicaset and replicationcontroller to autogen

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* merge main

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix tests

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix tests

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
Co-authored-by: shuting <shuting@nirmata.com>
2022-11-14 10:18:45 +00:00

319 lines
11 KiB
Go

package autogen
import (
"reflect"
"strings"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/utils"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
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 *kyvernov1.MatchResources `json:"match"`
ExcludeResources *kyvernov1.MatchResources `json:"exclude,omitempty"`
Context *[]kyvernov1.ContextEntry `json:"context,omitempty"`
AnyAllConditions *apiextensions.JSON `json:"preconditions,omitempty"`
Mutation *kyvernov1.Mutation `json:"mutate,omitempty"`
Validation *kyvernov1.Validation `json:"validate,omitempty"`
VerifyImages []kyvernov1.ImageVerification `json:"verifyImages,omitempty" yaml:"verifyImages,omitempty"`
}
func createRule(rule *kyvernov1.Rule) *kyvernoRule {
if rule == nil {
return nil
}
jsonFriendlyStruct := kyvernoRule{
Name: rule.Name,
VerifyImages: rule.VerifyImages,
}
if !reflect.DeepEqual(rule.MatchResources, kyvernov1.MatchResources{}) {
jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy()
}
if !reflect.DeepEqual(rule.ExcludeResources, kyvernov1.MatchResources{}) {
jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy()
}
if !reflect.DeepEqual(rule.Mutation, kyvernov1.Mutation{}) {
jsonFriendlyStruct.Mutation = rule.Mutation.DeepCopy()
}
if !reflect.DeepEqual(rule.Validation, kyvernov1.Validation{}) {
jsonFriendlyStruct.Validation = rule.Validation.DeepCopy()
}
kyvernoAnyAllConditions, _ := utils.ApiextensionsJsonToKyvernoConditions(rule.GetAnyAllConditions())
switch typedAnyAllConditions := kyvernoAnyAllConditions.(type) {
case kyvernov1.AnyAllConditions:
if !reflect.DeepEqual(typedAnyAllConditions, kyvernov1.AnyAllConditions{}) {
jsonFriendlyStruct.AnyAllConditions = rule.DeepCopy().RawAnyAllConditions
}
case []kyvernov1.Condition:
if len(typedAnyAllConditions) > 0 {
jsonFriendlyStruct.AnyAllConditions = rule.DeepCopy().RawAnyAllConditions
}
}
if len(rule.Context) > 0 {
jsonFriendlyStruct.Context = &rule.DeepCopy().Context
}
return &jsonFriendlyStruct
}
type generateResourceFilters func(kyvernov1.ResourceFilters, []string) kyvernov1.ResourceFilters
func generateRule(name string, rule *kyvernov1.Rule, tplKey, shift string, kinds []string, grf generateResourceFilters) *kyvernov1.Rule {
if rule == nil {
return nil
}
rule = rule.DeepCopy()
rule.Name = name
// overwrite Kinds by pod controllers defined in the annotation
if len(rule.MatchResources.Any) > 0 {
rule.MatchResources.Any = grf(rule.MatchResources.Any, kinds)
} else if len(rule.MatchResources.All) > 0 {
rule.MatchResources.All = grf(rule.MatchResources.All, kinds)
} else {
rule.MatchResources.Kinds = kinds
}
if len(rule.ExcludeResources.Any) > 0 {
rule.ExcludeResources.Any = grf(rule.ExcludeResources.Any, kinds)
} else if len(rule.ExcludeResources.All) > 0 {
rule.ExcludeResources.All = grf(rule.ExcludeResources.All, kinds)
} else {
if len(rule.ExcludeResources.Kinds) != 0 {
rule.ExcludeResources.Kinds = kinds
}
}
if target := rule.Mutation.GetPatchStrategicMerge(); target != nil {
newMutation := kyvernov1.Mutation{}
newMutation.SetPatchStrategicMerge(
map[string]interface{}{
"spec": map[string]interface{}{
tplKey: target,
},
},
)
rule.Mutation = newMutation
return rule
}
if len(rule.Mutation.ForEachMutation) > 0 && rule.Mutation.ForEachMutation != nil {
var newForeachMutation []kyvernov1.ForEachMutation
for _, foreach := range rule.Mutation.ForEachMutation {
temp := kyvernov1.ForEachMutation{
List: foreach.List,
Context: foreach.Context,
AnyAllConditions: foreach.AnyAllConditions,
}
temp.SetPatchStrategicMerge(
map[string]interface{}{
"spec": map[string]interface{}{
tplKey: foreach.GetPatchStrategicMerge(),
},
},
)
newForeachMutation = append(newForeachMutation, temp)
}
rule.Mutation = kyvernov1.Mutation{
ForEachMutation: newForeachMutation,
}
return rule
}
if target := rule.Validation.GetPattern(); target != nil {
newValidate := kyvernov1.Validation{
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "pattern"),
}
newValidate.SetPattern(
map[string]interface{}{
"spec": map[string]interface{}{
tplKey: target,
},
},
)
rule.Validation = newValidate
return rule
}
if rule.Validation.Deny != nil {
deny := kyvernov1.Validation{
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "deny"),
Deny: rule.Validation.Deny,
}
rule.Validation = deny
return rule
}
if rule.Validation.PodSecurity != nil {
newExclude := make([]kyvernov1.PodSecurityStandard, len(rule.Validation.PodSecurity.Exclude))
copy(newExclude, rule.Validation.PodSecurity.Exclude)
podSecurity := kyvernov1.Validation{
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "podSecurity"),
PodSecurity: &kyvernov1.PodSecurity{
Level: rule.Validation.PodSecurity.Level,
Version: rule.Validation.PodSecurity.Version,
Exclude: newExclude,
},
}
rule.Validation = podSecurity
return rule
}
if rule.Validation.GetAnyPattern() != nil {
anyPatterns, err := rule.Validation.DeserializeAnyPattern()
if err != nil {
logger.Error(err, "failed to deserialize anyPattern, expect type array")
}
var patterns []interface{}
for _, pattern := range anyPatterns {
newPattern := map[string]interface{}{
"spec": map[string]interface{}{
tplKey: pattern,
},
}
patterns = append(patterns, newPattern)
}
rule.Validation = kyvernov1.Validation{
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "anyPattern"),
}
rule.Validation.SetAnyPattern(patterns)
return rule
}
if len(rule.Validation.ForEachValidation) > 0 && rule.Validation.ForEachValidation != nil {
newForeachValidate := make([]kyvernov1.ForEachValidation, len(rule.Validation.ForEachValidation))
copy(newForeachValidate, rule.Validation.ForEachValidation)
rule.Validation = kyvernov1.Validation{
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "pattern"),
ForEachValidation: newForeachValidate,
}
return rule
}
if rule.VerifyImages != nil {
newVerifyImages := make([]kyvernov1.ImageVerification, len(rule.VerifyImages))
for i, vi := range rule.VerifyImages {
newVerifyImages[i] = *vi.DeepCopy()
}
rule.VerifyImages = newVerifyImages
return rule
}
return nil
}
func getAutogenRuleName(prefix string, name string) string {
name = prefix + "-" + name
if len(name) > 63 {
name = name[:63]
}
return name
}
func isAutogenRuleName(name string) bool {
return strings.HasPrefix(name, "autogen-")
}
func getAnyAllAutogenRule(v kyvernov1.ResourceFilters, match string, kinds []string) kyvernov1.ResourceFilters {
anyKind := v.DeepCopy()
for i, value := range v {
if kubeutils.ContainsKind(value.Kinds, match) {
anyKind[i].Kinds = kinds
}
}
return anyKind
}
func generateRuleForControllers(rule *kyvernov1.Rule, controllers string) *kyvernov1.Rule {
if isAutogenRuleName(rule.Name) || controllers == "" {
logger.V(5).Info("skip generateRuleForControllers")
return nil
}
logger.V(3).Info("processing rule", "rulename", rule.Name)
match, exclude := rule.MatchResources, rule.ExcludeResources
matchKinds, excludeKinds := match.GetKinds(), exclude.GetKinds()
if !kubeutils.ContainsKind(matchKinds, "Pod") || (len(excludeKinds) != 0 && !kubeutils.ContainsKind(excludeKinds, "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,
"ReplicaSet": 1,
"ReplicationController": 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,ReplicaSet,ReplicationController"
} else {
controllers = strings.Join(controllersValidated, ",")
}
}
return generateRule(
getAutogenRuleName("autogen", rule.Name),
rule,
"template",
"spec/template",
strings.Split(controllers, ","),
func(r kyvernov1.ResourceFilters, kinds []string) kyvernov1.ResourceFilters {
return getAnyAllAutogenRule(r, "Pod", kinds)
},
)
}
func generateCronJobRule(rule *kyvernov1.Rule, controllers string) *kyvernov1.Rule {
hasCronJob := strings.Contains(controllers, PodControllerCronJob) || strings.Contains(controllers, "all")
if !hasCronJob {
return nil
}
logger.V(3).Info("generating rule for cronJob")
return generateRule(
getAutogenRuleName("autogen-cronjob", rule.Name),
generateRuleForControllers(rule, controllers),
"jobTemplate",
"spec/jobTemplate/spec/template",
[]string{PodControllerCronJob},
func(r kyvernov1.ResourceFilters, kinds []string) kyvernov1.ResourceFilters {
return getAnyAllAutogenRule(r, "Job", kinds)
},
)
}
func updateGenRuleByte(pbyte []byte, kind string) (obj []byte) {
if kind == "Pod" {
obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.template.spec"))
}
if kind == "Cronjob" {
obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.jobTemplate.spec.template.spec"))
}
obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.template.metadata"))
return obj
}
func updateRestrictedFields(pbyte []byte, kind string) (obj []byte) {
if kind == "Pod" {
obj = []byte(strings.ReplaceAll(string(pbyte), `"restrictedField":"spec`, `"restrictedField":"spec.template.spec`))
}
if kind == "Cronjob" {
obj = []byte(strings.ReplaceAll(string(pbyte), `"restrictedField":"spec`, `"restrictedField":"spec.jobTemplate.spec.template.spec`))
}
obj = []byte(strings.ReplaceAll(string(obj), "metadata", "spec.template.metadata"))
return obj
}