1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat: use pointer in rule (exclude field) (#11050)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2024-09-10 13:14:49 +02:00 committed by GitHub
parent 9934c0e61a
commit b5e1c97913
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 219 additions and 178 deletions

View file

@ -46,6 +46,9 @@ func (m *MatchResources) GetKinds() []string {
// Validate implements programmatic validation // Validate implements programmatic validation
func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) { func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
if m == nil {
return errs
}
if len(m.Any) > 0 && len(m.All) > 0 { if len(m.Any) > 0 && len(m.All) > 0 {
errs = append(errs, field.Invalid(path, m, "Can't specify any and all together")) errs = append(errs, field.Invalid(path, m, "Can't specify any and all together"))
} }

View file

@ -65,7 +65,7 @@ type Rule struct {
// criteria can include resource information (e.g. kind, name, namespace, labels) // criteria can include resource information (e.g. kind, name, namespace, labels)
// and admission review request information like the name or role. // and admission review request information like the name or role.
// +optional // +optional
ExcludeResources MatchResources `json:"exclude,omitempty"` ExcludeResources *MatchResources `json:"exclude,omitempty"`
// ImageExtractors defines a mapping from kinds to ImageExtractorConfigs. // ImageExtractors defines a mapping from kinds to ImageExtractorConfigs.
// This config is only valid for verifyImages rules. // This config is only valid for verifyImages rules.
@ -252,6 +252,9 @@ func (r *Rule) ValidateRuleType(path *field.Path) (errs field.ErrorList) {
// ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set // ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set
func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) { func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) {
if r.ExcludeResources == nil {
return errs
}
if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 { if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 {
return errs return errs
} }
@ -266,7 +269,7 @@ func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorL
} }
return errs return errs
} }
if datautils.DeepEqual(r.ExcludeResources, MatchResources{}) { if datautils.DeepEqual(*r.ExcludeResources, MatchResources{}) {
return errs return errs
} }
excludeRoles := sets.New(r.ExcludeResources.Roles...) excludeRoles := sets.New(r.ExcludeResources.Roles...)

View file

