diff --git a/internal/modules/tenants/const.go b/internal/modules/tenants/const.go new file mode 100644 index 00000000..edae4537 --- /dev/null +++ b/internal/modules/tenants/const.go @@ -0,0 +1,8 @@ +// Copyright 2020-2023 Project Capsule Authors. +// SPDX-License-Identifier: Apache-2.0 + +package tenants + +const ( + basePath = "/apis/capsule.clastix.io/v1beta2/{endpoint:tenants/?}" +) diff --git a/internal/modules/tenants/get.go b/internal/modules/tenants/get.go new file mode 100644 index 00000000..b873b090 --- /dev/null +++ b/internal/modules/tenants/get.go @@ -0,0 +1,71 @@ +// Copyright 2020-2023 Project Capsule Authors. +// SPDX-License-Identifier: Apache-2.0 + +package tenants + +import ( + "net/http" + "slices" + + capsulev1beta2 "github.com/clastix/capsule/api/v1beta2" + "github.com/go-logr/logr" + "github.com/gorilla/mux" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/projectcapsule/capsule-proxy/internal/controllers" + "github.com/projectcapsule/capsule-proxy/internal/modules" + "github.com/projectcapsule/capsule-proxy/internal/modules/errors" + "github.com/projectcapsule/capsule-proxy/internal/request" + "github.com/projectcapsule/capsule-proxy/internal/tenant" +) + +type get struct { + capsuleLabel string + client client.Reader + log logr.Logger + rbReflector *controllers.RoleBindingReflector + gk schema.GroupKind +} + +func Get(roleBindingsReflector *controllers.RoleBindingReflector, client client.Reader) modules.Module { + label, _ := capsulev1beta2.GetTypeLabel(&capsulev1beta2.Tenant{}) + + return &get{ + capsuleLabel: label, + client: client, + log: ctrl.Log.WithName("tenant_get"), + rbReflector: roleBindingsReflector, + gk: schema.GroupKind{ + Group: corev1.GroupName, + Kind: "tenants", + }, + } +} + +func (l get) Path() string { + return "/apis/capsule.clastix.io/v1beta2/tenants/{name}" +} + +func (l get) Methods() []string { + return []string{http.MethodGet} +} + +func (l get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Request) (selector labels.Selector, err error) { + name := mux.Vars(proxyRequest.GetHTTPRequest())["name"] + + var userTenants []string + + for _, tnt := range proxyTenants { + userTenants = append(userTenants, tnt.Tenant.Name) + } + + if slices.Contains(userTenants, name) { + return labels.NewSelector(), nil + } else { + return nil, errors.NewNotFoundError(name, l.gk) + } +} diff --git a/internal/modules/tenants/list.go b/internal/modules/tenants/list.go new file mode 100644 index 00000000..4cb1d549 --- /dev/null +++ b/internal/modules/tenants/list.go @@ -0,0 +1,69 @@ +// Copyright 2020-2023 Project Capsule Authors. +// SPDX-License-Identifier: Apache-2.0 + +package tenants + +import ( + "net/http" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/selection" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/projectcapsule/capsule-proxy/internal/controllers" + "github.com/projectcapsule/capsule-proxy/internal/modules" + "github.com/projectcapsule/capsule-proxy/internal/modules/errors" + "github.com/projectcapsule/capsule-proxy/internal/request" + "github.com/projectcapsule/capsule-proxy/internal/tenant" +) + +type list struct { + roleBindingsReflector *controllers.RoleBindingReflector + log logr.Logger + gk schema.GroupKind +} + +func List(roleBindingsReflector *controllers.RoleBindingReflector) modules.Module { + return &list{ + roleBindingsReflector: roleBindingsReflector, + log: ctrl.Log.WithName("tenant_list"), + gk: schema.GroupKind{ + Group: corev1.GroupName, + Kind: "tenants", + }, + } +} + +func (l list) Path() string { + return basePath +} + +func (l list) Methods() []string { + return []string{http.MethodGet} +} + +func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Request) (selector labels.Selector, err error) { + var userTenants []string + + for _, tnt := range proxyTenants { + userTenants = append(userTenants, tnt.Tenant.Name) + } + + var r *labels.Requirement + + switch { + case len(userTenants) > 0: + r, err = labels.NewRequirement("kubernetes.io/metadata.name", selection.In, userTenants) + default: + r, err = labels.NewRequirement("dontexistsignoreme", selection.Exists, []string{}) + } + + if err != nil { + return nil, errors.NewBadRequest(err, l.gk) + } + + return labels.NewSelector().Add(*r), nil +} diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go index a13f82a9..a344e7c9 100644 --- a/internal/webserver/webserver.go +++ b/internal/webserver/webserver.go @@ -42,6 +42,7 @@ import ( "github.com/projectcapsule/capsule-proxy/internal/modules/priorityclass" "github.com/projectcapsule/capsule-proxy/internal/modules/runtimeclass" "github.com/projectcapsule/capsule-proxy/internal/modules/storageclass" + "github.com/projectcapsule/capsule-proxy/internal/modules/tenants" "github.com/projectcapsule/capsule-proxy/internal/options" req "github.com/projectcapsule/capsule-proxy/internal/request" "github.com/projectcapsule/capsule-proxy/internal/tenant" @@ -235,6 +236,8 @@ func (n *kubeFilter) registerModules(ctx context.Context, root *mux.Router) { metric.Get(n.reader), metric.List(n.reader), pod.Get(n.reader), + tenants.List(n.roleBindingsReflector), + tenants.Get(n.roleBindingsReflector, n.reader), } for _, i := range modList { mod := i