feat!: using CapsuleConfiguration CRD with reload at runtime

This commit is contained in:
Dario Tranchitella
2021-05-30 22:37:11 +02:00
parent 994a4c282d
commit 3570b02427
11 changed files with 379 additions and 70 deletions

View File

@@ -0,0 +1,68 @@
/*
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// CapsuleConfigurationSpec defines the Capsule configuration
// nolint:maligned
type CapsuleConfigurationSpec struct {
// Names of the groups for Capsule users.
// +kubebuilder:default={capsule.clastix.io}
UserGroups []string `json:"userGroups,omitempty"`
// 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.
// +kubebuilder:default=false
ForceTenantPrefix bool `json:"forceTenantPrefix,omitempty"`
// Disallow creation of namespaces, whose name matches this regexp
ProtectedNamespaceRegexpString string `json:"protectedNamespaceRegex,omitempty"`
// When defining the exact match for allowed Ingress hostnames at Tenant level, a collision is not allowed.
// Toggling this, Capsule will not check if a hostname collision is in place, allowing the creation of
// two or more Tenant resources although sharing the same allowed hostname(s).
//
// The JSON path of the resource is: /spec/ingressHostnames/allowed
AllowTenantIngressHostnamesCollision bool `json:"allowTenantIngressHostnamesCollision,omitempty"`
// Allow the collision of Ingress resource hostnames across all the Tenants.
// +kubebuilder:default=true
AllowIngressHostnameCollision bool `json:"allowIngressHostnameCollision,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// CapsuleConfiguration is the Schema for the Capsule configuration API
type CapsuleConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CapsuleConfigurationSpec `json:"spec,omitempty"`
}
// +kubebuilder:object:root=true
// CapsuleConfigurationList contains a list of CapsuleConfiguration
type CapsuleConfigurationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CapsuleConfiguration `json:"items"`
}
func init() {
SchemeBuilder.Register(&CapsuleConfiguration{}, &CapsuleConfigurationList{})
}

View File

@@ -96,6 +96,84 @@ func (in *AllowedListSpec) DeepCopy() *AllowedListSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CapsuleConfiguration) DeepCopyInto(out *CapsuleConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfiguration.
func (in *CapsuleConfiguration) DeepCopy() *CapsuleConfiguration {
if in == nil {
return nil
}
out := new(CapsuleConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CapsuleConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CapsuleConfigurationList) DeepCopyInto(out *CapsuleConfigurationList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CapsuleConfiguration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationList.
func (in *CapsuleConfigurationList) DeepCopy() *CapsuleConfigurationList {
if in == nil {
return nil
}
out := new(CapsuleConfigurationList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CapsuleConfigurationList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CapsuleConfigurationSpec) DeepCopyInto(out *CapsuleConfigurationSpec) {
*out = *in
if in.UserGroups != nil {
in, out := &in.UserGroups, &out.UserGroups
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationSpec.
func (in *CapsuleConfigurationSpec) DeepCopy() *CapsuleConfigurationSpec {
if in == nil {
return nil
}
out := new(CapsuleConfigurationSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExternalServiceIPs) DeepCopyInto(out *ExternalServiceIPs) {
*out = *in

View File

@@ -0,0 +1,82 @@
/*
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 rbac
import (
"context"
"github.com/go-logr/logr"
"github.com/pkg/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/pkg/configuration"
)
type Manager struct {
Log logr.Logger
Client client.Client
}
// InjectClient injects the Client interface, required by the Runnable interface
func (r *Manager) InjectClient(c client.Client) error {
r.Client = c
return nil
}
func filterByName(objName, desired string) bool {
return objName == desired
}
func forOptionPerInstanceName(instanceName string) builder.ForOption {
return builder.WithPredicates(predicate.Funcs{
CreateFunc: func(event event.CreateEvent) bool {
return filterByName(event.Object.GetName(), instanceName)
},
DeleteFunc: func(deleteEvent event.DeleteEvent) bool {
return filterByName(deleteEvent.Object.GetName(), instanceName)
},
UpdateFunc: func(updateEvent event.UpdateEvent) bool {
return filterByName(updateEvent.ObjectNew.GetName(), instanceName)
},
GenericFunc: func(genericEvent event.GenericEvent) bool {
return filterByName(genericEvent.Object.GetName(), instanceName)
},
})
}
func (r *Manager) SetupWithManager(mgr ctrl.Manager, configurationName string) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.CapsuleConfiguration{}, forOptionPerInstanceName(configurationName)).
Complete(r)
}
func (r *Manager) Reconcile(ctx context.Context, request reconcile.Request) (res reconcile.Result, err error) {
cfg := configuration.NewCapsuleConfiguration(r.Client, request.Name)
// Validating the Capsule Configuration options
if _, err = cfg.ProtectedNamespaceRegexp(); err != nil {
panic(errors.Wrap(err, "Invalid configuration for protected Namespace regex"))
}
return
}

65
main.go
View File

@@ -20,7 +20,6 @@ import (
goflag "flag"
"fmt"
"os"
"regexp"
goRuntime "runtime"
flag "github.com/spf13/pflag"
@@ -35,9 +34,11 @@ import (
capsulev1alpha1 "github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/controllers"
config "github.com/clastix/capsule/controllers/config"
"github.com/clastix/capsule/controllers/rbac"
"github.com/clastix/capsule/controllers/secret"
"github.com/clastix/capsule/controllers/servicelabels"
"github.com/clastix/capsule/pkg/configuration"
"github.com/clastix/capsule/pkg/indexer"
"github.com/clastix/capsule/pkg/webhook"
"github.com/clastix/capsule/pkg/webhook/ingress"
@@ -77,35 +78,16 @@ func printVersion() {
func main() {
var metricsAddr string
var enableLeaderElection bool
var forceTenantPrefix bool
var version bool
var capsuleGroups []string
var protectedNamespaceRegexpString string
var protectedNamespaceRegexp *regexp.Regexp
var allowTenantIngressHostnamesCollision bool
var allowIngressHostnamesCollision bool
var namespace string
var namespace, configurationName string
var goFlagSet goflag.FlagSet
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
flag.StringSliceVar(&capsuleGroups, "capsule-user-group", []string{capsulev1alpha1.GroupVersion.Group}, "Names of the groups for capsule users")
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(&version, "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.StringVar(&protectedNamespaceRegexpString, "protected-namespace-regex", "", "Disallow creation of namespaces, whose name matches this regexp")
flag.BoolVar(
&allowTenantIngressHostnamesCollision,
"allow-tenant-ingress-hostnames-collision",
false,
"When defining the exact match for allowed Ingress hostnames at Tenant level, a collision is not allowed. "+
"Toggling this, Capsule will not check if a hostname collision is in place, allowing the creation of "+
"two or more Tenant resources although sharing the same allowed hostname(s).",
)
flag.BoolVar(&allowIngressHostnamesCollision, "allow-ingress-hostname-collision", true, "Allow the Ingress hostname collision at Ingress resource level across all the Tenants.")
flag.StringVar(&configurationName, "configuration-name", "default", "The CapsuleConfiguration resource name to use")
opts := zap.Options{
EncoderConfigOptions: append([]zap.EncoderConfigOption{}, func(config *zapcore.EncoderConfig) {
@@ -129,6 +111,11 @@ func main() {
os.Exit(1)
}
if len(configurationName) == 0 {
setupLog.Error(fmt.Errorf("missing CapsuleConfiguration resource name"), "unable to start manager")
os.Exit(1)
}
manager, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
@@ -141,13 +128,6 @@ func main() {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if len(protectedNamespaceRegexpString) > 0 {
protectedNamespaceRegexp, err = regexp.Compile(protectedNamespaceRegexpString)
if err != nil {
setupLog.Error(err, "unable to compile protected-namespace-regex", "protected-namespace-regex", protectedNamespaceRegexp)
os.Exit(1)
}
}
majorVer, minorVer, _, err := utils.GetK8sVersion()
if err != nil {
@@ -158,8 +138,6 @@ func main() {
_ = manager.AddReadyzCheck("ping", healthz.Ping)
_ = manager.AddHealthzCheck("ping", healthz.Ping)
setupLog.Info("starting with following options:", "metricsAddr", metricsAddr, "enableLeaderElection", enableLeaderElection, "forceTenantPrefix", forceTenantPrefix)
if err = (&controllers.TenantReconciler{
Client: manager.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Tenant"),
@@ -170,19 +148,21 @@ func main() {
}
// +kubebuilder:scaffold:builder
cfg := configuration.NewCapsuleConfiguration(manager.GetClient(), configurationName)
// webhooks: the order matters, don't change it and just append
webhooksList := append(
make([]webhook.Webhook, 0),
ingress.Webhook(ingress.Handler(allowIngressHostnamesCollision)),
ingress.Webhook(ingress.Handler(cfg)),
pvc.Webhook(pvc.Handler()),
registry.Webhook(registry.Handler()),
podpriority.Webhook(podpriority.Handler()),
services.Webhook(services.Handler()),
ownerreference.Webhook(utils.InCapsuleGroups(capsuleGroups, ownerreference.Handler(forceTenantPrefix))),
namespacequota.Webhook(utils.InCapsuleGroups(capsuleGroups, namespacequota.Handler())),
networkpolicies.Webhook(utils.InCapsuleGroups(capsuleGroups, networkpolicies.Handler())),
tenantprefix.Webhook(utils.InCapsuleGroups(capsuleGroups, tenantprefix.Handler(forceTenantPrefix, protectedNamespaceRegexp))),
tenant.Webhook(tenant.Handler(allowTenantIngressHostnamesCollision)),
ownerreference.Webhook(utils.InCapsuleGroups(cfg, ownerreference.Handler(cfg))),
namespacequota.Webhook(utils.InCapsuleGroups(cfg, namespacequota.Handler())),
networkpolicies.Webhook(utils.InCapsuleGroups(cfg, networkpolicies.Handler())),
tenantprefix.Webhook(utils.InCapsuleGroups(cfg, tenantprefix.Handler(cfg))),
tenant.Webhook(tenant.Handler(cfg)),
)
if err = webhook.Register(manager, webhooksList...); err != nil {
setupLog.Error(err, "unable to setup webhooks")
@@ -191,13 +171,13 @@ func main() {
rbacManager := &rbac.Manager{
Log: ctrl.Log.WithName("controllers").WithName("Rbac"),
CapsuleGroups: capsuleGroups,
Configuration: cfg,
}
if err = manager.Add(rbacManager); err != nil {
setupLog.Error(err, "unable to create cluster roles")
os.Exit(1)
}
if err = rbacManager.SetupWithManager(manager); err != nil {
if err = rbacManager.SetupWithManager(manager, configurationName); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Rbac")
os.Exit(1)
}
@@ -241,6 +221,13 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "EndpointSliceLabels")
}
if err = (&config.Manager{
Log: ctrl.Log.WithName("controllers").WithName("CapsuleConfiguration"),
}).SetupWithManager(manager, configurationName); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "CapsuleConfiguration")
os.Exit(1)
}
if err = indexer.AddToManager(manager); err != nil {
setupLog.Error(err, "unable to setup indexers")
os.Exit(1)

View File

@@ -0,0 +1,87 @@
/*
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 configuration
import (
"context"
"regexp"
"github.com/pkg/errors"
machineryerr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/clastix/capsule/api/v1alpha1"
)
// capsuleConfiguration is the Capsule Configuration retrieval mode
// using a closure that provides the desired configuration.
type capsuleConfiguration struct {
retrievalFn func() *v1alpha1.CapsuleConfiguration
}
func NewCapsuleConfiguration(client client.Client, name string) Configuration {
return &capsuleConfiguration{retrievalFn: func() *v1alpha1.CapsuleConfiguration {
config := &v1alpha1.CapsuleConfiguration{}
if err := client.Get(context.Background(), types.NamespacedName{Name: name}, config); err != nil {
if machineryerr.IsNotFound(err) {
return &v1alpha1.CapsuleConfiguration{
Spec: v1alpha1.CapsuleConfigurationSpec{
UserGroups: []string{"capsule.clastix.io"},
ForceTenantPrefix: false,
ProtectedNamespaceRegexpString: "",
AllowTenantIngressHostnamesCollision: false,
AllowIngressHostnameCollision: true,
},
}
}
panic(errors.Wrap(err, "Cannot retrieve Capsule configuration with name "+name))
}
return config
}}
}
func (c capsuleConfiguration) AllowIngressHostnameCollision() bool {
return c.retrievalFn().Spec.AllowIngressHostnameCollision
}
func (c capsuleConfiguration) AllowTenantIngressHostnamesCollision() bool {
return c.retrievalFn().Spec.AllowTenantIngressHostnamesCollision
}
func (c capsuleConfiguration) ProtectedNamespaceRegexp() (*regexp.Regexp, error) {
expr := c.retrievalFn().Spec.ProtectedNamespaceRegexpString
if len(expr) == 0 {
return nil, nil
}
r, err := regexp.Compile(expr)
if err != nil {
return nil, errors.Wrap(err, "Cannot compile the protected namespace regexp")
}
return r, nil
}
func (c capsuleConfiguration) ForceTenantPrefix() bool {
return c.retrievalFn().Spec.ForceTenantPrefix
}
func (c capsuleConfiguration) UserGroups() []string {
return c.retrievalFn().Spec.UserGroups
}

View File

@@ -14,11 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package rbac
package configuration
type ImmutableClusterRoleBindingError struct {
}
import (
"regexp"
)
func (i ImmutableClusterRoleBindingError) Error() string {
return "The ClusterRoleBinding Role reference is immutable, deletion must be processed first"
type Configuration interface {
AllowIngressHostnameCollision() bool
AllowTenantIngressHostnamesCollision() bool
ProtectedNamespaceRegexp() (*regexp.Regexp, error)
ForceTenantPrefix() bool
UserGroups() []string
}

View File

@@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/pkg/configuration"
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
)
@@ -58,11 +59,11 @@ func (w *webhook) GetPath() string {
}
type handler struct {
allowHostnamesCollision bool
configuration configuration.Configuration
}
func Handler(allowIngressHostnamesCollision bool) capsulewebhook.Handler {
return &handler{allowHostnamesCollision: allowIngressHostnamesCollision}
func Handler(configuration configuration.Configuration) capsulewebhook.Handler {
return &handler{configuration: configuration}
}
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
@@ -214,7 +215,7 @@ func (r *handler) validateIngress(ctx context.Context, c client.Client, ingress
}
func (r *handler) validateCollision(ctx context.Context, clt client.Client, ingress Ingress) error {
if r.allowHostnamesCollision {
if r.configuration.AllowIngressHostnameCollision() {
return nil
}
for _, hostname := range ingress.Hostnames() {

View File

@@ -33,6 +33,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
capsulev1alpha1 "github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/pkg/configuration"
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
)
@@ -59,12 +60,12 @@ func (w *webhook) GetPath() string {
}
type handler struct {
forceTenantPrefix bool
cfg configuration.Configuration
}
func Handler(forceTenantPrefix bool) capsulewebhook.Handler {
func Handler(cfg configuration.Configuration) capsulewebhook.Handler {
return &handler{
forceTenantPrefix: forceTenantPrefix,
cfg: cfg,
}
}
@@ -129,7 +130,7 @@ func (h *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
return h.patchResponseForOwnerRef(&tenants[0], ns)
}
if h.forceTenantPrefix {
if h.cfg.ForceTenantPrefix() {
for _, tnt := range tenants {
if strings.HasPrefix(ns.GetName(), fmt.Sprintf("%s-", tnt.GetName())) {
return h.patchResponseForOwnerRef(tnt.DeepCopy(), ns)

View File

@@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/pkg/configuration"
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
)
@@ -52,11 +53,11 @@ func (w webhook) GetHandler() capsulewebhook.Handler {
}
type handler struct {
checkIngressHostnamesExact bool
configuration configuration.Configuration
}
func Handler(allowTenantIngressHostnamesCollision bool) capsulewebhook.Handler {
return &handler{checkIngressHostnamesExact: !allowTenantIngressHostnamesCollision}
func Handler(configuration configuration.Configuration) capsulewebhook.Handler {
return &handler{configuration: configuration}
}
// Validate Tenant name
@@ -110,7 +111,7 @@ func (h *handler) validateIngressHostnamesRegex(tenant *v1alpha1.Tenant) error {
// Check Ingress hostnames collision across all available Tenants
func (h *handler) validateIngressHostnamesCollision(context context.Context, clt client.Client, tenant *v1alpha1.Tenant) error {
if h.checkIngressHostnamesExact && tenant.Spec.IngressHostnames != nil && len(tenant.Spec.IngressHostnames.Exact) > 0 {
if !h.configuration.AllowTenantIngressHostnamesCollision() && tenant.Spec.IngressHostnames != nil && len(tenant.Spec.IngressHostnames.Exact) > 0 {
for _, h := range tenant.Spec.IngressHostnames.Exact {
tntList := &v1alpha1.TenantList{}
if err := clt.List(context, tntList, client.MatchingFieldsSelector{

View File

@@ -20,7 +20,6 @@ import (
"context"
"fmt"
"net/http"
"regexp"
"strings"
corev1 "k8s.io/api/core/v1"
@@ -29,6 +28,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/pkg/configuration"
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
)
@@ -57,14 +57,12 @@ func (w *webhook) GetPath() string {
}
type handler struct {
forceTenantPrefix bool
protectedNamespacesRegex *regexp.Regexp
configuration configuration.Configuration
}
func Handler(forceTenantPrefix bool, protectedNamespacesRegex *regexp.Regexp) capsulewebhook.Handler {
func Handler(configuration configuration.Configuration) capsulewebhook.Handler {
return &handler{
forceTenantPrefix: forceTenantPrefix,
protectedNamespacesRegex: protectedNamespacesRegex,
configuration: configuration,
}
}
@@ -74,13 +72,13 @@ func (r *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
if err := decoder.Decode(req, ns); err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
if r.protectedNamespacesRegex != nil {
if matched := r.protectedNamespacesRegex.MatchString(ns.GetName()); matched {
return admission.Denied("Creating namespaces with name matching " + r.protectedNamespacesRegex.String() + " regexp is not allowed; please, reach out to the system administrators")
if exp, _ := r.configuration.ProtectedNamespaceRegexp(); exp != nil {
if matched := exp.MatchString(ns.GetName()); matched {
return admission.Denied("Creating namespaces with name matching " + exp.String() + " regexp is not allowed; please, reach out to the system administrators")
}
}
if !r.forceTenantPrefix {
if !r.configuration.ForceTenantPrefix() {
return admission.Allowed("")
}

View File

@@ -22,19 +22,20 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/clastix/capsule/pkg/configuration"
"github.com/clastix/capsule/pkg/utils"
"github.com/clastix/capsule/pkg/webhook"
)
func InCapsuleGroups(capsuleGroups []string, webhookHandler webhook.Handler) webhook.Handler {
func InCapsuleGroups(configuration configuration.Configuration, webhookHandler webhook.Handler) webhook.Handler {
return &handler{
configuration: configuration,
handler: webhookHandler,
capsuleGroups: capsuleGroups,
}
}
type handler struct {
capsuleGroups []string
configuration configuration.Configuration
handler webhook.Handler
}
@@ -47,7 +48,7 @@ func (h handler) isCapsuleUser(req admission.Request) bool {
if groupList.Find("system:serviceaccounts:kube-system") {
return false
}
for _, group := range h.capsuleGroups {
for _, group := range h.configuration.UserGroups() {
if groupList.Find(group) {
return true
}