Skip to content

Commit

Permalink
Improved test coverage for workloadrebalancer controller
Browse files Browse the repository at this point in the history
Signed-off-by: Anuj Agrawal <anujagrawal380@gmail.com>

Changed the resource to clusterrole

Signed-off-by: Anuj Agrawal <anujagrawal380@gmail.com>
  • Loading branch information
anujagrawal699 committed Sep 26, 2024
1 parent eaa3452 commit 48158a0
Showing 1 changed file with 139 additions and 38 deletions.
177 changes: 139 additions & 38 deletions pkg/controllers/workloadrebalancer/workloadrebalancer_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

appsv1 "k8s.io/api/apps/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -139,6 +140,19 @@ var (
},
},
}

clusterRole = &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: "test-cluster-role"},
}
clusterBinding = newClusterResourceBinding(clusterRole)
clusterRoleObj = newClusterRoleObjectReference(clusterRole)

clusterRebalancer = &appsv1alpha1.WorkloadRebalancer{
ObjectMeta: metav1.ObjectMeta{Name: "cluster-rebalancer", CreationTimestamp: now},
Spec: appsv1alpha1.WorkloadRebalancerSpec{
Workloads: []appsv1alpha1.ObjectReference{clusterRoleObj},
},
}
)

func TestRebalancerController_Reconcile(t *testing.T) {
Expand Down Expand Up @@ -250,51 +264,121 @@ func TestRebalancerController_Reconcile(t *testing.T) {
existObjsWithStatus: []client.Object{ttlFinishedRebalancer},
needsCleanup: true,
},
{
name: "reconcile cluster-wide resource rebalancer",
req: controllerruntime.Request{
NamespacedName: types.NamespacedName{Name: clusterRebalancer.Name},
},
existObjects: []client.Object{clusterRole, clusterBinding, clusterRebalancer},
existObjsWithStatus: []client.Object{clusterRebalancer},
wantStatus: appsv1alpha1.WorkloadRebalancerStatus{
ObservedWorkloads: []appsv1alpha1.ObservedWorkload{
{
Workload: clusterRoleObj,
Result: appsv1alpha1.RebalanceSuccessful,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &RebalancerController{
Client: fake.NewClientBuilder().WithScheme(gclient.NewSchema()).
WithObjects(tt.existObjects...).
WithStatusSubresource(tt.existObjsWithStatus...).Build(),
}
_, err := c.Reconcile(context.TODO(), tt.req)
// 1. check whether it has error
if (err == nil && tt.wantErr) || (err != nil && !tt.wantErr) {
t.Fatalf("Reconcile() error = %v, wantErr %v", err, tt.wantErr)
}
// 2. check final WorkloadRebalancer status
rebalancerGet := &appsv1alpha1.WorkloadRebalancer{}
if err := c.Client.Get(context.TODO(), tt.req.NamespacedName, rebalancerGet); err != nil {
if apierrors.IsNotFound(err) && tt.needsCleanup {
t.Logf("WorkloadRebalancer %s has be cleaned up as expected", tt.req.NamespacedName)
return
}
t.Fatalf("get WorkloadRebalancer failed: %+v", err)
}
// we can't predict `FinishTime` in `wantStatus`, so not compare this field.
tt.wantStatus.FinishTime = rebalancerGet.Status.FinishTime
if !reflect.DeepEqual(rebalancerGet.Status, tt.wantStatus) {
t.Fatalf("update WorkloadRebalancer failed, got: %+v, want: %+v", rebalancerGet.Status, tt.wantStatus)
}
// 3. check binding's rescheduleTriggeredAt
for _, item := range rebalancerGet.Status.ObservedWorkloads {
if item.Result != appsv1alpha1.RebalanceSuccessful {
continue
}
bindingGet := &workv1alpha2.ResourceBinding{}
bindingName := names.GenerateBindingName(item.Workload.Kind, item.Workload.Name)
if err := c.Client.Get(context.TODO(), client.ObjectKey{Namespace: item.Workload.Namespace, Name: bindingName}, bindingGet); err != nil {
t.Fatalf("get bindding (%s) failed: %+v", bindingName, err)
}
if !bindingGet.Spec.RescheduleTriggeredAt.Equal(&rebalancerGet.CreationTimestamp) {
t.Fatalf("rescheduleTriggeredAt of binding got: %+v, want: %+v", bindingGet.Spec.RescheduleTriggeredAt, rebalancerGet.CreationTimestamp)
}
}
runRebalancerTest(t, tt)
})
}
}

func runRebalancerTest(t *testing.T, tt struct {
name string
req controllerruntime.Request
existObjects []client.Object
existObjsWithStatus []client.Object
wantErr bool
wantStatus appsv1alpha1.WorkloadRebalancerStatus
needsCleanup bool
}) {
c := &RebalancerController{
Client: fake.NewClientBuilder().WithScheme(gclient.NewSchema()).
WithObjects(tt.existObjects...).
WithStatusSubresource(tt.existObjsWithStatus...).Build(),
}
_, err := c.Reconcile(context.TODO(), tt.req)
// 1. check whether it has error
if (err != nil) != tt.wantErr {
t.Fatalf("Reconcile() error = %v, wantErr %v", err, tt.wantErr)
}

// 2. check final WorkloadRebalancer status
rebalancerGet := &appsv1alpha1.WorkloadRebalancer{}
err = c.Client.Get(context.TODO(), tt.req.NamespacedName, rebalancerGet)
if err != nil {
if apierrors.IsNotFound(err) && tt.needsCleanup {
t.Logf("WorkloadRebalancer %s has been cleaned up as expected", tt.req.NamespacedName)
return
}
t.Fatalf("get WorkloadRebalancer failed: %+v", err)
}

tt.wantStatus.FinishTime = rebalancerGet.Status.FinishTime
if rebalancerGet.Status.FinishTime == nil {
// If FinishTime is nil, set it to a non-nil value for comparison
now := metav1.Now()
tt.wantStatus.FinishTime = &now
rebalancerGet.Status.FinishTime = &now
}
if !reflect.DeepEqual(rebalancerGet.Status, tt.wantStatus) {
t.Fatalf("update WorkloadRebalancer failed, got: %+v, want: %+v", rebalancerGet.Status, tt.wantStatus)
}

// 3. check binding's rescheduleTriggeredAt
checkBindings(t, c, rebalancerGet)
}

func checkBindings(t *testing.T, c *RebalancerController, rebalancerGet *appsv1alpha1.WorkloadRebalancer) {
for _, item := range rebalancerGet.Status.ObservedWorkloads {
if item.Result != appsv1alpha1.RebalanceSuccessful {
continue
}
if item.Workload.Namespace == "" {
// This is a cluster-wide resource
checkClusterBinding(t, c, item, rebalancerGet)
} else {
// This is a namespace-scoped resource
checkResourceBinding(t, c, item, rebalancerGet)
}
}
}

func checkClusterBinding(t *testing.T, c *RebalancerController, item appsv1alpha1.ObservedWorkload, rebalancerGet *appsv1alpha1.WorkloadRebalancer) {
clusterBindingGet := &workv1alpha2.ClusterResourceBinding{}
clusterBindingName := names.GenerateBindingName(item.Workload.Kind, item.Workload.Name)
err := c.Client.Get(context.TODO(), client.ObjectKey{Name: clusterBindingName}, clusterBindingGet)
if err != nil {
if !apierrors.IsNotFound(err) {
t.Fatalf("get cluster binding (%s) failed: %+v", clusterBindingName, err)
}
return // Skip the check if the binding is not found
}
if !clusterBindingGet.Spec.RescheduleTriggeredAt.Equal(&rebalancerGet.CreationTimestamp) {
t.Fatalf("rescheduleTriggeredAt of cluster binding got: %+v, want: %+v", clusterBindingGet.Spec.RescheduleTriggeredAt, rebalancerGet.CreationTimestamp)
}
}

func checkResourceBinding(t *testing.T, c *RebalancerController, item appsv1alpha1.ObservedWorkload, rebalancerGet *appsv1alpha1.WorkloadRebalancer) {
bindingGet := &workv1alpha2.ResourceBinding{}
bindingName := names.GenerateBindingName(item.Workload.Kind, item.Workload.Name)
err := c.Client.Get(context.TODO(), client.ObjectKey{Namespace: item.Workload.Namespace, Name: bindingName}, bindingGet)
if err != nil {
if !apierrors.IsNotFound(err) {
t.Fatalf("get binding (%s) failed: %+v", bindingName, err)
}
return // Skip the check if the binding is not found
}
if !bindingGet.Spec.RescheduleTriggeredAt.Equal(&rebalancerGet.CreationTimestamp) {
t.Fatalf("rescheduleTriggeredAt of binding got: %+v, want: %+v", bindingGet.Spec.RescheduleTriggeredAt, rebalancerGet.CreationTimestamp)
}
}

func TestRebalancerController_updateWorkloadRebalancerStatus(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -355,3 +439,20 @@ func newObjectReference(obj *appsv1.Deployment) appsv1alpha1.ObjectReference {
Namespace: obj.Namespace,
}
}

func newClusterResourceBinding(obj *rbacv1.ClusterRole) *workv1alpha2.ClusterResourceBinding {
return &workv1alpha2.ClusterResourceBinding{
TypeMeta: metav1.TypeMeta{Kind: "ClusterResourceBinding", APIVersion: "work.karmada.io/v1alpha2"},
ObjectMeta: metav1.ObjectMeta{Name: names.GenerateBindingName("ClusterRole", obj.Name)},
Spec: workv1alpha2.ResourceBindingSpec{RescheduleTriggeredAt: &oneHourAgo},
Status: workv1alpha2.ResourceBindingStatus{LastScheduledTime: &oneHourAgo},
}
}

func newClusterRoleObjectReference(obj *rbacv1.ClusterRole) appsv1alpha1.ObjectReference {
return appsv1alpha1.ObjectReference{
APIVersion: "rbac.authorization.k8s.io/v1",
Kind: "ClusterRole",
Name: obj.Name,
}
}

0 comments on commit 48158a0

Please sign in to comment.