mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-25 16:07:24 +00:00
refactor: hostname collision is now managed at Tenant level
This commit is contained in:
@@ -18,15 +18,6 @@ type CapsuleConfigurationSpec struct {
|
||||
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
|
||||
|
||||
@@ -154,7 +154,7 @@ func main() {
|
||||
route.PVC(pvc.Handler()),
|
||||
route.Service(service.Handler()),
|
||||
route.NetworkPolicy(utils.InCapsuleGroups(cfg, networkpolicy.Handler())),
|
||||
route.Tenant(tenant.NameHandler(), tenant.IngressClassRegexHandler(), tenant.StorageClassRegexHandler(), tenant.ContainerRegistryRegexHandler(), tenant.HostnameRegexHandler(), tenant.HostnamesCollisionHandler(cfg), tenant.FreezedEmitter()),
|
||||
route.Tenant(tenant.NameHandler(), tenant.IngressClassRegexHandler(), tenant.StorageClassRegexHandler(), tenant.ContainerRegistryRegexHandler(), tenant.HostnameRegexHandler(), tenant.FreezedEmitter()),
|
||||
route.OwnerReference(utils.InCapsuleGroups(cfg, ownerreference.Handler(cfg))),
|
||||
route.Cordoning(tenant.CordoningHandler(cfg)),
|
||||
)
|
||||
@@ -224,7 +224,7 @@ func main() {
|
||||
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
|
||||
if err = indexer.AddToManager(manager, ctx); err != nil {
|
||||
if err = indexer.AddToManager(ctx, manager); err != nil {
|
||||
setupLog.Error(err, "unable to setup indexers")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -29,11 +29,9 @@ func NewCapsuleConfiguration(client client.Client, name string) Configuration {
|
||||
if machineryerr.IsNotFound(err) {
|
||||
return &capsulev1alpha1.CapsuleConfiguration{
|
||||
Spec: capsulev1alpha1.CapsuleConfigurationSpec{
|
||||
UserGroups: []string{"capsule.clastix.io"},
|
||||
ForceTenantPrefix: false,
|
||||
ProtectedNamespaceRegexpString: "",
|
||||
AllowTenantIngressHostnamesCollision: false,
|
||||
AllowIngressHostnameCollision: true,
|
||||
UserGroups: []string{"capsule.clastix.io"},
|
||||
ForceTenantPrefix: false,
|
||||
ProtectedNamespaceRegexpString: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -44,14 +42,6 @@ func NewCapsuleConfiguration(client client.Client, name string) Configuration {
|
||||
}}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
)
|
||||
|
||||
type Configuration interface {
|
||||
AllowIngressHostnameCollision() bool
|
||||
AllowTenantIngressHostnamesCollision() bool
|
||||
ProtectedNamespaceRegexp() (*regexp.Regexp, error)
|
||||
ForceTenantPrefix() bool
|
||||
UserGroups() []string
|
||||
|
||||
@@ -24,9 +24,8 @@ type CustomIndexer interface {
|
||||
Func() client.IndexerFunc
|
||||
}
|
||||
|
||||
func AddToManager(m manager.Manager, ctx context.Context) error {
|
||||
func AddToManager(ctx context.Context, mgr manager.Manager) error {
|
||||
indexers := append([]CustomIndexer{},
|
||||
tenant.IngressHostnames{},
|
||||
tenant.NamespacesReference{},
|
||||
tenant.OwnerReference{},
|
||||
namespace.OwnerReference{},
|
||||
@@ -44,7 +43,7 @@ func AddToManager(m manager.Manager, ctx context.Context) error {
|
||||
}
|
||||
|
||||
for _, f := range indexers {
|
||||
if err := m.GetFieldIndexer().IndexField(ctx, f.Object(), f.Field(), f.Func()); err != nil {
|
||||
if err := mgr.GetFieldIndexer().IndexField(ctx, f.Object(), f.Field(), f.Func()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
type IngressHostnames struct {
|
||||
}
|
||||
|
||||
func (IngressHostnames) Object() client.Object {
|
||||
return &capsulev1beta1.Tenant{}
|
||||
}
|
||||
|
||||
func (IngressHostnames) Field() string {
|
||||
return ".spec.ingressHostnames"
|
||||
}
|
||||
|
||||
func (IngressHostnames) Func() client.IndexerFunc {
|
||||
return func(object client.Object) (out []string) {
|
||||
tenant := object.(*capsulev1beta1.Tenant)
|
||||
if tenant.Spec.IngressOptions.AllowedHostnames != nil {
|
||||
out = append(out, tenant.Spec.IngressOptions.AllowedHostnames.Exact...)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -111,10 +111,6 @@ func (r *collision) OnDelete(client.Client, *admission.Decoder, record.EventReco
|
||||
}
|
||||
|
||||
func (r *collision) validateCollision(ctx context.Context, clt client.Client, ing Ingress, scope capsulev1beta1.HostnameCollisionScope) error {
|
||||
if r.configuration.AllowIngressHostnameCollision() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for hostname, paths := range ing.HostnamePathsPairs() {
|
||||
for path := range paths {
|
||||
var ingressObjList client.ObjectList
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
"github.com/clastix/capsule/pkg/configuration"
|
||||
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
|
||||
"github.com/clastix/capsule/pkg/webhook/utils"
|
||||
)
|
||||
|
||||
type hostnamesCollisionHandler struct {
|
||||
configuration configuration.Configuration
|
||||
}
|
||||
|
||||
func HostnamesCollisionHandler(configuration configuration.Configuration) capsulewebhook.Handler {
|
||||
return &hostnamesCollisionHandler{configuration: configuration}
|
||||
}
|
||||
|
||||
func (h *hostnamesCollisionHandler) validateTenant(ctx context.Context, req admission.Request, clt client.Client, decoder *admission.Decoder) *admission.Response {
|
||||
tenant := &capsulev1beta1.Tenant{}
|
||||
if err := decoder.Decode(req, tenant); err != nil {
|
||||
return utils.ErroredResponse(err)
|
||||
}
|
||||
|
||||
if !h.configuration.AllowTenantIngressHostnamesCollision() && tenant.Spec.IngressOptions.AllowedHostnames != nil && len(tenant.Spec.IngressOptions.AllowedHostnames.Exact) > 0 {
|
||||
for _, h := range tenant.Spec.IngressOptions.AllowedHostnames.Exact {
|
||||
tntList := &capsulev1beta1.TenantList{}
|
||||
if err := clt.List(ctx, tntList, client.MatchingFieldsSelector{
|
||||
Selector: fields.OneTermEqualSelector(".spec.ingressHostnames", h),
|
||||
}); err != nil {
|
||||
response := admission.Errored(http.StatusInternalServerError, fmt.Errorf("cannot retrieve Tenant list using .spec.ingressHostnames field selector: %w", err))
|
||||
|
||||
return &response
|
||||
}
|
||||
switch {
|
||||
case len(tntList.Items) == 1 && tntList.Items[0].GetName() == tenant.GetName():
|
||||
continue
|
||||
case len(tntList.Items) > 0:
|
||||
response := admission.Denied(fmt.Sprintf("the allowed hostname %s is already used by the Tenant %s, cannot proceed", h, tntList.Items[0].GetName()))
|
||||
|
||||
return &response
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *hostnamesCollisionHandler) OnCreate(client client.Client, decoder *admission.Decoder, _ record.EventRecorder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
if response := h.validateTenant(ctx, req, client, decoder); response != nil {
|
||||
return response
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *hostnamesCollisionHandler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
|
||||
return func(context.Context, admission.Request) *admission.Response {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *hostnamesCollisionHandler) OnUpdate(client client.Client, decoder *admission.Decoder, _ record.EventRecorder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
if response := h.validateTenant(ctx, req, client, decoder); response != nil {
|
||||
return response
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user