Skip to content

Commit

Permalink
Implement the match logic (#932)
Browse files Browse the repository at this point in the history
* Implement matching logic

Adding a Match function that accepts a Match instance, an object to be matched against
and its related namespace.

Adding a AppliesTo function that accepts a ApplyTo slice and an object to be matched against.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>

* Integrate the mutators with the match logic

Each mutator matches uses the Matches function to check if the given object matches
the mutator. This also includes a change in the mutator interface as metav1.Object
do not have accessors for kind / group.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>

Co-authored-by: Rita Zhang <rita.z.zhang@gmail.com>
  • Loading branch information
fedepaol and ritazh committed Nov 30, 2020
1 parent 57dabe2 commit 0af9133
Show file tree
Hide file tree
Showing 7 changed files with 652 additions and 12 deletions.
12 changes: 8 additions & 4 deletions pkg/mutation/assign_mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"github.com/open-policy-agent/gatekeeper/pkg/mutation/path/parser"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)

// AssignMutator is a mutator object built out of a
Expand All @@ -22,9 +22,13 @@ type AssignMutator struct {
// AssignMutator implements mutatorWithSchema
var _ MutatorWithSchema = &AssignMutator{}

func (m *AssignMutator) Matches(obj metav1.Object, ns *corev1.Namespace) bool {
// TODO implement using matches function
return false
func (m *AssignMutator) Matches(obj runtime.Object, ns *corev1.Namespace) bool {
matches, err := Matches(m.assign.Spec.Match, obj, ns)
if err != nil {
log.Error(err, "AssignMutator.Matches failed", "assign", m.assign.Name)
return false
}
return matches
}

func (m *AssignMutator) Mutate(obj *unstructured.Unstructured) error {
Expand Down
12 changes: 8 additions & 4 deletions pkg/mutation/assignmeta_mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/open-policy-agent/gatekeeper/pkg/mutation/path/parser"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)

var (
Expand Down Expand Up @@ -44,9 +44,13 @@ type AssignMetadataMutator struct {
// assignMetadataMutator implements mutator
var _ Mutator = &AssignMetadataMutator{}

func (m *AssignMetadataMutator) Matches(obj metav1.Object, ns *corev1.Namespace) bool {
// TODO implement using matches function
return false
func (m *AssignMetadataMutator) Matches(obj runtime.Object, ns *corev1.Namespace) bool {
matches, err := Matches(m.assignMetadata.Spec.Match, obj, ns)
if err != nil {
log.Error(err, "AssignMetadataMutator.Matches failed", "assignMeta", m.assignMetadata.Name)
return false
}
return matches
}

func (m *AssignMetadataMutator) Mutate(obj *unstructured.Unstructured) error {
Expand Down
157 changes: 157 additions & 0 deletions pkg/mutation/match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package mutation

import (
"fmt"

mutationsv1 "github.com/open-policy-agent/gatekeeper/apis/mutations/v1alpha1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)

// Matches verifies if the given object belonging to the given namespace
// matches the current mutator.
func Matches(match mutationsv1.Match, obj runtime.Object, ns *corev1.Namespace) (bool, error) {
meta, err := meta.Accessor(obj)
if err != nil {
return false, fmt.Errorf("Accessor failed for %s", obj.GetObjectKind().GroupVersionKind().Kind)
}

foundMatch := false

for _, kk := range match.Kinds {
kindMatches := false
groupMatches := false

for _, k := range kk.Kinds {
if k == "*" || k == obj.GetObjectKind().GroupVersionKind().Kind {
kindMatches = true
break
}
}
if len(kk.Kinds) == 0 {
kindMatches = true
}

for _, g := range kk.APIGroups {
if g == "*" || g == obj.GetObjectKind().GroupVersionKind().Group {
groupMatches = true
break
}
}
if len(kk.APIGroups) == 0 {
groupMatches = true
}

if kindMatches && groupMatches {
foundMatch = true
}
}
if len(match.Kinds) == 0 {
foundMatch = true
}

if !foundMatch {
return false, nil
}

if match.Scope == apiextensionsv1beta1.ClusterScoped &&
meta.GetNamespace() != "" {
return false, nil
}

if match.Scope == apiextensionsv1beta1.NamespaceScoped &&
meta.GetNamespace() == "" {
return false, nil
}

found := false
for _, n := range match.Namespaces {
if meta.GetNamespace() == n {
found = true
break
}
}
if !found && len(match.Namespaces) > 0 {
return false, nil
}

for _, n := range match.ExcludedNamespaces {
if meta.GetNamespace() == n {
return false, nil
}
}
if match.LabelSelector != nil {
selector, err := metav1.LabelSelectorAsSelector(match.LabelSelector)
if err != nil {
return false, err
}
if !selector.Matches(labels.Set(meta.GetLabels())) {
return false, nil
}
}

if match.NamespaceSelector != nil {
selector, err := metav1.LabelSelectorAsSelector(match.NamespaceSelector)
if err != nil {
return false, err
}

switch {
case isNamespace(obj): // if the object is a namespace, namespace selector matches against the object
if !selector.Matches(labels.Set(meta.GetLabels())) {
return false, nil
}
case meta.GetNamespace() == "":
// cluster scoped, matches by default
case !selector.Matches(labels.Set(ns.Labels)):
return false, nil
}
}

return true, nil
}

// AppliesTo checks if any item the given slice of ApplyTo applies to the given object
func AppliesTo(applyTo []mutationsv1.ApplyTo, obj *unstructured.Unstructured) bool {
for _, apply := range applyTo {
matchesGroup := false
matchesVersion := false
matchesKind := false

gvk := obj.GroupVersionKind()
for _, g := range apply.Groups {
if g == gvk.Group {
matchesGroup = true
break
}
}
for _, g := range apply.Versions {
if g == gvk.Version {
matchesVersion = true
break
}
}
for _, g := range apply.Kinds {
if g == gvk.Kind {
matchesKind = true
break
}
}
if matchesGroup &&
matchesVersion &&
matchesKind {
return true
}
}
return false
}

func isNamespace(obj runtime.Object) bool {
return obj.GetObjectKind().GroupVersionKind().Kind == "Namespace" &&
obj.GetObjectKind().GroupVersionKind().Group == ""
}
Loading

0 comments on commit 0af9133

Please sign in to comment.