mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-19 04:26:45 +00:00
feat(tenant): add dedicated tenantowner crd (#1764)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
This commit is contained in:
@@ -7,13 +7,13 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
)
|
||||
|
||||
// ResourcePoolSpec.
|
||||
type ResourcePoolSpec struct {
|
||||
// Selector to match the namespaces that should be managed by the GlobalResourceQuota
|
||||
Selectors []api.NamespaceSelector `json:"selectors,omitempty"`
|
||||
Selectors []misc.NamespaceSelector `json:"selectors,omitempty"`
|
||||
// Define the resourcequota served by this resourcepool.
|
||||
Quota corev1.ResourceQuotaSpec `json:"quota"`
|
||||
// The Defaults given for each namespace, the default is not counted towards the total allocation
|
||||
|
||||
@@ -46,11 +46,13 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
|
||||
}
|
||||
|
||||
in.Spec.Owners = append(in.Spec.Owners, api.OwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.OwnerKind(owner.Kind),
|
||||
Name: owner.Name,
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.OwnerKind(owner.Kind),
|
||||
Name: owner.Name,
|
||||
},
|
||||
ClusterRoles: owner.GetRoles(*src, index),
|
||||
},
|
||||
ClusterRoles: owner.GetRoles(*src, index),
|
||||
ProxyOperations: proxySettings,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,15 +4,76 @@
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
func (in *Tenant) CollectOwners(ctx context.Context, c client.Client, allowPromotion bool, admins api.UserListSpec) (api.OwnerStatusListSpec, error) {
|
||||
owners := in.Spec.Owners.ToStatusOwners()
|
||||
|
||||
// Promoted ServiceAccounts
|
||||
if allowPromotion && len(in.Status.Namespaces) > 0 {
|
||||
saList := &corev1.ServiceAccountList{}
|
||||
if err := c.List(ctx, saList,
|
||||
client.MatchingLabels{
|
||||
meta.OwnerPromotionLabel: meta.OwnerPromotionLabelTrigger,
|
||||
},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, sa := range saList.Items {
|
||||
for _, ns := range in.Status.Namespaces {
|
||||
if sa.GetNamespace() != ns {
|
||||
continue
|
||||
}
|
||||
|
||||
owners.Upsert(api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: serviceaccount.ServiceAccountUsernamePrefix + sa.Namespace + ":" + sa.Name,
|
||||
},
|
||||
ClusterRoles: []string{
|
||||
api.ProvisionerRoleName,
|
||||
api.DeleterRoleName,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Administrators
|
||||
for _, a := range admins {
|
||||
owners.Upsert(api.CoreOwnerSpec{
|
||||
UserSpec: a,
|
||||
ClusterRoles: []string{
|
||||
api.DeleterRoleName,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Dedicated Owner Objects
|
||||
listed, err := in.Spec.Permissions.ListMatchingOwners(ctx, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, o := range listed {
|
||||
owners.Upsert(o.Spec.CoreOwnerSpec)
|
||||
}
|
||||
|
||||
return owners, nil
|
||||
}
|
||||
|
||||
func (in *Tenant) IsFull() bool {
|
||||
// we don't have limits on assigned Namespaces
|
||||
if in.Spec.NamespaceOptions == nil || in.Spec.NamespaceOptions.Quota == nil {
|
||||
|
||||
@@ -15,25 +15,31 @@ var tenant = &Tenant{
|
||||
Spec: TenantSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "User",
|
||||
Name: "user1",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "User",
|
||||
Name: "user1",
|
||||
},
|
||||
ClusterRoles: []string{"cluster-admin", "read-only"},
|
||||
},
|
||||
ClusterRoles: []string{"cluster-admin", "read-only"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "Group",
|
||||
Name: "group1",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "Group",
|
||||
Name: "group1",
|
||||
},
|
||||
ClusterRoles: []string{"edit"},
|
||||
},
|
||||
ClusterRoles: []string{"edit"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: "service",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: "service",
|
||||
},
|
||||
ClusterRoles: []string{"read-only"},
|
||||
},
|
||||
ClusterRoles: []string{"read-only"},
|
||||
},
|
||||
},
|
||||
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
||||
|
||||
@@ -6,6 +6,7 @@ package v1beta2
|
||||
import (
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
@@ -22,6 +23,8 @@ type TenantStatus struct {
|
||||
// Allowed Cluster Objects within Tenant
|
||||
TenantAvailableStatus `json:",inline"`
|
||||
|
||||
// Collected owners for this tenant
|
||||
Owners api.OwnerStatusListSpec `json:"owners,omitempty"`
|
||||
// +kubebuilder:default=Active
|
||||
// The operational state of the Tenant. Possible values are "Active", "Cordoned".
|
||||
State tenantState `json:"state"`
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
)
|
||||
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
type TenantSpec struct {
|
||||
// Specify Permissions for the Tenant.
|
||||
Permissions Permissions `json:"permissions,omitempty"`
|
||||
// Specifies the owners of the Tenant.
|
||||
// Optional
|
||||
Owners api.OwnerListSpec `json:"owners,omitempty"`
|
||||
@@ -72,6 +78,21 @@ type TenantSpec struct {
|
||||
ForceTenantPrefix *bool `json:"forceTenantPrefix,omitempty"`
|
||||
}
|
||||
|
||||
type Permissions struct {
|
||||
// Matches TenantOwner objects which are promoted to owners of this tenant
|
||||
// The elements are OR operations and independent. You can see the resulting Tenant Owners
|
||||
// in the Status.Owners specification of the Tenant.
|
||||
MatchOwners []*metav1.LabelSelector `json:"matchOwners,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Permissions) ListMatchingOwners(
|
||||
ctx context.Context,
|
||||
c client.Client,
|
||||
opts ...client.ListOption,
|
||||
) ([]*TenantOwner, error) {
|
||||
return misc.ListBySelectors[*TenantOwner](ctx, c, &TenantOwnerList{}, p.MatchOwners)
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// TenantOwnerSpec defines the desired state of TenantOwner.
|
||||
type TenantOwnerSpec struct {
|
||||
api.CoreOwnerSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// TenantOwnerStatus defines the observed state of TenantOwner.
|
||||
type TenantOwnerStatus struct{}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
|
||||
// TenantOwner is the Schema for the tenantowners API.
|
||||
type TenantOwner struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// metadata is a standard object metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
// spec defines the desired state of TenantOwner.
|
||||
// +required
|
||||
Spec TenantOwnerSpec `json:"spec"`
|
||||
|
||||
// status defines the observed state of TenantOwner.
|
||||
// +optional
|
||||
Status TenantOwnerStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// TenantOwnerList contains a list of TenantOwner.
|
||||
type TenantOwnerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []TenantOwner `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&TenantOwner{}, &TenantOwnerList{})
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package v1beta2
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -410,6 +411,32 @@ func (in *ObjectReferenceStatus) DeepCopy() *ObjectReferenceStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Permissions) DeepCopyInto(out *Permissions) {
|
||||
*out = *in
|
||||
if in.MatchOwners != nil {
|
||||
in, out := &in.MatchOwners, &out.MatchOwners
|
||||
*out = make([]*metav1.LabelSelector, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(metav1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Permissions.
|
||||
func (in *Permissions) DeepCopy() *Permissions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Permissions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ProcessedItems) DeepCopyInto(out *ProcessedItems) {
|
||||
{
|
||||
@@ -727,7 +754,7 @@ func (in *ResourcePoolSpec) DeepCopyInto(out *ResourcePoolSpec) {
|
||||
*out = *in
|
||||
if in.Selectors != nil {
|
||||
in, out := &in.Selectors, &out.Selectors
|
||||
*out = make([]api.NamespaceSelector, len(*in))
|
||||
*out = make([]misc.NamespaceSelector, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -982,6 +1009,96 @@ func (in *TenantList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantOwner) DeepCopyInto(out *TenantOwner) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwner.
|
||||
func (in *TenantOwner) DeepCopy() *TenantOwner {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwner)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TenantOwner) 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 *TenantOwnerList) DeepCopyInto(out *TenantOwnerList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]TenantOwner, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwnerList.
|
||||
func (in *TenantOwnerList) DeepCopy() *TenantOwnerList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwnerList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TenantOwnerList) 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 *TenantOwnerSpec) DeepCopyInto(out *TenantOwnerSpec) {
|
||||
*out = *in
|
||||
in.CoreOwnerSpec.DeepCopyInto(&out.CoreOwnerSpec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwnerSpec.
|
||||
func (in *TenantOwnerSpec) DeepCopy() *TenantOwnerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwnerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantOwnerStatus) DeepCopyInto(out *TenantOwnerStatus) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwnerStatus.
|
||||
func (in *TenantOwnerStatus) DeepCopy() *TenantOwnerStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwnerStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantResource) DeepCopyInto(out *TenantResource) {
|
||||
*out = *in
|
||||
@@ -1092,6 +1209,7 @@ func (in *TenantResourceStatus) DeepCopy() *TenantResourceStatus {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = *in
|
||||
in.Permissions.DeepCopyInto(&out.Permissions)
|
||||
if in.Owners != nil {
|
||||
in, out := &in.Owners, &out.Owners
|
||||
*out = make(api.OwnerListSpec, len(*in))
|
||||
@@ -1179,6 +1297,13 @@ func (in *TenantSpec) DeepCopy() *TenantSpec {
|
||||
func (in *TenantStatus) DeepCopyInto(out *TenantStatus) {
|
||||
*out = *in
|
||||
in.TenantAvailableStatus.DeepCopyInto(&out.TenantAvailableStatus)
|
||||
if in.Owners != nil {
|
||||
in, out := &in.Owners, &out.Owners
|
||||
*out = make(api.OwnerStatusListSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
|
||||
Reference in New Issue
Block a user