From 38cd3be71a81afdec95e2bac53836800e9c31836 Mon Sep 17 00:00:00 2001 From: Dario Tranchitella Date: Fri, 7 Aug 2020 10:25:05 +0200 Subject: [PATCH] Programmatic Tenant prefix for the Capsule namespaces (#41) --- config/webhook/manifests.yaml | 17 ++++++ main.go | 10 +++- pkg/webhook/tenant_prefix/patching.go | 84 +++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 pkg/webhook/tenant_prefix/patching.go diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 286e88bb..792018af 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -120,3 +120,20 @@ webhooks: - CREATE resources: - persistentvolumeclaims +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /validating-v1-namespace-tenant-prefix + failurePolicy: Fail + name: prefix.namespace.capsule.clastix.io + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - namespaces diff --git a/main.go b/main.go index edc95ac2..7c2b74a0 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ import ( "github.com/clastix/capsule/pkg/webhook/network_policies" "github.com/clastix/capsule/pkg/webhook/owner_reference" "github.com/clastix/capsule/pkg/webhook/pvc" + "github.com/clastix/capsule/pkg/webhook/tenant_prefix" "github.com/clastix/capsule/version" // +kubebuilder:scaffold:imports ) @@ -66,12 +67,17 @@ func printVersion() { func main() { var metricsAddr string var enableLeaderElection bool + var forceTenantPrefix bool var v bool + flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") flag.BoolVar(&v, "version", false, "Print the Capsule version and exit") + flag.BoolVar(&forceTenantPrefix, "force-tenant-prefix", false, "Enforces the Tenant owner, "+ + "during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash. "+ + "This is useful to avoid Namespace name collision in a public CaaS environment.") flag.Parse() ctrl.SetLogger(zap.New(zap.UseDevMode(true))) @@ -104,7 +110,9 @@ func main() { // +kubebuilder:scaffold:builder //webhooks - err = webhook.Register(mgr, &ingress.ExtensionIngress{}, &ingress.NetworkIngress{}, pvc.Webhook{}, &owner_reference.Webhook{}, &namespace_quota.Webhook{}, network_policies.Webhook{}) + wl := make([]webhook.Webhook, 0) + wl = append(wl, &ingress.ExtensionIngress{}, &ingress.NetworkIngress{}, pvc.Webhook{}, &owner_reference.Webhook{}, &namespace_quota.Webhook{}, network_policies.Webhook{}, tenant_prefix.Webhook{ForceTenantPrefix: forceTenantPrefix}) + err = webhook.Register(mgr, wl...) if err != nil { setupLog.Error(err, "unable to setup webhooks") os.Exit(1) diff --git a/pkg/webhook/tenant_prefix/patching.go b/pkg/webhook/tenant_prefix/patching.go new file mode 100644 index 00000000..30b44fd0 --- /dev/null +++ b/pkg/webhook/tenant_prefix/patching.go @@ -0,0 +1,84 @@ +/* +Copyright 2020 Clastix Labs. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tenant_prefix + +import ( + "context" + "net/http" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/clastix/capsule/api/v1alpha1" + "github.com/clastix/capsule/pkg/webhook" +) + +// +kubebuilder:webhook:path=/validating-v1-namespace-tenant-prefix,mutating=false,failurePolicy=fail,groups="",resources=namespaces,verbs=create,versions=v1,name=prefix.namespace.capsule.clastix.io + +type Webhook struct { + ForceTenantPrefix bool +} + +func (o Webhook) GetHandler() webhook.Handler { + return &handler{forceTenantPrefix: o.ForceTenantPrefix} +} + +func (o Webhook) GetName() string { + return "OwnerReference" +} + +func (o Webhook) GetPath() string { + return "/validating-v1-namespace-tenant-prefix" +} + +type handler struct { + forceTenantPrefix bool +} + +func (r *handler) OnCreate(ctx context.Context, req admission.Request, clt client.Client, decoder *admission.Decoder) admission.Response { + if !r.forceTenantPrefix { + return admission.Allowed("") + } + ns := &corev1.Namespace{} + if err := decoder.Decode(req, ns); err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + t := &v1alpha1.Tenant{} + for _, or := range ns.ObjectMeta.OwnerReferences { + // retrieving the selected Tenant + if err := clt.Get(ctx, types.NamespacedName{Name: or.Name}, t); err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + } + + if e := t.GetName() + "-" + ns.GetName(); !strings.HasPrefix(ns.GetName(), t.GetName()+"-") { + return admission.Denied("The namespace doesn't match the tenant prefix, expected " + e) + } + return admission.Allowed("") +} + +func (r *handler) OnDelete(ctx context.Context, req admission.Request, client client.Client, decoder *admission.Decoder) admission.Response { + return admission.Allowed("") +} + +func (r *handler) OnUpdate(ctx context.Context, req admission.Request, client client.Client, decoder *admission.Decoder) admission.Response { + return admission.Allowed("") +}