@ -1396,7 +1396,11 @@ func (in *Rule) DeepCopyInto(out *Rule) {
} }
} }
in.MatchResources.DeepCopyInto(&out.MatchResources) in.MatchResources.DeepCopyInto(&out.MatchResources)
in.ExcludeResources.DeepCopyInto(&out.ExcludeResources) if in.ExcludeResources != nil {
in, out := &in.ExcludeResources, &out.ExcludeResources
*out = new(MatchResources)
(*in).DeepCopyInto(*out)
}
if in.ImageExtractors != nil { if in.ImageExtractors != nil {
in, out := &in.ImageExtractors, &out.ImageExtractors in, out := &in.ImageExtractors, &out.ImageExtractors
*out = make(ImageExtractorConfigs, len(*in)) *out = make(ImageExtractorConfigs, len(*in))

View file

@ -41,6 +41,9 @@ func (m *MatchResources) GetKinds() []string {
// ValidateNoUserInfo verifies that no user info is used // ValidateNoUserInfo verifies that no user info is used
func (m *MatchResources) ValidateNoUserInfo(path *field.Path) (errs field.ErrorList) { func (m *MatchResources) ValidateNoUserInfo(path *field.Path) (errs field.ErrorList) {
if m == nil {
return errs
}
anyPath := path.Child("any") anyPath := path.Child("any")
for i, filter := range m.Any { for i, filter := range m.Any {
errs = append(errs, filter.UserInfo.ValidateNoUserInfo(anyPath.Index(i))...) errs = append(errs, filter.UserInfo.ValidateNoUserInfo(anyPath.Index(i))...)
@ -54,6 +57,9 @@ func (m *MatchResources) ValidateNoUserInfo(path *field.Path) (errs field.ErrorL
// ValidateResourceWithNoUserInfo implements programmatic validation and verifies that no user info is used // ValidateResourceWithNoUserInfo implements programmatic validation and verifies that no user info is used
func (m *MatchResources) ValidateResourceWithNoUserInfo(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) { func (m *MatchResources) ValidateResourceWithNoUserInfo(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
if m == nil {
return errs
}
if len(m.Any) > 0 && len(m.All) > 0 { if len(m.Any) > 0 && len(m.All) > 0 {
errs = append(errs, field.Invalid(path, m, "Can't specify any and all together")) errs = append(errs, field.Invalid(path, m, "Can't specify any and all together"))
} }
@ -72,6 +78,9 @@ func (m *MatchResources) ValidateResourceWithNoUserInfo(path *field.Path, namesp
// Validate implements programmatic validation // Validate implements programmatic validation
func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) { func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
if m == nil {
return errs
}
if len(m.Any) > 0 && len(m.All) > 0 { if len(m.Any) > 0 && len(m.All) > 0 {
errs = append(errs, field.Invalid(path, m, "Can't specify any and all together")) errs = append(errs, field.Invalid(path, m, "Can't specify any and all together"))
} }

View file

@ -32,7 +32,7 @@ type Rule struct {
// criteria can include resource information (e.g. kind, name, namespace, labels) // criteria can include resource information (e.g. kind, name, namespace, labels)
// and admission review request information like the name or role. // and admission review request information like the name or role.
// +optional // +optional
ExcludeResources MatchResources `json:"exclude,omitempty"` ExcludeResources *MatchResources `json:"exclude,omitempty"`
// ImageExtractors defines a mapping from kinds to ImageExtractorConfigs. // ImageExtractors defines a mapping from kinds to ImageExtractorConfigs.
// This config is only valid for verifyImages rules. // This config is only valid for verifyImages rules.
@ -160,6 +160,9 @@ func (r *Rule) ValidateRuleType(path *field.Path) (errs field.ErrorList) {
// ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set // ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set
func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) { func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) {
if r.ExcludeResources == nil {
return errs
}
if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 { if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 {
return errs return errs
} }

View file

@ -712,7 +712,11 @@ func (in *Rule) DeepCopyInto(out *Rule) {
} }
} }
in.MatchResources.DeepCopyInto(&out.MatchResources) in.MatchResources.DeepCopyInto(&out.MatchResources)
in.ExcludeResources.DeepCopyInto(&out.ExcludeResources) if in.ExcludeResources != nil {
in, out := &in.ExcludeResources, &out.ExcludeResources
*out = new(MatchResources)
(*in).DeepCopyInto(*out)
}
if in.ImageExtractors != nil { if in.ImageExtractors != nil {
in, out := &in.ImageExtractors, &out.ImageExtractors in, out := &in.ImageExtractors, &out.ImageExtractors
*out = make(v1.ImageExtractorConfigs, len(*in)) *out = make(v1.ImageExtractorConfigs, len(*in))

View file

@ -98,6 +98,7 @@ func GetKindsFromPolicy(out io.Writer, policy kyvernov1.PolicyInterface, subreso
} }
knownkinds.Insert(k) knownkinds.Insert(k)
} }
if rule.ExcludeResources != nil {
for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds {
k, err := getKind(kind, subresources, dClient) k, err := getKind(kind, subresources, dClient)
if err != nil { if err != nil {
@ -107,6 +108,7 @@ func GetKindsFromPolicy(out io.Writer, policy kyvernov1.PolicyInterface, subreso
knownkinds.Insert(k) knownkinds.Insert(k)
} }
} }
}
return knownkinds return knownkinds
} }

View file

@ -77,8 +77,8 @@ func CanAutoGen(spec *kyvernov1.Spec) (applyAutoGen bool, controllers sets.Set[s
return false, sets.New("none") return false, sets.New("none")
} }
} }
match, exclude := rule.MatchResources, rule.ExcludeResources match := rule.MatchResources
if !checkAutogenSupport(&needed, match.ResourceDescription, exclude.ResourceDescription) { if !checkAutogenSupport(&needed, match.ResourceDescription) {
debug.Info("skip generating rule on pod controllers: Name / Selector in resource description may not be applicable.", "rule", rule.Name) debug.Info("skip generating rule on pod controllers: Name / Selector in resource description may not be applicable.", "rule", rule.Name)
return false, sets.New[string]() return false, sets.New[string]()
} }
@ -94,6 +94,11 @@ func CanAutoGen(spec *kyvernov1.Spec) (applyAutoGen bool, controllers sets.Set[s
return false, sets.New[string]() return false, sets.New[string]()
} }
} }
if exclude := rule.ExcludeResources; exclude != nil {
if !checkAutogenSupport(&needed, exclude.ResourceDescription) {
debug.Info("skip generating rule on pod controllers: Name / Selector in resource description may not be applicable.", "rule", rule.Name)
return false, sets.New[string]()
}
for _, value := range exclude.Any { for _, value := range exclude.Any {
if !checkAutogenSupport(&needed, value.ResourceDescription) { if !checkAutogenSupport(&needed, value.ResourceDescription) {
debug.Info("skip generating rule on pod controllers: Name / Selector in exclude any block is not applicable.", "rule", rule.Name) debug.Info("skip generating rule on pod controllers: Name / Selector in exclude any block is not applicable.", "rule", rule.Name)
@ -107,6 +112,7 @@ func CanAutoGen(spec *kyvernov1.Spec) (applyAutoGen bool, controllers sets.Set[s
} }
} }
} }
}
if !needed { if !needed {
return false, sets.New[string]() return false, sets.New[string]()
} }
@ -210,7 +216,7 @@ func convertRule(rule kyvernoRule, kind string) (*kyvernov1.Rule, error) {
out.MatchResources = *rule.MatchResources out.MatchResources = *rule.MatchResources
} }
if rule.ExcludeResources != nil { if rule.ExcludeResources != nil {
out.ExcludeResources = *rule.ExcludeResources out.ExcludeResources = rule.ExcludeResources
} }
if rule.Context != nil { if rule.Context != nil {
out.Context = *rule.Context out.Context = *rule.Context

View file

@ -42,7 +42,7 @@ func createRule(rule *kyvernov1.Rule) *kyvernoRule {
if !datautils.DeepEqual(rule.MatchResources, kyvernov1.MatchResources{}) { if !datautils.DeepEqual(rule.MatchResources, kyvernov1.MatchResources{}) {
jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy() jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy()
} }
if !datautils.DeepEqual(rule.ExcludeResources, kyvernov1.MatchResources{}) { if rule.ExcludeResources != nil && !datautils.DeepEqual(*rule.ExcludeResources, kyvernov1.MatchResources{}) {
jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy() jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy()
} }
if !datautils.DeepEqual(rule.Mutation, kyvernov1.Mutation{}) { if !datautils.DeepEqual(rule.Mutation, kyvernov1.Mutation{}) {
@ -84,6 +84,7 @@ func generateRule(name string, rule *kyvernov1.Rule, tplKey, shift string, kinds
} else { } else {
rule.MatchResources.Kinds = kinds rule.MatchResources.Kinds = kinds
} }
if rule.ExcludeResources != nil {
if len(rule.ExcludeResources.Any) > 0 { if len(rule.ExcludeResources.Any) > 0 {
rule.ExcludeResources.Any = grf(rule.ExcludeResources.Any, kinds) rule.ExcludeResources.Any = grf(rule.ExcludeResources.Any, kinds)
} else if len(rule.ExcludeResources.All) > 0 { } else if len(rule.ExcludeResources.All) > 0 {
@ -93,6 +94,7 @@ func generateRule(name string, rule *kyvernov1.Rule, tplKey, shift string, kinds
rule.ExcludeResources.Kinds = kinds rule.ExcludeResources.Kinds = kinds
} }
} }
}
if target := rule.Mutation.GetPatchStrategicMerge(); target != nil { if target := rule.Mutation.GetPatchStrategicMerge(); target != nil {
newMutation := kyvernov1.Mutation{} newMutation := kyvernov1.Mutation{}
newMutation.SetPatchStrategicMerge( newMutation.SetPatchStrategicMerge(
@ -255,8 +257,12 @@ func generateRuleForControllers(rule *kyvernov1.Rule, controllers string) *kyver
return nil return nil
} }
debug.Info("processing rule", "rulename", rule.Name) debug.Info("processing rule", "rulename", rule.Name)
match, exclude := rule.MatchResources, rule.ExcludeResources match := rule.MatchResources
matchKinds, excludeKinds := match.GetKinds(), exclude.GetKinds() matchKinds := match.GetKinds()
var excludeKinds []string
if exclude := rule.ExcludeResources; exclude != nil {
excludeKinds = exclude.GetKinds()
}
if !kubeutils.ContainsKind(matchKinds, "Pod") || (len(excludeKinds) != 0 && !kubeutils.ContainsKind(excludeKinds, "Pod")) { if !kubeutils.ContainsKind(matchKinds, "Pod") || (len(excludeKinds) != 0 && !kubeutils.ContainsKind(excludeKinds, "Pod")) {
return nil return nil
} }

View file

@ -144,7 +144,7 @@ func TestAddOperationsForValidatingWebhookConf(t *testing.T) {
Kinds: []string{"ConfigMap"}, Kinds: []string{"ConfigMap"},
}, },
}, },
ExcludeResources: kyverno.MatchResources{ ExcludeResources: &kyverno.MatchResources{
ResourceDescription: kyverno.ResourceDescription{ ResourceDescription: kyverno.ResourceDescription{
Operations: []kyverno.AdmissionOperation{"DELETE", "CONNECT", "CREATE"}, Operations: []kyverno.AdmissionOperation{"DELETE", "CONNECT", "CREATE"},
}, },
@ -263,7 +263,7 @@ func TestAddOperationsForMutatingtingWebhookConf(t *testing.T) {
Kinds: []string{"Secret"}, Kinds: []string{"Secret"},
}, },
}, },
ExcludeResources: kyverno.MatchResources{ ExcludeResources: &kyverno.MatchResources{
ResourceDescription: kyverno.ResourceDescription{ ResourceDescription: kyverno.ResourceDescription{
Operations: []kyverno.AdmissionOperation{"UPDATE"}, Operations: []kyverno.AdmissionOperation{"UPDATE"},
}, },

View file

@ -263,6 +263,7 @@ func computeOperationsForValidatingWebhookConf(r kyvernov1.Rule, operationStatus
operationStatusMap[webhookConnect] = true operationStatusMap[webhookConnect] = true
operationStatusMap[webhookDelete] = true operationStatusMap[webhookDelete] = true
} }
if r.ExcludeResources != nil {
if r.ExcludeResources.ResourceDescription.Operations != nil { if r.ExcludeResources.ResourceDescription.Operations != nil {
for _, o := range r.ExcludeResources.ResourceDescription.Operations { for _, o := range r.ExcludeResources.ResourceDescription.Operations {
operationStatusMap[string(o)] = false operationStatusMap[string(o)] = false
@ -274,6 +275,7 @@ func computeOperationsForValidatingWebhookConf(r kyvernov1.Rule, operationStatus
if len(r.ExcludeResources.All) != 0 { if len(r.ExcludeResources.All) != 0 {
_, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap) _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap)
} }
}
return operationStatusMap return operationStatusMap
} }
@ -307,6 +309,7 @@ func computeOperationsForMutatingWebhookConf(r kyvernov1.Rule, operationStatusMa
operationStatusMap[webhookCreate] = true operationStatusMap[webhookCreate] = true
operationStatusMap[webhookUpdate] = true operationStatusMap[webhookUpdate] = true
} }
if r.ExcludeResources != nil {
if r.ExcludeResources.ResourceDescription.Operations != nil { if r.ExcludeResources.ResourceDescription.Operations != nil {
for _, o := range r.ExcludeResources.ResourceDescription.Operations { for _, o := range r.ExcludeResources.ResourceDescription.Operations {
operationStatusMap[string(o)] = false operationStatusMap[string(o)] = false
@ -319,6 +322,7 @@ func computeOperationsForMutatingWebhookConf(r kyvernov1.Rule, operationStatusMa
_, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap) _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap)
} }
} }
}
return operationStatusMap return operationStatusMap
} }
@ -375,18 +379,20 @@ func computeResourcesOfRule(r kyvernov1.Rule) []string {
if len(r.MatchResources.All) != 0 { if len(r.MatchResources.All) != 0 {
resources = scanResourceFilterForResources(r.MatchResources.Any) resources = scanResourceFilterForResources(r.MatchResources.Any)
} }
if r.MatchResources.ResourceDescription.Kinds != nil {
resources = append(resources, r.MatchResources.ResourceDescription.Kinds...)
}
if r.ExcludeResources != nil {
if len(r.ExcludeResources.Any) != 0 { if len(r.ExcludeResources.Any) != 0 {
resources = scanResourceFilterForResources(r.MatchResources.Any) resources = scanResourceFilterForResources(r.MatchResources.Any)
} }
if len(r.ExcludeResources.All) != 0 { if len(r.ExcludeResources.All) != 0 {
resources = scanResourceFilterForResources(r.MatchResources.Any) resources = scanResourceFilterForResources(r.MatchResources.Any)
} }
if r.MatchResources.ResourceDescription.Kinds != nil {
resources = append(resources, r.MatchResources.ResourceDescription.Kinds...)
}
if r.ExcludeResources.ResourceDescription.Kinds != nil { if r.ExcludeResources.ResourceDescription.Kinds != nil {
resources = append(resources, r.ExcludeResources.ResourceDescription.Kinds...) resources = append(resources, r.ExcludeResources.ResourceDescription.Kinds...)
} }
}
return resources return resources
} }

