mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-20 04:56:44 +00:00
* feat: implement performance optimizations --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
279 lines
9.8 KiB
Go
279 lines
9.8 KiB
Go
// Copyright 2020-2026 Project Capsule Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package tenant
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
|
|
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
|
capmeta "github.com/projectcapsule/capsule/pkg/api/meta"
|
|
"github.com/projectcapsule/capsule/pkg/api/rbac"
|
|
)
|
|
|
|
func TestUpdateReconcilingStatusSynchronizesCurrentStatus(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scheme := runtime.NewScheme()
|
|
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
|
|
t.Fatalf("add Capsule scheme: %v", err)
|
|
}
|
|
|
|
latest := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "tenant", Generation: 2},
|
|
Status: capsulev1beta2.TenantStatus{
|
|
ObservedGeneration: 2,
|
|
State: capsulev1beta2.TenantStateActive,
|
|
Conditions: capmeta.ConditionList{
|
|
capmeta.NewReadyCondition(&capsulev1beta2.Tenant{}),
|
|
capmeta.NewCordonedCondition(&capsulev1beta2.Tenant{}),
|
|
},
|
|
},
|
|
}
|
|
reader := fake.NewClientBuilder().WithScheme(scheme).WithObjects(latest).Build()
|
|
manager := &Manager{Client: reader, reader: reader}
|
|
|
|
stale := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{Name: latest.Name, Generation: latest.Generation},
|
|
}
|
|
if err := manager.updateReconcilingStatus(context.Background(), stale); err != nil {
|
|
t.Fatalf("update reconciling status: %v", err)
|
|
}
|
|
|
|
if len(stale.Status.Conditions) == 0 {
|
|
t.Fatal("current API status was not copied to stale reconcile instance")
|
|
}
|
|
if stale.Status.ObservedGeneration != latest.Status.ObservedGeneration {
|
|
t.Fatalf("observed generation = %d, want %d", stale.Status.ObservedGeneration, latest.Status.ObservedGeneration)
|
|
}
|
|
}
|
|
|
|
func TestEnsureTenantStatusInitializedTracksCordoning(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tenant := &capsulev1beta2.Tenant{}
|
|
ensureTenantStatusInitialized(tenant)
|
|
condition := tenant.Status.Conditions.GetConditionByType(capmeta.CordonedCondition)
|
|
if condition == nil || condition.Status != metav1.ConditionFalse || condition.Reason != capmeta.ActiveReason {
|
|
t.Fatalf("initial Cordoned condition = %#v, want False/%s", condition, capmeta.ActiveReason)
|
|
}
|
|
|
|
tenant.Spec.Cordoned = true
|
|
ensureTenantStatusInitialized(tenant)
|
|
condition = tenant.Status.Conditions.GetConditionByType(capmeta.CordonedCondition)
|
|
if condition == nil || condition.Status != metav1.ConditionTrue || condition.Reason != capmeta.CordonedReason {
|
|
t.Fatalf("cordoned condition = %#v, want True/%s", condition, capmeta.CordonedReason)
|
|
}
|
|
if tenant.Status.State != capsulev1beta2.TenantStateCordoned {
|
|
t.Fatalf("state = %q, want Cordoned", tenant.Status.State)
|
|
}
|
|
|
|
tenant.Spec.Cordoned = false
|
|
ensureTenantStatusInitialized(tenant)
|
|
condition = tenant.Status.Conditions.GetConditionByType(capmeta.CordonedCondition)
|
|
if condition == nil || condition.Status != metav1.ConditionFalse || condition.Reason != capmeta.ActiveReason {
|
|
t.Fatalf("uncordoned condition = %#v, want False/%s", condition, capmeta.ActiveReason)
|
|
}
|
|
}
|
|
|
|
func TestUpdateReconcilingStatusRepairsSpecOwnersAtCurrentGeneration(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scheme := runtime.NewScheme()
|
|
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
|
|
t.Fatalf("add Capsule scheme: %v", err)
|
|
}
|
|
|
|
promoted := rbac.CoreOwnerSpec{
|
|
UserSpec: rbac.UserSpec{Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:tenant:promoted"},
|
|
}
|
|
specOwner := rbac.CoreOwnerSpec{
|
|
UserSpec: rbac.UserSpec{Kind: rbac.UserOwner, Name: "alice"},
|
|
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
|
}
|
|
latest := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "tenant", Generation: 2},
|
|
Spec: capsulev1beta2.TenantSpec{
|
|
Owners: rbac.OwnerListSpec{{CoreOwnerSpec: specOwner}},
|
|
},
|
|
Status: capsulev1beta2.TenantStatus{
|
|
ObservedGeneration: 2,
|
|
Owners: rbac.OwnerStatusListSpec{promoted},
|
|
},
|
|
}
|
|
cl := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithStatusSubresource(&capsulev1beta2.Tenant{}).
|
|
WithObjects(latest).
|
|
Build()
|
|
manager := &Manager{Client: cl, reader: cl}
|
|
instance := latest.DeepCopy()
|
|
|
|
if err := manager.updateReconcilingStatus(context.Background(), instance); err != nil {
|
|
t.Fatalf("update reconciling status: %v", err)
|
|
}
|
|
|
|
updated := &capsulev1beta2.Tenant{}
|
|
if err := cl.Get(context.Background(), client.ObjectKey{Name: latest.Name}, updated); err != nil {
|
|
t.Fatalf("get updated tenant: %v", err)
|
|
}
|
|
if _, found := updated.Status.Owners.FindOwner(specOwner.Name, specOwner.Kind); !found {
|
|
t.Fatalf("spec owner was not written: %#v", updated.Status.Owners)
|
|
}
|
|
if _, found := updated.Status.Owners.FindOwner(promoted.Name, promoted.Kind); !found {
|
|
t.Fatalf("existing promoted owner was not preserved: %#v", updated.Status.Owners)
|
|
}
|
|
if updated.Status.State != capsulev1beta2.TenantStateActive {
|
|
t.Fatalf("status state = %q, want Active", updated.Status.State)
|
|
}
|
|
if updated.Status.Conditions.GetConditionByType(capmeta.ReadyCondition) == nil {
|
|
t.Fatal("Ready condition was not initialized")
|
|
}
|
|
}
|
|
|
|
func TestUpdateTenantOwnersStatusPersistsEvaluatedOwners(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scheme := runtime.NewScheme()
|
|
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
|
|
t.Fatalf("add Capsule scheme: %v", err)
|
|
}
|
|
|
|
stored := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "tenant"},
|
|
Status: capsulev1beta2.TenantStatus{
|
|
State: capsulev1beta2.TenantStateActive,
|
|
},
|
|
}
|
|
owners := rbac.OwnerStatusListSpec{{
|
|
UserSpec: rbac.UserSpec{Kind: rbac.UserOwner, Name: "alice"},
|
|
ClusterRoles: []string{
|
|
"admin",
|
|
"capsule-namespace-deleter",
|
|
},
|
|
}}
|
|
|
|
cl := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithStatusSubresource(&capsulev1beta2.Tenant{}).
|
|
WithObjects(stored).
|
|
Build()
|
|
manager := &Manager{Client: cl, reader: cl}
|
|
instance := stored.DeepCopy()
|
|
instance.Status.Owners = owners
|
|
|
|
if err := manager.updateTenantOwnersStatus(context.Background(), instance); err != nil {
|
|
t.Fatalf("update tenant owner status: %v", err)
|
|
}
|
|
|
|
updated := &capsulev1beta2.Tenant{}
|
|
if err := cl.Get(context.Background(), client.ObjectKey{Name: stored.Name}, updated); err != nil {
|
|
t.Fatalf("get updated tenant: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(updated.Status.Owners, owners) {
|
|
t.Fatalf("status owners = %#v, want %#v", updated.Status.Owners, owners)
|
|
}
|
|
if updated.Status.State != stored.Status.State {
|
|
t.Fatalf("status state = %q, want %q", updated.Status.State, stored.Status.State)
|
|
}
|
|
}
|
|
|
|
func TestUpdateTenantOwnersStatusInitializesRequiredStatus(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scheme := runtime.NewScheme()
|
|
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
|
|
t.Fatalf("add Capsule scheme: %v", err)
|
|
}
|
|
|
|
stored := &capsulev1beta2.Tenant{ObjectMeta: metav1.ObjectMeta{Name: "tenant"}}
|
|
cl := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithStatusSubresource(&capsulev1beta2.Tenant{}).
|
|
WithObjects(stored).
|
|
Build()
|
|
manager := &Manager{Client: cl, reader: cl}
|
|
instance := stored.DeepCopy()
|
|
instance.Status.Owners = rbac.OwnerStatusListSpec{{
|
|
UserSpec: rbac.UserSpec{Kind: rbac.UserOwner, Name: "alice"},
|
|
}}
|
|
|
|
if err := manager.updateTenantOwnersStatus(context.Background(), instance); err != nil {
|
|
t.Fatalf("update tenant owner status: %v", err)
|
|
}
|
|
|
|
updated := &capsulev1beta2.Tenant{}
|
|
if err := cl.Get(context.Background(), client.ObjectKey{Name: stored.Name}, updated); err != nil {
|
|
t.Fatalf("get updated tenant: %v", err)
|
|
}
|
|
if updated.Status.State != capsulev1beta2.TenantStateActive {
|
|
t.Fatalf("status state = %q, want Active", updated.Status.State)
|
|
}
|
|
if updated.Status.Conditions.GetConditionByType(capmeta.ReadyCondition) == nil {
|
|
t.Fatal("Ready condition was not initialized")
|
|
}
|
|
}
|
|
|
|
func TestUpdateTenantClassStatusPreservesOwnersAndConditions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scheme := runtime.NewScheme()
|
|
if err := capsulev1beta2.AddToScheme(scheme); err != nil {
|
|
t.Fatalf("add Capsule scheme: %v", err)
|
|
}
|
|
|
|
owner := rbac.CoreOwnerSpec{UserSpec: rbac.UserSpec{Kind: rbac.UserOwner, Name: "alice"}}
|
|
ready := capmeta.NewReadyCondition(&capsulev1beta2.Tenant{})
|
|
cordoned := capmeta.NewCordonedCondition(&capsulev1beta2.Tenant{})
|
|
stored := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "tenant"},
|
|
Status: capsulev1beta2.TenantStatus{
|
|
Owners: rbac.OwnerStatusListSpec{owner},
|
|
State: capsulev1beta2.TenantStateActive,
|
|
Conditions: capmeta.ConditionList{ready, cordoned},
|
|
},
|
|
}
|
|
cl := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithStatusSubresource(&capsulev1beta2.Tenant{}).
|
|
WithObjects(stored).
|
|
Build()
|
|
manager := &Manager{Client: cl, reader: cl}
|
|
persistedBefore := &capsulev1beta2.Tenant{}
|
|
if err := cl.Get(context.Background(), client.ObjectKey{Name: stored.Name}, persistedBefore); err != nil {
|
|
t.Fatalf("get tenant before class update: %v", err)
|
|
}
|
|
if err := manager.updateTenantClassStatus(
|
|
context.Background(),
|
|
stored.Name,
|
|
func(_ context.Context, tenant *capsulev1beta2.Tenant) error {
|
|
tenant.Status.Classes.StorageClasses = []string{"fast"}
|
|
|
|
return nil
|
|
},
|
|
); err != nil {
|
|
t.Fatalf("update tenant class status: %v", err)
|
|
}
|
|
|
|
updated := &capsulev1beta2.Tenant{}
|
|
if err := cl.Get(context.Background(), client.ObjectKey{Name: stored.Name}, updated); err != nil {
|
|
t.Fatalf("get updated tenant: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(updated.Status.Owners, persistedBefore.Status.Owners) {
|
|
t.Fatalf("owners = %#v, want preserved %#v", updated.Status.Owners, persistedBefore.Status.Owners)
|
|
}
|
|
if !reflect.DeepEqual(updated.Status.Conditions, persistedBefore.Status.Conditions) {
|
|
t.Fatalf("conditions = %#v, want preserved %#v", updated.Status.Conditions, persistedBefore.Status.Conditions)
|
|
}
|
|
if !reflect.DeepEqual(updated.Status.Classes.StorageClasses, []string{"fast"}) {
|
|
t.Fatalf("storage classes = %#v, want [fast]", updated.Status.Classes.StorageClasses)
|
|
}
|
|
}
|