Skip to content

Commit

Permalink
Support Naming Policy
Browse files Browse the repository at this point in the history
  • Loading branch information
bells17 committed Sep 21, 2021
1 parent cd1c38f commit f116f46
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 8 deletions.
50 changes: 43 additions & 7 deletions hooks/subnamespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package hooks
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"regexp"

accuratev1 "github.com/cybozu-go/accurate/api/v1"
"github.com/cybozu-go/accurate/pkg/config"
"github.com/cybozu-go/accurate/pkg/constants"
admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
Expand Down Expand Up @@ -47,7 +51,8 @@ func (m *subNamespaceMutator) Handle(ctx context.Context, req admission.Request)

type subNamespaceValidator struct {
client.Client
dec *admission.Decoder
dec *admission.Decoder
namingPolicies []config.NamingPolicy
}

var _ admission.Handler = &subNamespaceValidator{}
Expand All @@ -67,15 +72,45 @@ func (v *subNamespaceValidator) Handle(ctx context.Context, req admission.Reques
return admission.Errored(http.StatusInternalServerError, err)
}

if ns.Labels[constants.LabelType] == constants.NSTypeRoot || ns.Labels[constants.LabelParent] != "" {
return admission.Allowed("")
if ns.Labels[constants.LabelType] != constants.NSTypeRoot && ns.Labels[constants.LabelParent] == "" {
return admission.Denied(fmt.Sprintf("namespace %s is neither a root nor a sub namespace", ns.Name))
}

if err := v.MatchNamingPolicy(ctx, sn); err != nil {
return admission.Denied(fmt.Sprintf("namespace %s is not match naming policies", ns.Name))
}

return admission.Allowed("")
}

func (v *subNamespaceValidator) MatchNamingPolicy(ctx context.Context, sn *accuratev1.SubNamespace) error {
logger := log.FromContext(ctx)
logger.Info("MatchNamingPolicy called\n")
for _, policy := range v.namingPolicies {
logger.Info("namingPolicies", "Root", policy.Root, "Match", policy.Match)
logger.Info("sn", "Namespace", sn.Namespace, "Name", sn.Name)
matched, err := regexp.MatchString(policy.Root, sn.Namespace)
if err != nil {
return err
}
if matched {
logger.Info("matched root namespace", "Namespace", sn.Namespace, "Name", sn.Name, "Root", policy.Root)
matched, err := regexp.MatchString(policy.Match, sn.Name)
if err != nil {
return err
}
if !matched {
logger.Info("matched sn name", "Namespace", sn.Namespace, "Name", sn.Name, "Match", policy.Match)
return errors.New("naming policy is not matched")
}
}
}

return admission.Denied(fmt.Sprintf("namespace %s is neither a root nor a sub namespace", ns.Name))
return nil
}

