1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 09:26:54 +00:00
kyverno/pkg/cel/autogen/rule.go
Mariam Fahmy b8c6931aa5
feat: add autogen package for ValidatingPolicies (#11996)
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
2025-01-27 12:36:11 +00:00

212 lines
6.4 KiB
Go

package autogen
import (
"bytes"
"encoding/json"
"slices"
"strings"
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
)
type AutogenRule struct {
MatchConstraints *admissionregistrationv1.MatchResources `json:"matchConstraints,omitempty"`
MatchConditions []admissionregistrationv1.MatchCondition `json:"matchConditions,omitempty"`
Validations []admissionregistrationv1.Validation `json:"validations,omitempty"`
AuditAnnotation []admissionregistrationv1.AuditAnnotation `json:"auditAnnotations,omitempty"`
Variables []admissionregistrationv1.Variable `json:"variables,omitempty"`
}
func generateCronJobRule(spec *kyvernov2alpha1.ValidatingPolicySpec, controllers string) (*AutogenRule, error) {
operations := spec.MatchConstraints.ResourceRules[0].Operations
// create a resource rule for the cronjob resource
matchConstraints := createMatchConstraints(controllers, operations)
// convert match conditions
matchConditions := spec.MatchConditions
if bytes, err := json.Marshal(matchConditions); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, controllers)
if err := json.Unmarshal(bytes, &matchConditions); err != nil {
return nil, err
}
}
// convert validations
validations := spec.Validations
for i := range validations {
if bytes, err := json.Marshal(validations[i]); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, controllers)
if err := json.Unmarshal(bytes, &validations[i]); err != nil {
return nil, err
}
}
}
// convert audit annotations
auditAnnotations := spec.AuditAnnotations
if bytes, err := json.Marshal(auditAnnotations); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, controllers)
if err := json.Unmarshal(bytes, &auditAnnotations); err != nil {
return nil, err
}
}
// convert variables
variables := spec.Variables
if bytes, err := json.Marshal(variables); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, controllers)
if err := json.Unmarshal(bytes, &variables); err != nil {
return nil, err
}
}
return &AutogenRule{
MatchConstraints: matchConstraints,
MatchConditions: matchConditions,
Validations: validations,
AuditAnnotation: auditAnnotations,
Variables: variables,
}, nil
}
func generateRuleForControllers(spec *kyvernov2alpha1.ValidatingPolicySpec, controllers string) (*AutogenRule, error) {
operations := spec.MatchConstraints.ResourceRules[0].Operations
// create a resource rule for pod controllers
matchConstraints := createMatchConstraints(controllers, operations)
// convert match conditions
matchConditions := spec.MatchConditions
if bytes, err := json.Marshal(matchConditions); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, "pods")
if err := json.Unmarshal(bytes, &matchConditions); err != nil {
return nil, err
}
}
// convert validations
validations := spec.Validations
if bytes, err := json.Marshal(validations); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, "pods")
if err := json.Unmarshal(bytes, &validations); err != nil {
return nil, err
}
}
// convert audit annotations
auditAnnotations := spec.AuditAnnotations
if bytes, err := json.Marshal(auditAnnotations); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, "pods")
if err := json.Unmarshal(bytes, &auditAnnotations); err != nil {
return nil, err
}
}
// convert variables
variables := spec.Variables
if bytes, err := json.Marshal(variables); err != nil {
return nil, err
} else {
bytes = updateFields(bytes, "pods")
if err := json.Unmarshal(bytes, &variables); err != nil {
return nil, err
}
}
return &AutogenRule{
MatchConstraints: matchConstraints,
MatchConditions: matchConditions,
Validations: validations,
AuditAnnotation: auditAnnotations,
Variables: variables,
}, nil
}
func createMatchConstraints(controllers string, operations []admissionregistrationv1.OperationType) *admissionregistrationv1.MatchResources {
resources := strings.Split(controllers, ",")
var rules []admissionregistrationv1.NamedRuleWithOperations
for _, resource := range resources {
if resource == "jobs" || resource == "cronjobs" {
rules = append(rules, admissionregistrationv1.NamedRuleWithOperations{
RuleWithOperations: admissionregistrationv1.RuleWithOperations{
Rule: admissionregistrationv1.Rule{
Resources: []string{resource},
APIGroups: []string{"batch"},
APIVersions: []string{"v1"},
},
Operations: operations,
},
})
} else {
newRule := true
for i := range rules {
if slices.Contains(rules[i].APIGroups, "apps") && slices.Contains(rules[i].APIVersions, "v1") {
rules[i].Resources = append(rules[i].Resources, resource)
newRule = false
break
}
}
if newRule {
rules = append(rules, admissionregistrationv1.NamedRuleWithOperations{
RuleWithOperations: admissionregistrationv1.RuleWithOperations{
Rule: admissionregistrationv1.Rule{
Resources: []string{resource},
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
},
Operations: operations,
},
})
}
}
}
return &admissionregistrationv1.MatchResources{
ResourceRules: rules,
}
}
var (
podReplacementRules [][2][]byte = [][2][]byte{
{[]byte("object.spec"), []byte("object.spec.template.spec")},
{[]byte("oldObject.spec"), []byte("oldObject.spec.template.spec")},
{[]byte("object.metadata"), []byte("object.spec.template.metadata")},
{[]byte("oldObject.metadata"), []byte("oldObject.spec.template.metadata")},
}
cronJobReplacementRules [][2][]byte = [][2][]byte{
{[]byte("object.spec"), []byte("object.spec.jobTemplate.spec.template.spec")},
{[]byte("oldObject.spec"), []byte("oldObject.spec.jobTemplate.spec.template.spec")},
{[]byte("object.metadata"), []byte("object.spec.jobTemplate.spec.template.metadata")},
{[]byte("oldObject.metadata"), []byte("oldObject.spec.jobTemplate.spec.template.metadata")},
}
)
func updateFields(data []byte, resource string) []byte {
switch resource {
case "pods":
for _, replacement := range podReplacementRules {
data = bytes.ReplaceAll(data, replacement[0], replacement[1])
}
case "cronjobs":
for _, replacement := range cronJobReplacementRules {
data = bytes.ReplaceAll(data, replacement[0], replacement[1])
}
}
return data
}