mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-19 12:36:39 +00:00
* feat: add globalresourcequota api Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
241 lines
6.1 KiB
Go
241 lines
6.1 KiB
Go
// Copyright 2020-2026 Project Capsule Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package tenant
|
|
|
|
import (
|
|
"maps"
|
|
"strings"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
|
"github.com/projectcapsule/capsule/pkg/api/meta"
|
|
tpl "github.com/projectcapsule/capsule/pkg/template"
|
|
"github.com/projectcapsule/capsule/pkg/utils"
|
|
)
|
|
|
|
func TenanLabelValue(ns *corev1.Namespace) string {
|
|
if ns.GetLabels() == nil {
|
|
return ""
|
|
}
|
|
|
|
return ns.GetLabels()[meta.TenantLabel]
|
|
}
|
|
|
|
func HasTenantReference(ns *corev1.Namespace) bool {
|
|
if ns.Labels != nil && ns.Labels[meta.TenantLabel] != "" {
|
|
return true
|
|
}
|
|
|
|
//nolint:modernize
|
|
for _, ref := range ns.OwnerReferences {
|
|
if IsTenantOwnerReference(ref) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// HasConsistentTenantReference reports whether Tenant ownership is either
|
|
// completely absent or represented by one matching label and ownerReference.
|
|
func HasConsistentTenantReference(ns *corev1.Namespace) bool {
|
|
if ns == nil {
|
|
return true
|
|
}
|
|
|
|
label := TenanLabelValue(ns)
|
|
refs := TenantOwnerReferences(ns)
|
|
|
|
if label == "" {
|
|
return len(refs) == 0
|
|
}
|
|
|
|
return len(refs) == 1 && refs[0].Name == label
|
|
}
|
|
|
|
func TenantOwnerReferences(ns *corev1.Namespace) []metav1.OwnerReference {
|
|
if ns == nil {
|
|
return nil
|
|
}
|
|
|
|
refs := make([]metav1.OwnerReference, 0)
|
|
|
|
for _, ref := range ns.GetOwnerReferences() {
|
|
if IsTenantOwnerReference(ref) {
|
|
refs = append(refs, ref)
|
|
}
|
|
}
|
|
|
|
return refs
|
|
}
|
|
|
|
func TenantOwnerReferenceName(ns *corev1.Namespace) string {
|
|
for _, ref := range ns.GetOwnerReferences() {
|
|
if IsTenantOwnerReference(ref) {
|
|
return ref.Name
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func HasTenantOwnership(ns *corev1.Namespace) bool {
|
|
return TenanLabelValue(ns) != "" || TenantOwnerReferenceName(ns) != ""
|
|
}
|
|
|
|
func TenantOwnershipChanged(oldNs, newNs *corev1.Namespace) bool {
|
|
return TenanLabelValue(oldNs) != TenanLabelValue(newNs) ||
|
|
HasTenantOwnership(oldNs) != HasTenantOwnership(newNs)
|
|
}
|
|
|
|
func AddNamespaceNameLabels(labels map[string]string, ns *corev1.Namespace) {
|
|
labels["kubernetes.io/metadata.name"] = ns.GetName()
|
|
}
|
|
|
|
func AddTenantNameLabel(labels map[string]string, tnt *capsulev1beta2.Tenant) {
|
|
labels[meta.TenantLabel] = tnt.GetName()
|
|
}
|
|
|
|
func HasCustomResourceQuotaAnnotation(tenant *capsulev1beta2.Tenant) bool {
|
|
for k := range tenant.GetAnnotations() {
|
|
if !strings.HasPrefix(k, meta.ResourceQuotaAnnotationPrefix) {
|
|
continue
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func BuildInstanceMetadataForNamespace(ns *corev1.Namespace, tnt *capsulev1beta2.Tenant) (labels map[string]string, annotations map[string]string) {
|
|
annotations = make(map[string]string)
|
|
labels = make(map[string]string)
|
|
|
|
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{
|
|
Name: ns.GetName(),
|
|
UID: ns.GetUID(),
|
|
})
|
|
|
|
if instance == nil {
|
|
return labels, annotations
|
|
}
|
|
|
|
annotations = instance.Metadata.Annotations
|
|
labels = instance.Metadata.Labels
|
|
|
|
return labels, annotations
|
|
}
|
|
|
|
func BuildNamespaceMetadataForTenant(ns *corev1.Namespace, tnt *capsulev1beta2.Tenant) (labels map[string]string, annotations map[string]string, err error) {
|
|
annotations = BuildNamespaceAnnotationsForTenant(tnt)
|
|
labels = BuildNamespaceLabelsForTenant(tnt)
|
|
|
|
fastContext := FastContextForTenantAndNamespace(tnt, ns)
|
|
|
|
if opts := tnt.Spec.NamespaceOptions; opts != nil && len(opts.AdditionalMetadataList) > 0 {
|
|
for _, md := range opts.AdditionalMetadataList {
|
|
var ok bool
|
|
|
|
ok, err = utils.IsNamespaceSelectedBySelector(ns, md.NamespaceSelector)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
tLabels := tpl.FastTemplateMap(md.Labels, fastContext)
|
|
tAnnotations := tpl.FastTemplateMap(md.Annotations, fastContext)
|
|
|
|
utils.MapMergeNoOverrite(labels, tLabels)
|
|
utils.MapMergeNoOverrite(annotations, tAnnotations)
|
|
}
|
|
}
|
|
|
|
return labels, annotations, nil
|
|
}
|
|
|
|
func BuildNamespaceAnnotationsForTenant(tnt *capsulev1beta2.Tenant) map[string]string {
|
|
annotations := make(map[string]string)
|
|
|
|
//nolint:staticcheck
|
|
if md := tnt.Spec.NamespaceOptions; md != nil && md.AdditionalMetadata != nil {
|
|
maps.Copy(annotations, md.AdditionalMetadata.Annotations)
|
|
}
|
|
|
|
if tnt.Spec.NodeSelector != nil {
|
|
annotations = utils.BuildNodeSelector(tnt, annotations)
|
|
}
|
|
|
|
if ic := tnt.Spec.IngressOptions.AllowedClasses; ic != nil {
|
|
if len(ic.Exact) > 0 {
|
|
annotations[meta.AvailableIngressClassesAnnotation] = strings.Join(ic.Exact, ",")
|
|
}
|
|
|
|
//nolint:staticcheck,nolintlint // Preserve annotations for the deprecated v1beta2 field until it is removed.
|
|
if len(ic.Regex) > 0 {
|
|
annotations[meta.AvailableIngressClassesRegexpAnnotation] = ic.Regex
|
|
}
|
|
}
|
|
|
|
if sc := tnt.Spec.StorageClasses; sc != nil {
|
|
if len(sc.Exact) > 0 {
|
|
annotations[meta.AvailableStorageClassesAnnotation] = strings.Join(sc.Exact, ",")
|
|
}
|
|
|
|
//nolint:staticcheck,nolintlint // Preserve annotations for the deprecated v1beta2 field until it is removed.
|
|
if len(sc.Regex) > 0 {
|
|
annotations[meta.AvailableStorageClassesRegexpAnnotation] = sc.Regex
|
|
}
|
|
}
|
|
|
|
//nolint:staticcheck
|
|
if cr := tnt.Spec.ContainerRegistries; cr != nil {
|
|
if len(cr.Exact) > 0 {
|
|
annotations[meta.AllowedRegistriesAnnotation] = strings.Join(cr.Exact, ",")
|
|
}
|
|
|
|
//nolint:staticcheck
|
|
if len(cr.Regex) > 0 {
|
|
annotations[meta.AllowedRegistriesRegexpAnnotation] = cr.Regex
|
|
}
|
|
}
|
|
|
|
for _, key := range []string{
|
|
meta.ForbiddenNamespaceLabelsAnnotation,
|
|
meta.ForbiddenNamespaceLabelsRegexpAnnotation,
|
|
meta.ForbiddenNamespaceAnnotationsAnnotation,
|
|
meta.ForbiddenNamespaceAnnotationsRegexpAnnotation,
|
|
} {
|
|
if value, ok := tnt.Annotations[key]; ok {
|
|
annotations[key] = value
|
|
}
|
|
}
|
|
|
|
return annotations
|
|
}
|
|
|
|
func BuildNamespaceLabelsForTenant(tnt *capsulev1beta2.Tenant) map[string]string {
|
|
labels := make(map[string]string)
|
|
|
|
if HasCustomResourceQuotaAnnotation(tnt) {
|
|
labels[meta.CustomResourcesLabel] = meta.ValueTrue
|
|
}
|
|
|
|
//nolint:staticcheck
|
|
if md := tnt.Spec.NamespaceOptions; md != nil && md.AdditionalMetadata != nil {
|
|
maps.Copy(labels, md.AdditionalMetadata.Labels)
|
|
}
|
|
|
|
if tnt.Spec.Cordoned {
|
|
labels[meta.CordonedLabel] = meta.ValueTrue
|
|
}
|
|
|
|
return labels
|
|
}
|