// SetupSubNamespaceWebhook registers the webhooks for SubNamespace
func SetupSubNamespaceWebhook(mgr manager.Manager, dec *admission.Decoder) {
func SetupSubNamespaceWebhook(mgr manager.Manager, dec *admission.Decoder, namingPolicies []config.NamingPolicy) {
serv := mgr.GetWebhookServer()

m := &subNamespaceMutator{
Expand All @@ -84,8 +119,9 @@ func SetupSubNamespaceWebhook(mgr manager.Manager, dec *admission.Decoder) {
serv.Register("/mutate-accurate-cybozu-com-v1-subnamespace", &webhook.Admission{Handler: m})

v := &subNamespaceValidator{
Client: mgr.GetClient(),
dec: dec,
Client: mgr.GetClient(),
dec: dec,
namingPolicies: namingPolicies,
}
serv.Register("/validate-accurate-cybozu-com-v1-subnamespace", &webhook.Admission{Handler: v})
}
64 changes: 64 additions & 0 deletions hooks/subnamespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,68 @@ var _ = Describe("SubNamespace webhook", func() {

Expect(controllerutil.ContainsFinalizer(sn, constants.Finalizer)).To(BeTrue())
})

Context("Naming Policy", func() {
When("the root namespace name is matched some Root Naming Policies", func() {
When("the SubNamespace name is matched to the Root's Match Naming Policy", func() {
It("should allow creation of SubNamespace in a root namespace - pattern1", func() {
ns := &corev1.Namespace{}
ns.Name = "naming-policy-root-1"
ns.Labels = map[string]string{constants.LabelType: constants.NSTypeRoot}
err := k8sClient.Create(ctx, ns)
Expect(err).NotTo(HaveOccurred())

sn := &accuratev1.SubNamespace{}
sn.Namespace = "naming-policy-root-1"
sn.Name = "naming-policy-root-1-child"
err = k8sClient.Create(ctx, sn)
Expect(err).NotTo(HaveOccurred())
})

It("should allow creation of SubNamespace in a root namespace - pattern2", func() {
ns := &corev1.Namespace{}
ns.Name = "root-ns-match-1"
ns.Labels = map[string]string{constants.LabelType: constants.NSTypeRoot}
err := k8sClient.Create(ctx, ns)
Expect(err).NotTo(HaveOccurred())

sn := &accuratev1.SubNamespace{}
sn.Namespace = "root-ns-match-1"
sn.Name = "child-match-1"
err = k8sClient.Create(ctx, sn)
Expect(err).NotTo(HaveOccurred())
})
})

When("the SubNamespace name is not matched to the Root's Match Naming Policy", func() {
It("should deny creation of SubNamespace in a root namespace - pattern1", func() {
ns := &corev1.Namespace{}
ns.Name = "naming-policy-root-2"
ns.Labels = map[string]string{constants.LabelType: constants.NSTypeRoot}
err := k8sClient.Create(ctx, ns)
Expect(err).NotTo(HaveOccurred())

sn := &accuratev1.SubNamespace{}
sn.Namespace = "naming-policy-root-2"
sn.Name = "naming-policy-root-2--child"
err = k8sClient.Create(ctx, sn)
Expect(err).To(HaveOccurred())
})

It("should deny creation of SubNamespace in a root namespace - pattern2", func() {
ns := &corev1.Namespace{}
ns.Name = "root-ns-match-2"
ns.Labels = map[string]string{constants.LabelType: constants.NSTypeRoot}
err := k8sClient.Create(ctx, ns)
Expect(err).NotTo(HaveOccurred())

sn := &accuratev1.SubNamespace{}
sn.Namespace = "root-ns-match-2"
sn.Name = "child-2"
err = k8sClient.Create(ctx, sn)
Expect(err).To(HaveOccurred())
})
})
})
})
})
16 changes: 15 additions & 1 deletion hooks/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
admissionv1beta1 "k8s.io/api/admission/v1beta1"
//+kubebuilder:scaffold:imports
accuratev1 "github.com/cybozu-go/accurate/api/v1"
"github.com/cybozu-go/accurate/pkg/config"
"github.com/cybozu-go/accurate/pkg/indexing"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
Expand Down Expand Up @@ -91,7 +92,20 @@ var _ = BeforeSuite(func() {
dec, err := admission.NewDecoder(scheme)
Expect(err).NotTo(HaveOccurred())
SetupNamespaceWebhook(mgr, dec)
SetupSubNamespaceWebhook(mgr, dec)
SetupSubNamespaceWebhook(mgr, dec, []config.NamingPolicy{
{
Root: "naming-policy-root-1",
Match: "naming-policy-root-1-child",
},
{
Root: "naming-policy-root-2",
Match: "naming-policy-root-2-child",
},
{
Root: ".+-match-.+",
Match: ".+-match-.+",
},
})

go func() {
err = mgr.Start(ctx)
Expand Down
6 changes: 6 additions & 0 deletions pkg/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ import (
"sigs.k8s.io/yaml"
)

type NamingPolicy struct {
Root string `json:"root,omitempty"`
Match string `json:"match,omitempty"`
}

// Config represents the configuration file of Accurate.
type Config struct {
LabelKeys []string `json:"labelKeys,omitempty"`
AnnotationKeys []string `json:"annotationKeys,omitempty"`
Watches []metav1.GroupVersionKind `json:"watches,omitempty"`
NamingPolicies []NamingPolicy `json:"namingPolicies,omitempty"`
}

// Validate validates the configurations.
Expand Down

0 comments on commit f116f46

Please sign in to comment.