Skip to content

Commit

Permalink
Allow excluding namespaces via labels open-policy-agent#1078
Browse files Browse the repository at this point in the history
  • Loading branch information
becky-hd committed Feb 19, 2021
1 parent 001f4c3 commit deaa8cd
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 19 deletions.
1 change: 1 addition & 0 deletions apis/config/v1alpha1/config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type SyncOnlyEntry struct {

type MatchEntry struct {
ExcludedNamespaces []string `json:"excludedNamespaces,omitempty"`
NamespaceSelectors []*metav1.LabelSelector `json:"namespaceSelectors,omitempty"`
Processes []string `json:"processes,omitempty"`
}

Expand Down
12 changes: 12 additions & 0 deletions apis/config/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 56 additions & 15 deletions pkg/controller/config/process/excluder.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package process

import (
"github.com/open-policy-agent/gatekeeper/pkg/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"reflect"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sync"

configv1alpha1 "github.com/open-policy-agent/gatekeeper/apis/config/v1alpha1"
Expand All @@ -20,9 +25,12 @@ const (
Star = Process("*")
)

var log = logf.Log.WithName("controller").WithValues("kind", "Config")

type Excluder struct {
mux sync.RWMutex
excludedNamespaces map[Process]map[string]bool
namespaceSelectors map[Process][]labels.Selector
}

var allProcesses = []Process{
Expand All @@ -33,6 +41,7 @@ var allProcesses = []Process{

var processExcluder = &Excluder{
excludedNamespaces: make(map[Process]map[string]bool),
namespaceSelectors: make(map[Process][]labels.Selector),
}

func Get() *Excluder {
Expand All @@ -42,6 +51,7 @@ func Get() *Excluder {
func New() *Excluder {
return &Excluder{
excludedNamespaces: make(map[Process]map[string]bool),
namespaceSelectors: make(map[Process][]labels.Selector),
}
}

Expand All @@ -50,21 +60,28 @@ func (s *Excluder) Add(entry []configv1alpha1.MatchEntry) {
defer s.mux.Unlock()

for _, matchEntry := range entry {
for _, ns := range matchEntry.ExcludedNamespaces {
for _, op := range matchEntry.Processes {
var processes []Process
for _, op := range matchEntry.Processes {
if Process(op) == Star {
processes = allProcesses
break
}
processes = append(processes, Process(op))
}
for _, p := range processes {
for _, ns := range matchEntry.ExcludedNamespaces {
// adding excluded namespace to all processes for "*"
if Process(op) == Star {
for _, o := range allProcesses {
if s.excludedNamespaces[o] == nil {
s.excludedNamespaces[o] = make(map[string]bool)
}
s.excludedNamespaces[o][ns] = true
}
if s.excludedNamespaces[p] == nil {
s.excludedNamespaces[p] = make(map[string]bool)
}
s.excludedNamespaces[p][ns] = true
}
for _, ns := range matchEntry.NamespaceSelectors {
selector, e := metav1.LabelSelectorAsSelector(ns)
if e == nil {
s.namespaceSelectors[p] = append(s.namespaceSelectors[p], selector)
} else {
if s.excludedNamespaces[Process(op)] == nil {
s.excludedNamespaces[Process(op)] = make(map[string]bool)
}
s.excludedNamespaces[Process(op)][ns] = true
log.Error(e, "illegal namespaceSelectors format")
}
}
}
Expand All @@ -75,12 +92,13 @@ func (s *Excluder) Replace(new *Excluder) {
s.mux.Lock()
defer s.mux.Unlock()
s.excludedNamespaces = new.excludedNamespaces
s.namespaceSelectors = new.namespaceSelectors
}

func (s *Excluder) Equals(new *Excluder) bool {
s.mux.RLock()
defer s.mux.RUnlock()
return reflect.DeepEqual(s.excludedNamespaces, new.excludedNamespaces)
return reflect.DeepEqual(s.excludedNamespaces, new.excludedNamespaces) && reflect.DeepEqual(s.namespaceSelectors, new.namespaceSelectors)
}

func (s *Excluder) IsNamespaceExcluded(process Process, obj runtime.Object) (bool, error) {
Expand All @@ -92,9 +110,32 @@ func (s *Excluder) IsNamespaceExcluded(process Process, obj runtime.Object) (boo
return false, errors.Wrapf(err, "Failed to get accessor for %s - %s", obj.GetObjectKind().GroupVersionKind().Group, obj.GetObjectKind().GroupVersionKind().Kind)
}

if obj.GetObjectKind().GroupVersionKind().Kind == "Namespace" && obj.GetObjectKind().GroupVersionKind().Group == "" {
if util.IsNamespace(obj) {
return s.excludedNamespaces[process][meta.GetName()], nil
}

return s.excludedNamespaces[process][meta.GetNamespace()], nil
}

func (s *Excluder) IsNamespaceSelectorExcluded(process Process, obj runtime.Object, ns *corev1.Namespace) (bool, error) {
s.mux.RLock()
defer s.mux.RUnlock()

meta, err := meta.Accessor(obj)
if err != nil {
return false, errors.Wrapf(err, "Failed to get accessor for %s - %s", obj.GetObjectKind().GroupVersionKind().Group, obj.GetObjectKind().GroupVersionKind().Kind)
}
for _, selector := range s.namespaceSelectors[process] {
switch {
case util.IsNamespace(obj): // if the object is a namespace, namespace selector matches against the object
if selector.Matches(labels.Set(meta.GetLabels())) {
return true, nil
}
case meta.GetNamespace() == "":
// cluster scoped
case selector.Matches(labels.Set(ns.Labels)):
return true, nil
}
}
return false, nil
}
23 changes: 19 additions & 4 deletions pkg/controller/sync/sync_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package sync

import (
"context"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -187,7 +189,8 @@ func (r *ReconcileSync) Reconcile(ctx context.Context, request reconcile.Request
}

// namespace is excluded from sync
isExcludedNamespace, err := r.skipExcludedNamespace(instance)
isExcludedNamespace, err := r.skipExcludedNamespace(ctx, instance)
// TODO return reconcile.Result{}
if err != nil {
log.Error(err, "error while excluding namespaces")
}
Expand Down Expand Up @@ -245,13 +248,25 @@ func (r *ReconcileSync) Reconcile(ctx context.Context, request reconcile.Request
return reconcile.Result{}, nil
}

func (r *ReconcileSync) skipExcludedNamespace(obj *unstructured.Unstructured) (bool, error) {
func (r *ReconcileSync) skipExcludedNamespace(ctx context.Context, obj *unstructured.Unstructured) (bool, error) {
isNamespaceExcluded, err := r.processExcluder.IsNamespaceExcluded(process.Sync, obj)
if err != nil {
return false, err
}

return isNamespaceExcluded, err
if isNamespaceExcluded {
return true, nil
}
ns := &corev1.Namespace{}
if !util.IsNamespace(obj) {
if err = r.reader.Get(ctx, types.NamespacedName{Name: obj.GetNamespace()}, ns); err!= nil {
return false, err
}
}
isNamespaceExcluded, err = r.processExcluder.IsNamespaceSelectorExcluded(process.Sync, obj, ns)
if err != nil {
return false, err
}
return isNamespaceExcluded, nil
}

func NewMetricsCache() *MetricsCache {
Expand Down
8 changes: 8 additions & 0 deletions pkg/util/obj_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package util

import "k8s.io/apimachinery/pkg/runtime"

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

0 comments on commit deaa8cd

Please sign in to comment.