View file

@ -241,14 +241,14 @@ func TestComputeOperationsForMutatingWebhookConf(t *testing.T) {
PatchesJSON6902: "add", PatchesJSON6902: "add",
}, },
MatchResources: kyvernov1.MatchResources{}, MatchResources: kyvernov1.MatchResources{},
ExcludeResources: kyvernov1.MatchResources{}, ExcludeResources: &kyvernov1.MatchResources{},
}, },
{ {
Mutation: kyvernov1.Mutation{ Mutation: kyvernov1.Mutation{
PatchesJSON6902: "add", PatchesJSON6902: "add",
}, },
MatchResources: kyvernov1.MatchResources{}, MatchResources: kyvernov1.MatchResources{},
ExcludeResources: kyvernov1.MatchResources{}, ExcludeResources: &kyvernov1.MatchResources{},
}, },
}, },
expectedResult: map[string]bool{ expectedResult: map[string]bool{
@ -264,7 +264,7 @@ func TestComputeOperationsForMutatingWebhookConf(t *testing.T) {
PatchesJSON6902: "add", PatchesJSON6902: "add",
}, },
MatchResources: kyvernov1.MatchResources{}, MatchResources: kyvernov1.MatchResources{},
ExcludeResources: kyvernov1.MatchResources{ ExcludeResources: &kyvernov1.MatchResources{
ResourceDescription: kyvernov1.ResourceDescription{ ResourceDescription: kyvernov1.ResourceDescription{
Operations: []kyvernov1.AdmissionOperation{webhookCreate}, Operations: []kyvernov1.AdmissionOperation{webhookCreate},
}, },
@ -317,7 +317,7 @@ func TestComputeOperationsForValidatingWebhookConf(t *testing.T) {
rules: []kyvernov1.Rule{ rules: []kyvernov1.Rule{
{ {
MatchResources: kyvernov1.MatchResources{}, MatchResources: kyvernov1.MatchResources{},
ExcludeResources: kyvernov1.MatchResources{}, ExcludeResources: &kyvernov1.MatchResources{},
}, },
}, },
expectedResult: map[string]bool{ expectedResult: map[string]bool{
@ -336,7 +336,7 @@ func TestComputeOperationsForValidatingWebhookConf(t *testing.T) {
Operations: []kyvernov1.AdmissionOperation{webhookCreate, webhookUpdate}, Operations: []kyvernov1.AdmissionOperation{webhookCreate, webhookUpdate},
}, },
}, },
ExcludeResources: kyvernov1.MatchResources{ ExcludeResources: &kyvernov1.MatchResources{
ResourceDescription: kyvernov1.ResourceDescription{ ResourceDescription: kyvernov1.ResourceDescription{
Operations: []kyvernov1.AdmissionOperation{webhookDelete}, Operations: []kyvernov1.AdmissionOperation{webhookDelete},
}, },

View file

@ -25,7 +25,7 @@ func matchResource(resource unstructured.Unstructured, rule kyvernov1.Rule) bool
return false return false
} }
} }
if rule.ExcludeResources != nil {
if rule.ExcludeResources.All != nil || rule.ExcludeResources.Any != nil { if rule.ExcludeResources.All != nil || rule.ExcludeResources.Any != nil {
excluded := match.CheckMatchesResources( excluded := match.CheckMatchesResources(
resource, resource,
@ -42,5 +42,6 @@ func matchResource(resource unstructured.Unstructured, rule kyvernov1.Rule) bool
return false return false
} }
} }
}
return true return true
} }

