karmada/pkg/controllers/federatedresourcequota/federated_resource_quota_sy...

295 lines
8.3 KiB
Go

/*
Copyright 2024 The Karmada Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package federatedresourcequota
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
"github.com/karmada-io/karmada/pkg/util"
)
// setupTest initializes a test environment with the given runtime objects
// It returns a fake client and a SyncController for use in tests
func setupTest(t *testing.T, objs ...runtime.Object) (client.Client, *SyncController) {
scheme := runtime.NewScheme()
assert.NoError(t, policyv1alpha1.Install(scheme))
assert.NoError(t, workv1alpha1.Install(scheme))
assert.NoError(t, clusterv1alpha1.Install(scheme))
assert.NoError(t, corev1.AddToScheme(scheme))
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objs...).Build()
controller := &SyncController{
Client: fakeClient,
EventRecorder: record.NewFakeRecorder(100),
}
return fakeClient, controller
}
// TestCleanUpWorks tests the cleanUpWorks function of the SyncController
func TestCleanUpWorks(t *testing.T) {
tests := []struct {
name string
existingWorks []runtime.Object
namespace string
quotaName string
expectedError bool
}{
{
name: "Successfully delete works",
existingWorks: []runtime.Object{
&workv1alpha1.Work{
ObjectMeta: metav1.ObjectMeta{
Name: "work-1",
Namespace: "default",
Labels: map[string]string{
util.FederatedResourceQuotaNamespaceLabel: "default",
util.FederatedResourceQuotaNameLabel: "test-quota",
},
},
},
&workv1alpha1.Work{
ObjectMeta: metav1.ObjectMeta{
Name: "work-2",
Namespace: "default",
Labels: map[string]string{
util.FederatedResourceQuotaNamespaceLabel: "default",
util.FederatedResourceQuotaNameLabel: "test-quota",
},
},
},
},
namespace: "default",
quotaName: "test-quota",
expectedError: false,
},
{
name: "No works to delete",
existingWorks: []runtime.Object{},
namespace: "default",
quotaName: "test-quota",
expectedError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient, controller := setupTest(t, tt.existingWorks...)
err := controller.cleanUpWorks(context.Background(), tt.namespace, tt.quotaName)
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
// Verify that works are deleted
workList := &workv1alpha1.WorkList{}
err = fakeClient.List(context.Background(), workList, client.MatchingLabels{
util.FederatedResourceQuotaNamespaceLabel: tt.namespace,
util.FederatedResourceQuotaNameLabel: tt.quotaName,
})
assert.NoError(t, err)
assert.Empty(t, workList.Items)
})
}
}
// It verifies that works are correctly created for the given FederatedResourceQuota and clusters
func TestBuildWorks(t *testing.T) {
tests := []struct {
name string
quota *policyv1alpha1.FederatedResourceQuota
clusters []clusterv1alpha1.Cluster
expectedError bool
expectedWorks int
}{
{
name: "Successfully build works for all clusters",
quota: &policyv1alpha1.FederatedResourceQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "test-quota",
Namespace: "default",
},
Spec: policyv1alpha1.FederatedResourceQuotaSpec{
StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
{
ClusterName: "cluster1",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
},
},
{
ClusterName: "cluster2",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
},
},
},
},
},
clusters: []clusterv1alpha1.Cluster{
{ObjectMeta: metav1.ObjectMeta{Name: "cluster1"}},
{ObjectMeta: metav1.ObjectMeta{Name: "cluster2"}},
},
expectedError: false,
expectedWorks: 2,
},
{
name: "No clusters available",
quota: &policyv1alpha1.FederatedResourceQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "test-quota",
Namespace: "default",
},
Spec: policyv1alpha1.FederatedResourceQuotaSpec{
StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
{
ClusterName: "cluster1",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
},
},
},
},
},
clusters: []clusterv1alpha1.Cluster{},
expectedError: false,
expectedWorks: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient, controller := setupTest(t)
err := controller.buildWorks(context.Background(), tt.quota, tt.clusters)
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
// Verify the number of created works
workList := &workv1alpha1.WorkList{}
err = fakeClient.List(context.Background(), workList, client.MatchingLabels{
util.FederatedResourceQuotaNamespaceLabel: tt.quota.Namespace,
util.FederatedResourceQuotaNameLabel: tt.quota.Name,
})
assert.NoError(t, err)
assert.Len(t, workList.Items, tt.expectedWorks)
})
}
}
// TestExtractClusterHardResourceList tests the extractClusterHardResourceList function
func TestExtractClusterHardResourceList(t *testing.T) {
tests := []struct {
name string
spec policyv1alpha1.FederatedResourceQuotaSpec
clusterName string
expectedResult corev1.ResourceList
}{
{
name: "Cluster found in static assignments",
spec: policyv1alpha1.FederatedResourceQuotaSpec{
StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
{
ClusterName: "cluster1",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
},
},
},
clusterName: "cluster1",
expectedResult: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
},
{
name: "Cluster not found in static assignments",
spec: policyv1alpha1.FederatedResourceQuotaSpec{
StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
{
ClusterName: "cluster1",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
},
},
},
},
clusterName: "cluster2",
expectedResult: nil,
},
{
name: "Empty static assignments",
spec: policyv1alpha1.FederatedResourceQuotaSpec{
StaticAssignments: []policyv1alpha1.StaticClusterAssignment{},
},
clusterName: "cluster1",
expectedResult: nil,
},
{
name: "Multiple static assignments",
spec: policyv1alpha1.FederatedResourceQuotaSpec{
StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
{
ClusterName: "cluster1",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
},
},
{
ClusterName: "cluster2",
Hard: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
},
},
},
},
clusterName: "cluster2",
expectedResult: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := extractClusterHardResourceList(tt.spec, tt.clusterName)
assert.Equal(t, tt.expectedResult, result)
})
}
}