Skip to content

Commit

Permalink
Merge pull request #89 from santinoncs/included-namespaces
Browse files Browse the repository at this point in the history
Add --include-namespaces-regex features
  • Loading branch information
k8s-ci-robot authored Oct 10, 2021
2 parents de12632 + 77c3d14 commit e297652
Show file tree
Hide file tree
Showing 20 changed files with 106 additions and 33 deletions.
8 changes: 4 additions & 4 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var (
restartOnSecretRefresh bool
unpropagatedAnnotations arrayArg
excludedNamespaces arrayArg
includedNamespacesRegex string
)

func init() {
Expand Down Expand Up @@ -98,14 +99,13 @@ func main() {
flag.IntVar(&webhookServerPort, "webhook-server-port", 443, "The port that the webhook server serves at.")
flag.Var(&unpropagatedAnnotations, "unpropagated-annotation", "An annotation that, if present, will be stripped out of any propagated copies of an object. May be specified multiple times, with each instance specifying one annotation. See the user guide for more information.")
flag.Var(&excludedNamespaces, "excluded-namespace", "A namespace that, if present, will be excluded from HNC management. May be specified multiple times, with each instance specifying one namespace. See the user guide for more information.")
flag.StringVar(&includedNamespacesRegex, "included-namespace-regex", ".*", "Namespace regular expression. Namespaces that match this regexp will be included and handle by HNC. As it is a regex, this parameter cannot be specified multiple times. Implicit wrapping of the expression \"^...$\" is done here")
flag.BoolVar(&restartOnSecretRefresh, "cert-restart-on-secret-refresh", false, "Kills the process when secrets are refreshed so that the pod can be restarted (secrets take up to 60s to be updated by running pods)")
flag.Parse()
// Assign the array args to the configuration variables after the args are parsed.
config.UnpropagatedAnnotations = unpropagatedAnnotations
config.ExcludedNamespaces = make(map[string]bool)
for _, exn := range excludedNamespaces {
config.ExcludedNamespaces[exn] = true
}

config.SetNamespaces(includedNamespacesRegex, excludedNamespaces...)

// Enable OpenCensus exporters to export metrics
// to Stackdriver Monitoring.
Expand Down
4 changes: 3 additions & 1 deletion hack/tools.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//+build tools
//go:build tools
// +build tools

//
// This is used to ensure that controller-gen is included in the /vendor directory. See
// https://stackoverflow.com/questions/52428230/how-do-go-modules-work-with-installable-commands.
Expand Down
5 changes: 4 additions & 1 deletion internal/config/default_config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package config

import "regexp"

// UnpropgatedAnnotations is a list of annotations on objects that should _not_ be propagated by HNC.
// Much like HNC itself, other systems (such as GKE Config Sync) use annotations to "claim" an
// object - such as deleting objects it doesn't recognize. By removing these annotations on
Expand All @@ -14,4 +16,5 @@ var UnpropagatedAnnotations []string
//
// This value is controlled by the --excluded-namespace command line, which may
// be set multiple times.
var ExcludedNamespaces map[string]bool
var excludedNamespaces map[string]bool
var includedNamespacesRegex *regexp.Regexp
30 changes: 30 additions & 0 deletions internal/config/namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package config

import (
"regexp"
)

func SetNamespaces(regex string, excluded ...string) {

if regex == "" {
regex = ".*"
}

includedNamespacesRegex = regexp.MustCompile("^" + regex + "$")

excludedNamespaces = make(map[string]bool)
for _, exn := range excluded {
excludedNamespaces[exn] = true
}

}

func IsNamespaceIncluded(name string) bool {

if excludedNamespaces[name] {
return false
}

return includedNamespacesRegex.MatchString(name)

}
36 changes: 36 additions & 0 deletions internal/config/namespace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package config

import (
. "github.com/onsi/gomega"
"testing"
)

func TestIsNamespaceIncluded(t *testing.T) {

tests := []struct {
name string
regex string
excludeNamespaces []string
expect bool
}{
{name: "foobar", regex: "foo.*", excludeNamespaces: []string{"bar"}, expect: true},
{name: "bar", regex: "foo-.*", excludeNamespaces: []string{"bar"}, expect: false},
{name: "bar", regex: ".*", excludeNamespaces: []string{"bar"}, expect: false},
{name: "foo", regex: ".*", excludeNamespaces: []string{"bar"}, expect: true},
{name: "foo", regex: ".*", excludeNamespaces: []string{"bar", "foo"}, expect: false},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)

// Test
SetNamespaces(tc.regex, tc.excludeNamespaces...)
isIncluded := IsNamespaceIncluded(tc.name)

// Report
g.Expect(isIncluded).Should(Equal(tc.expect))
})
}

}
4 changes: 2 additions & 2 deletions internal/mutators/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func (m *Namespace) Handle(ctx context.Context, req admission.Request) admission
// Currently, we only add `included-namespace` label to non-excluded namespaces
// if the label is missing.
func (m *Namespace) handle(log logr.Logger, ns *corev1.Namespace) {
// Early exit if the namespace is excluded.
if config.ExcludedNamespaces[ns.Name] {

if !config.IsNamespaceIncluded(ns.Name) {
return
}

Expand Down
2 changes: 1 addition & 1 deletion internal/mutators/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
func TestMutateNamespaceIncludedLabel(t *testing.T) {
m := &Namespace{}
l := zap.New()
config.ExcludedNamespaces = map[string]bool{"excluded": true}
config.SetNamespaces("", "excluded")

tests := []struct {
name string
Expand Down
4 changes: 2 additions & 2 deletions internal/reconcilers/anchor.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (r *AnchorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr

// Always delete anchor (and any other HNC CRs) in the excluded namespaces and
// early exit.
if config.ExcludedNamespaces[pnm] {
if !config.IsNamespaceIncluded(pnm) {
// Since the anchors in the excluded namespaces are never synced by HNC,
// there are no finalizers on the anchors that we can delete them without
// removing the finalizers first.
Expand All @@ -87,7 +87,7 @@ func (r *AnchorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
// namespace that should not be created as a subnamespace, but the webhook has
// been bypassed and the anchor has been successfully created. Forbidden
// anchors won't have finalizers.
if config.ExcludedNamespaces[nm] {
if !config.IsNamespaceIncluded(nm) {
inst.Status.State = api.Forbidden
return ctrl.Result{}, r.writeInstance(ctx, log, inst)
}
Expand Down
6 changes: 3 additions & 3 deletions internal/reconcilers/anchor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var _ = Describe("Anchor", func() {
BeforeEach(func() {
fooName = createNS(ctx, "foo")
barName = createNSName("bar")
config.ExcludedNamespaces = nil
config.SetNamespaces("")
})

It("should create an subnamespace and update the hierarchy according to the anchor", func() {
Expand Down Expand Up @@ -52,14 +52,14 @@ var _ = Describe("Anchor", func() {
})

It("should remove the anchor in an excluded namespace", func() {
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")
kube_system_anchor_bar := newAnchor(barName, "kube-system")
updateAnchor(ctx, kube_system_anchor_bar)
Eventually(canGetAnchor(ctx, barName, "kube-system")).Should(Equal(false))
})

It("should set the anchor.status.state to Forbidden if the subnamespace is an excluded namespace", func() {
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")
foo_anchor_kube_system := newAnchor("kube-system", fooName)
updateAnchor(ctx, foo_anchor_kube_system)
Eventually(getAnchorState(ctx, fooName, "kube-system")).Should(Equal(api.Forbidden))
Expand Down
6 changes: 3 additions & 3 deletions internal/reconcilers/hierarchy_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ func (r *HierarchyConfigReconciler) Reconcile(ctx context.Context, req ctrl.Requ
ns := req.NamespacedName.Namespace
log := loggerWithRID(r.Log).WithValues("ns", ns)

// Early exit if it's an excluded namespace.
if config.ExcludedNamespaces[ns] {
// Early exit if it's an excluded namespace
if !config.IsNamespaceIncluded(ns) {
return ctrl.Result{}, r.handleExcludedNamespace(ctx, log, ns)
}

Expand Down Expand Up @@ -418,7 +418,7 @@ func (r *HierarchyConfigReconciler) syncParent(log logr.Logger, inst *api.Hierar

// Sync this namespace with its current parent.
curParent := r.Forest.Get(inst.Spec.Parent)
if config.ExcludedNamespaces[inst.Spec.Parent] {
if !config.IsNamespaceIncluded(inst.Spec.Parent) {
log.Info("Setting ConditionActivitiesHalted: excluded namespace set as parent", "parent", inst.Spec.Parent)
ns.SetCondition(api.ConditionActivitiesHalted, api.ReasonIllegalParent, fmt.Sprintf("Parent %q is an excluded namespace", inst.Spec.Parent))
} else if curParent != nil && !curParent.Exists() {
Expand Down
8 changes: 4 additions & 4 deletions internal/reconcilers/hierarchy_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var _ = Describe("Hierarchy", func() {
BeforeEach(func() {
fooName = createNS(ctx, "foo")
barName = createNS(ctx, "bar")
config.ExcludedNamespaces = nil
config.SetNamespaces("")
})

It("should set a child on the parent", func() {
Expand All @@ -34,7 +34,7 @@ var _ = Describe("Hierarchy", func() {

It("should remove the hierarchyconfiguration singleton in an excluded namespacee", func() {
// Set the excluded-namespace "kube-system"'s parent to "bar".
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")
exHier := newHierarchy("kube-system")
exHier.Spec.Parent = barName
updateHierarchy(ctx, exHier)
Expand All @@ -45,7 +45,7 @@ var _ = Describe("Hierarchy", func() {

It("should set IllegalParent condition if the parent is an excluded namespace", func() {
// Set bar's parent to the excluded-namespace "kube-system".
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")
barHier := newHierarchy(barName)
barHier.Spec.Parent = "kube-system"
updateHierarchy(ctx, barHier)
Expand Down Expand Up @@ -382,7 +382,7 @@ var _ = Describe("Hierarchy", func() {
})

It("should remove included-namespace namespace labels from excluded namespaces", func() {
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")
kubeSystem := getNamespace(ctx, "kube-system")

// Add additional label "other:other" to verify the labels are updated.
Expand Down
2 changes: 1 addition & 1 deletion internal/reconcilers/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func (r *ObjectReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
resp := ctrl.Result{}
log := loggerWithRID(r.Log).WithValues("trigger", req.NamespacedName)

if config.ExcludedNamespaces[req.Namespace] {
if !config.IsNamespaceIncluded(req.Namespace) {
return resp, nil
}

Expand Down
4 changes: 2 additions & 2 deletions internal/validators/anchor.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ func (v *Anchor) handle(req *anchorRequest) admission.Response {
switch req.op {
case k8sadm.Create:
// Can't create subnamespaces in excluded namespaces
if config.ExcludedNamespaces[pnm] {
if !config.IsNamespaceIncluded(pnm) {
msg := fmt.Sprintf("Cannot create a subnamespace in the excluded namespace %q", pnm)
return deny(metav1.StatusReasonForbidden, msg)
}
// Can't create subnamespaces using excluded namespace names
if config.ExcludedNamespaces[cnm] {
if !config.IsNamespaceIncluded(cnm) {
msg := fmt.Sprintf("Cannot create a subnamespace using the excluded namespace name %q", cnm)
return deny(metav1.StatusReasonForbidden, msg)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/validators/anchor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestCreateSubnamespaces(t *testing.T) {
// namespace "c".
f := foresttest.Create("-Aa")
h := &Anchor{Forest: f}
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")

tests := []struct {
name string
Expand Down
4 changes: 2 additions & 2 deletions internal/validators/hierarchy.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ func (v *Hierarchy) handle(ctx context.Context, log logr.Logger, req *request) a
return allow("HNC SA")
}

if config.ExcludedNamespaces[req.hc.Namespace] {
if !config.IsNamespaceIncluded(req.hc.Namespace) {
reason := fmt.Sprintf("Cannot set the excluded namespace %q as a child of another namespace", req.hc.Namespace)
return deny(metav1.StatusReasonForbidden, reason)
}
if config.ExcludedNamespaces[req.hc.Spec.Parent] {
if !config.IsNamespaceIncluded(req.hc.Spec.Parent) {
reason := fmt.Sprintf("Cannot set the parent to the excluded namespace %q", req.hc.Spec.Parent)
return deny(metav1.StatusReasonForbidden, reason)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/validators/hierarchy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestStructure(t *testing.T) {
h := &Hierarchy{Forest: f}
l := zap.New()
// For this unit test, we only set `kube-system` as an excluded namespace.
config.ExcludedNamespaces = map[string]bool{"kube-system": true}
config.SetNamespaces("", "kube-system")

tests := []struct {
name string
Expand Down
6 changes: 3 additions & 3 deletions internal/validators/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ func (v *Namespace) illegalIncludedNamespaceLabel(req *nsRequest) admission.Resp
return allow("")
}
}
isExcluded := config.ExcludedNamespaces[req.ns.Name]
isIncluded := config.IsNamespaceIncluded(req.ns.Name)

// An excluded namespaces should not have included-namespace label.
if isExcluded && hasLabel {
if !isIncluded && hasLabel {
msg := fmt.Sprintf("You cannot enforce webhook rules on this excluded namespace using the %q label. "+
"See https://github.com/kubernetes-sigs/hierarchical-namespaces/blob/master/docs/user-guide/concepts.md#included-namespace-label "+
"for detail.", api.LabelIncludedNamespace)
Expand All @@ -133,7 +133,7 @@ func (v *Namespace) illegalIncludedNamespaceLabel(req *nsRequest) admission.Resp
// right value.
// Note: since we have a mutating webhook to set the correct label if it's
// missing before this, we only need to check if the label value is correct.
if !isExcluded && labelValue != "true" {
if isIncluded && labelValue != "true" {
msg := fmt.Sprintf("You cannot change the value of the %q label. It has to be set as true on a non-excluded namespace. "+
"See https://github.com/kubernetes-sigs/hierarchical-namespaces/blob/master/docs/user-guide/concepts.md#included-namespace-label "+
"for detail.", api.LabelIncludedNamespace)
Expand Down
2 changes: 1 addition & 1 deletion internal/validators/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func setSubAnnotation(ns *corev1.Namespace, pnm string) {
func TestIllegalIncludedNamespaceNamespace(t *testing.T) {
f := foresttest.Create("-a-c") // a <- b; c <- d
vns := &Namespace{Forest: f}
config.ExcludedNamespaces["excluded"] = true
config.SetNamespaces("", "excluded")

tests := []struct {
name string
Expand Down
2 changes: 1 addition & 1 deletion internal/validators/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (o *Object) Handle(ctx context.Context, req admission.Request) admission.Re
// Note: This is added just in case the "hnc.x-k8s.io/excluded-namespace=true"
// label is not added on the excluded namespaces. VWHConfiguration of this VWH
// already has a `namespaceSelector` to exclude namespaces with the label.
if config.ExcludedNamespaces[req.Namespace] {
if !config.IsNamespaceIncluded(req.Namespace) {
return allow("excluded namespace " + req.Namespace)
}
// Allow changes to the types that are not in propagate mode. This is to dynamically enable/disable
Expand Down
2 changes: 2 additions & 0 deletions internal/validators/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package validators

import (
"context"
"sigs.k8s.io/hierarchical-namespaces/internal/config"
"testing"
"time"

Expand Down Expand Up @@ -37,6 +38,7 @@ func TestType(t *testing.T) {
f.AddTypeSyncer(or)
l := zap.New()
o := &Object{Forest: f, Log: l}
config.SetNamespaces("", "kube-system")

tests := []struct {
name string
Expand Down

0 comments on commit e297652

Please sign in to comment.