Files
capsule/pkg/tenant/namespaces_context_test.go
T
Oliver BählerandGitHub 99e05220ab feat: implement performance optimizations (#2045)
* feat: implement performance optimizations

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
2026-07-21 16:27:29 +02:00

255 lines
8.6 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package tenant_test
import (
"context"
"reflect"
"strings"
"testing"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"github.com/projectcapsule/capsule/pkg/api/meta"
"github.com/projectcapsule/capsule/pkg/api/rbac"
"github.com/projectcapsule/capsule/pkg/runtime/sanitize"
"github.com/projectcapsule/capsule/pkg/tenant"
"github.com/projectcapsule/capsule/pkg/users"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)
func TestNamespaceTerminationHelpers(t *testing.T) {
t.Parallel()
ctx := context.Background()
cl := tenantFakeClient(t,
&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "busy", Name: "pod-a"}},
)
pending, err := tenant.NamespaceIsPendingPodTerminating(ctx, cl, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "busy"}})
if err != nil {
t.Fatalf("NamespaceIsPendingPodTerminating() unexpected error: %v", err)
}
if !pending {
t.Fatalf("NamespaceIsPendingPodTerminating() = false, want true")
}
pending, err = tenant.NamespaceIsPendingPodTerminating(ctx, cl, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "empty"}})
if err != nil {
t.Fatalf("NamespaceIsPendingPodTerminating(empty) unexpected error: %v", err)
}
if pending {
t.Fatalf("NamespaceIsPendingPodTerminating(empty) = true, want false")
}
}
func TestNamespaceIsPendingUnmanagedTerminationByStatus(t *testing.T) {
t.Parallel()
ctx := context.Background()
tnt := tenantObject("tenant-a", withUID(types.UID("tenant-uid")))
tnt.Status.UpdateInstance(&capsulev1beta2.TenantStatusNamespaceItem{
Name: "tenant-a-ns",
Conditions: meta.ConditionList{{
Type: meta.TerminatingCondition,
Status: metav1.ConditionTrue,
}},
})
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
Name: "tenant-a-ns",
OwnerReferences: []metav1.OwnerReference{tenantOwnerReference("tenant-a", types.UID("tenant-uid"))},
}}
cl := tenantFakeClient(t, tnt, ns)
pending, err := tenant.NamespaceIsPendingUnmanagedTerminationByStatus(ctx, cl, ns)
if err != nil {
t.Fatalf("NamespaceIsPendingUnmanagedTerminationByStatus() unexpected error: %v", err)
}
if !pending {
t.Fatalf("NamespaceIsPendingUnmanagedTerminationByStatus() = false, want true")
}
pending, err = tenant.NamespaceIsPendingUnmanagedTerminationByStatus(ctx, cl, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "unowned"}})
if err != nil {
t.Fatalf("NamespaceIsPendingUnmanagedTerminationByStatus(unowned) unexpected error: %v", err)
}
if pending {
t.Fatalf("NamespaceIsPendingUnmanagedTerminationByStatus(unowned) = true, want false")
}
}
func TestResolveNamespaceTenant(t *testing.T) {
t.Parallel()
ctx := context.Background()
tnt := tenantObject("tenant-a", withUID(types.UID("tenant-uid")))
cl := tenantFakeClient(t, tnt)
valid := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
Name: "tenant-a-ns",
Labels: map[string]string{meta.TenantLabel: "tenant-a"},
OwnerReferences: []metav1.OwnerReference{
tenantOwnerReference("tenant-a", types.UID("tenant-uid")),
},
}}
got, err := tenant.ResolveNamespaceTenant(ctx, cl, valid)
if err != nil {
t.Fatalf("ResolveNamespaceTenant() unexpected error: %v", err)
}
if got == nil || got.Name != "tenant-a" {
t.Fatalf("ResolveNamespaceTenant() = %#v, want tenant-a", got)
}
if got, err := tenant.ResolveNamespaceTenant(ctx, cl, nil); err != nil || got != nil {
t.Fatalf("ResolveNamespaceTenant(nil) = %#v, %v, want nil nil", got, err)
}
tests := []struct {
name string
ns *corev1.Namespace
want string
}{
{
name: "label without owner reference",
ns: &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{meta.TenantLabel: "tenant-a"},
}},
want: "no Tenant ownerReference",
},
{
name: "owner reference without label",
ns: &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{tenantOwnerReference("tenant-a", types.UID("tenant-uid"))},
}},
want: "but no tenant label",
},
{
name: "mismatched label",
ns: &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{meta.TenantLabel: "other"},
OwnerReferences: []metav1.OwnerReference{tenantOwnerReference("tenant-a", types.UID("tenant-uid"))},
}},
want: "does not match owner reference",
},
}
for _, tt := range tests {
_, err := tenant.ResolveNamespaceTenant(ctx, cl, tt.ns)
if err == nil || !strings.Contains(err.Error(), tt.want) {
t.Fatalf("%s error = %v, want substring %q", tt.name, err, tt.want)
}
}
}
func TestCollectTenantNamespaceByLabel(t *testing.T) {
t.Parallel()
ctx := context.Background()
cl := tenantFakeClient(t,
&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "a", Labels: map[string]string{meta.TenantLabel: "tenant-a", "env": "prod"}}},
&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "b", Labels: map[string]string{meta.TenantLabel: "tenant-a", "env": "dev"}}},
&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "c", Labels: map[string]string{meta.TenantLabel: "tenant-b", "env": "prod"}}},
)
namespaces, err := tenant.CollectTenantNamespaceByLabel(ctx, cl, *tenantObject("tenant-a"), &metav1.LabelSelector{
MatchLabels: map[string]string{"env": "prod"},
})
if err != nil {
t.Fatalf("CollectTenantNamespaceByLabel() unexpected error: %v", err)
}
if names := collectedNamespaceNames(namespaces); !reflect.DeepEqual(names, []string{"a"}) {
t.Fatalf("CollectTenantNamespaceByLabel() = %#v, want namespace a", names)
}
}
func TestNamespaceOwnershipAndContexts(t *testing.T) {
t.Parallel()
tnt := tenantObject("tenant-a", withUID(types.UID("tenant-uid")), withStatusOwner(rbac.UserOwner, "alice"))
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
Name: "tenant-a-ns",
OwnerReferences: []metav1.OwnerReference{tenantOwnerReference("tenant-a", types.UID("tenant-uid"))},
}}
if !tenant.NamespaceIsOwned(context.Background(), nil, nil, ns, tnt, users.AdmissionUser{Username: "alice"}) {
t.Fatalf("NamespaceIsOwned() = false, want true for status owner")
}
if tenant.NamespaceIsOwned(context.Background(), nil, nil, ns, tnt, users.AdmissionUser{Username: "bob"}) {
t.Fatalf("NamespaceIsOwned() = true, want false for non-owner")
}
scheme := runtime.NewScheme()
if err := corev1.AddToScheme(scheme); err != nil {
t.Fatalf("adding core scheme: %v", err)
}
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
t.Fatalf("adding capsule scheme: %v", err)
}
ctx, err := tenant.NewTenantNamespaceContext(tnt, ns, scheme, sanitize.DefaultSanitizeOptions())
if err != nil {
t.Fatalf("NewTenantNamespaceContext() unexpected error: %v", err)
}
if ctx["tenant"] == nil || ctx["namespace"] == nil {
t.Fatalf("NewTenantNamespaceContext() = %#v, want tenant and namespace contexts", ctx)
}
fast := tenant.FastContextForTenantAndNamespace(tnt, ns)
if !reflect.DeepEqual(fast, map[string]string{"tenant.name": "tenant-a", "namespace": "tenant-a-ns"}) {
t.Fatalf("FastContextForTenantAndNamespace() = %#v", fast)
}
}
func TestTenantNamespaceContextIncludesStatusWhenRetained(t *testing.T) {
t.Parallel()
scheme := runtime.NewScheme()
if err := corev1.AddToScheme(scheme); err != nil {
t.Fatalf("adding core scheme: %v", err)
}
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
t.Fatalf("adding Capsule scheme: %v", err)
}
tnt := &capsulev1beta2.Tenant{
ObjectMeta: metav1.ObjectMeta{Name: "tenant-a"},
Status: capsulev1beta2.TenantStatus{State: capsulev1beta2.TenantStateActive},
}
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "tenant-a-ns"},
Status: corev1.NamespaceStatus{Phase: corev1.NamespaceActive},
}
opts := sanitize.DefaultSanitizeOptions()
opts.StripStatus = false
ctx, err := tenant.NewTenantNamespaceContext(tnt, ns, scheme, opts)
if err != nil {
t.Fatalf("NewTenantNamespaceContext() unexpected error: %v", err)
}
tenantContext := ctx["tenant"].(map[string]any)
tenantStatus := tenantContext["status"].(map[string]any)
if tenantStatus["state"] != string(capsulev1beta2.TenantStateActive) {
t.Fatalf("tenant status state = %#v, want Active", tenantStatus["state"])
}
namespaceContext := ctx["namespace"].(map[string]any)
namespaceStatus := namespaceContext["status"].(map[string]any)
if namespaceStatus["phase"] != string(corev1.NamespaceActive) {
t.Fatalf("namespace status phase = %#v, want Active", namespaceStatus["phase"])
}
}
func collectedNamespaceNames(namespaces []corev1.Namespace) []string {
names := make([]string, 0, len(namespaces))
for _, ns := range namespaces {
names = append(names, ns.Name)
}
return names
}