View file

@ -209,7 +209,7 @@ func MatchesResourceDescription(
} }
// check exlude conditions only if match succeeds // check exlude conditions only if match succeeds
if len(reasonsForFailure) == 0 { if len(reasonsForFailure) == 0 && rule.ExcludeResources != nil {
if len(rule.ExcludeResources.Any) > 0 { if len(rule.ExcludeResources.Any) > 0 {
// exclude the object if ANY of the criteria match // exclude the object if ANY of the criteria match
for _, rer := range rule.ExcludeResources.Any { for _, rer := range rule.ExcludeResources.Any {

View file

@ -1963,7 +1963,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
}, },
}, },
}}, }},
ExcludeResources: v1.MatchResources{}, ExcludeResources: &v1.MatchResources{},
} }
// this is the request info that was also passed with the mocked pod // this is the request info that was also passed with the mocked pod
@ -1996,7 +1996,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
}, },
}, },
}}, }},
ExcludeResources: v1.MatchResources{Any: v1.ResourceFilters{}}, ExcludeResources: &v1.MatchResources{Any: v1.ResourceFilters{}},
} }
// Second test: confirm that matching this rule does not create any errors (and raise if err != nil) // Second test: confirm that matching this rule does not create any errors (and raise if err != nil)
@ -2005,7 +2005,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
} }
// Now we extend the previous rule to have an Exclude part. Making it 'not-empty' should make the exclude-code run. // Now we extend the previous rule to have an Exclude part. Making it 'not-empty' should make the exclude-code run.
rule2.ExcludeResources = v1.MatchResources{Any: v1.ResourceFilters{ rule2.ExcludeResources = &v1.MatchResources{Any: v1.ResourceFilters{
v1.ResourceFilter{ v1.ResourceFilter{
ResourceDescription: v1.ResourceDescription{ ResourceDescription: v1.ResourceDescription{
Kinds: []string{"Pod"}, Kinds: []string{"Pod"},
@ -2461,7 +2461,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
rule := v1.Rule{ rule := v1.Rule{
MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}, MatchResources: v1.MatchResources{ResourceDescription: resourceDescription},
ExcludeResources: v1.MatchResources{ResourceDescription: resourceDescriptionExclude}, ExcludeResources: &v1.MatchResources{ResourceDescription: resourceDescriptionExclude},
} }
if err := MatchesResourceDescription(*resource, rule, v2.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err == nil { if err := MatchesResourceDescription(*resource, rule, v2.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err == nil {

View file

@ -179,7 +179,7 @@ func createRule(f *fuzz.ConsumeFuzzer) (*kyvernov1.Rule, error) {
if err != nil { if err != nil {
return rule, err return rule, err
} }
rule.ExcludeResources = *er rule.ExcludeResources = er
} }
setRawAnyAllConditions, err := f.GetBool() setRawAnyAllConditions, err := f.GetBool()

View file

@ -57,7 +57,7 @@ func BuildValidatingAdmissionPolicy(
} }
// convert the exclude block // convert the exclude block
exclude := rule.ExcludeResources if exclude := rule.ExcludeResources; exclude != nil {
if !exclude.ResourceDescription.IsEmpty() { if !exclude.ResourceDescription.IsEmpty() {
if err := translateResource(discoveryClient, &matchResources, &excludeRules, exclude.ResourceDescription, false); err != nil { if err := translateResource(discoveryClient, &matchResources, &excludeRules, exclude.ResourceDescription, false); err != nil {
return err return err
@ -74,6 +74,7 @@ func BuildValidatingAdmissionPolicy(
return err return err
} }
} }
}
// convert the exceptions if exist // convert the exceptions if exist
for _, exception := range exceptions { for _, exception := range exceptions {

View file

@ -72,25 +72,16 @@ func checkPolicy(spec *kyvernov1.Spec) (bool, string) {
} }
// check the matched/excluded resources of the CEL rule. // check the matched/excluded resources of the CEL rule.
match, exclude := rule.MatchResources, rule.ExcludeResources match := rule.MatchResources
if ok, msg := checkUserInfo(match.UserInfo); !ok { if ok, msg := checkUserInfo(match.UserInfo); !ok {
return false, msg return false, msg
} }
if ok, msg := checkUserInfo(exclude.UserInfo); !ok {
return false, msg
}
if ok, msg := checkResources(match.ResourceDescription, true); !ok { if ok, msg := checkResources(match.ResourceDescription, true); !ok {
return false, msg return false, msg
} }
if ok, msg := checkResources(exclude.ResourceDescription, false); !ok {
return false, msg
}
if ok, msg := checkResourceFilter(match.Any, true); !ok { if ok, msg := checkResourceFilter(match.Any, true); !ok {
return false, msg return false, msg
} }
if len(match.All) > 1 { if len(match.All) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the match block is not applicable." msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the match block is not applicable."
return false, msg return false, msg
@ -98,11 +89,17 @@ func checkPolicy(spec *kyvernov1.Spec) (bool, string) {
if ok, msg := checkResourceFilter(match.All, true); !ok { if ok, msg := checkResourceFilter(match.All, true); !ok {
return false, msg return false, msg
} }
if rule.ExcludeResources != nil {
exclude := rule.ExcludeResources
if ok, msg := checkUserInfo(exclude.UserInfo); !ok {
return false, msg
}
if ok, msg := checkResources(exclude.ResourceDescription, false); !ok {
return false, msg
}
if ok, msg := checkResourceFilter(exclude.Any, false); !ok { if ok, msg := checkResourceFilter(exclude.Any, false); !ok {
return false, msg return false, msg
} }
if len(exclude.All) > 1 { if len(exclude.All) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the exclude block is not applicable." msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the exclude block is not applicable."
return false, msg return false, msg
@ -110,6 +107,7 @@ func checkPolicy(spec *kyvernov1.Spec) (bool, string) {
if ok, msg := checkResourceFilter(exclude.All, false); !ok { if ok, msg := checkResourceFilter(exclude.All, false); !ok {
return false, msg return false, msg
} }
}
return true, msg return true, msg
} }

View file

@ -43,11 +43,6 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error {
if path := userInfoDefined(rule.MatchResources.UserInfo); path != "" { if path := userInfoDefined(rule.MatchResources.UserInfo); path != "" {
return fmt.Errorf("invalid variable used at path: spec/rules[%d]/match/%s", idx, path) return fmt.Errorf("invalid variable used at path: spec/rules[%d]/match/%s", idx, path)
} }
if path := userInfoDefined(rule.ExcludeResources.UserInfo); path != "" {
return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/%s", idx, path)
}
if len(rule.MatchResources.Any) > 0 { if len(rule.MatchResources.Any) > 0 {
for i, value := range rule.MatchResources.Any { for i, value := range rule.MatchResources.Any {
if path := userInfoDefined(value.UserInfo); path != "" { if path := userInfoDefined(value.UserInfo); path != "" {
@ -55,7 +50,6 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error {
} }
} }
} }
if len(rule.MatchResources.All) > 0 { if len(rule.MatchResources.All) > 0 {
for i, value := range rule.MatchResources.All { for i, value := range rule.MatchResources.All {
if path := userInfoDefined(value.UserInfo); path != "" { if path := userInfoDefined(value.UserInfo); path != "" {
@ -63,7 +57,10 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error {
} }
} }
} }
if rule.ExcludeResources != nil {
if path := userInfoDefined(rule.ExcludeResources.UserInfo); path != "" {
return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/%s", idx, path)
}
if len(rule.ExcludeResources.All) > 0 { if len(rule.ExcludeResources.All) > 0 {
for i, value := range rule.ExcludeResources.All { for i, value := range rule.ExcludeResources.All {
if path := userInfoDefined(value.UserInfo); path != "" { if path := userInfoDefined(value.UserInfo); path != "" {
@ -71,7 +68,6 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error {
} }
} }
} }
if len(rule.ExcludeResources.Any) > 0 { if len(rule.ExcludeResources.Any) > 0 {
for i, value := range rule.ExcludeResources.Any { for i, value := range rule.ExcludeResources.Any {
if path := userInfoDefined(value.UserInfo); path != "" { if path := userInfoDefined(value.UserInfo); path != "" {
@ -79,7 +75,7 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error {
} }
} }
} }
}
return nil return nil
} }

View file

@ -236,7 +236,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
for i, rule := range rules { for i, rule := range rules {
match := rule.MatchResources match := rule.MatchResources
exclude := rule.ExcludeResources
for j, value := range match.Any { for j, value := range match.Any {
if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].match.any[%d].kinds: %v", i, j, err) return warnings, fmt.Errorf("path: spec.rules[%d].match.any[%d].kinds: %v", i, j, err)
@ -247,6 +246,10 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
return warnings, fmt.Errorf("path: spec.rules[%d].match.all[%d].kinds: %v", i, j, err) return warnings, fmt.Errorf("path: spec.rules[%d].match.all[%d].kinds: %v", i, j, err)
} }
} }
if err := validateKinds(rule.MatchResources.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].match.kinds: %v", i, err)
}
if exclude := rule.ExcludeResources; exclude != nil {
for j, value := range exclude.Any { for j, value := range exclude.Any {
if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].exclude.any[%d].kinds: %v", i, j, err) return warnings, fmt.Errorf("path: spec.rules[%d].exclude.any[%d].kinds: %v", i, j, err)
@ -257,15 +260,11 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
return warnings, fmt.Errorf("path: spec.rules[%d].exclude.all[%d].kinds: %v", i, j, err) return warnings, fmt.Errorf("path: spec.rules[%d].exclude.all[%d].kinds: %v", i, j, err)
} }
} }
if err := validateKinds(exclude.Kinds, rule, mock, background, client); err != nil {
if err := validateKinds(rule.MatchResources.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].match.kinds: %v", i, err)
}
if err := validateKinds(rule.ExcludeResources.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].exclude.kinds: %v", i, err) return warnings, fmt.Errorf("path: spec.rules[%d].exclude.kinds: %v", i, err)
} }
} }
}
for i, rule := range rules { for i, rule := range rules {
rulePath := rulesPath.Index(i) rulePath := rulesPath.Index(i)
@ -377,12 +376,13 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
} }
match := rule.MatchResources match := rule.MatchResources
exclude := rule.ExcludeResources
matchKinds := match.GetKinds() matchKinds := match.GetKinds()
excludeKinds := exclude.GetKinds() var allKinds []string
allKinds := make([]string, 0, len(matchKinds)+len(excludeKinds))
allKinds = append(allKinds, matchKinds...) allKinds = append(allKinds, matchKinds...)
if exclude := rule.ExcludeResources; exclude != nil {
excludeKinds := exclude.GetKinds()
allKinds = append(allKinds, excludeKinds...) allKinds = append(allKinds, excludeKinds...)
}
if rule.HasValidate() { if rule.HasValidate() {
validationElem := rule.Validation.DeepCopy() validationElem := rule.Validation.DeepCopy()
if validationElem.Deny != nil { if validationElem.Deny != nil {
@ -691,17 +691,16 @@ func jsonPatchPathHasVariables(patch string) error {
return nil return nil
} }
func objectHasVariables(object interface{}) error { func objectHasVariables(object any) error {
var err error if object != nil {
objectJSON, err := json.Marshal(object) objectJSON, err := json.Marshal(object)
if err != nil { if err != nil {
return err return err
} }
if len(regexVariables.FindAllStringSubmatch(string(objectJSON), -1)) > 0 { if len(regexVariables.FindAllStringSubmatch(string(objectJSON), -1)) > 0 {
return fmt.Errorf("invalid variables") return fmt.Errorf("invalid variables")
} }
}
return nil return nil
} }
@ -963,22 +962,22 @@ func ruleOnlyDealsWithResourceMetaData(rule kyvernov1.Rule) bool {
func validateResources(path *field.Path, rule kyvernov1.Rule) (string, error) { func validateResources(path *field.Path, rule kyvernov1.Rule) (string, error) {
// validate userInfo in match and exclude // validate userInfo in match and exclude
if errs := rule.ExcludeResources.UserInfo.Validate(path.Child("exclude")); len(errs) != 0 { if exclude := rule.ExcludeResources; exclude != nil {
if errs := exclude.UserInfo.Validate(path.Child("exclude")); len(errs) != 0 {
return "exclude", errs.ToAggregate() return "exclude", errs.ToAggregate()
} }
if (len(exclude.Any) > 0 || len(exclude.All) > 0) && !datautils.DeepEqual(exclude.ResourceDescription, kyvernov1.ResourceDescription{}) {
return "exclude.", fmt.Errorf("can't specify any/all together with exclude resources")
}
if len(exclude.Any) > 0 && len(exclude.All) > 0 {
return "match.", fmt.Errorf("can't specify any and all together")
}
}
if (len(rule.MatchResources.Any) > 0 || len(rule.MatchResources.All) > 0) && !datautils.DeepEqual(rule.MatchResources.ResourceDescription, kyvernov1.ResourceDescription{}) { if (len(rule.MatchResources.Any) > 0 || len(rule.MatchResources.All) > 0) && !datautils.DeepEqual(rule.MatchResources.ResourceDescription, kyvernov1.ResourceDescription{}) {
return "match.", fmt.Errorf("can't specify any/all together with match resources") return "match.", fmt.Errorf("can't specify any/all together with match resources")
} }
if (len(rule.ExcludeResources.Any) > 0 || len(rule.ExcludeResources.All) > 0) && !datautils.DeepEqual(rule.ExcludeResources.ResourceDescription, kyvernov1.ResourceDescription{}) {
return "exclude.", fmt.Errorf("can't specify any/all together with exclude resources")
}
if len(rule.ExcludeResources.Any) > 0 && len(rule.ExcludeResources.All) > 0 {
return "match.", fmt.Errorf("can't specify any and all together")
}
if len(rule.MatchResources.Any) > 0 { if len(rule.MatchResources.Any) > 0 {
for _, rmr := range rule.MatchResources.Any { for _, rmr := range rule.MatchResources.Any {
// matched resources // matched resources