Skip to content

Commit

Permalink
trigger sync when cluster belongs to LabelSelector type clusterset (o…
Browse files Browse the repository at this point in the history
…pen-cluster-management-io#81)

Signed-off-by: haoqing0110 <qhao@redhat.com>

Signed-off-by: haoqing0110 <qhao@redhat.com>
  • Loading branch information
haoqing0110 authored Sep 26, 2022
1 parent 6b2c9ef commit 4f1e7c3
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 47 deletions.
54 changes: 26 additions & 28 deletions pkg/controllers/scheduling/cluster_event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package scheduling

import (
"fmt"
"reflect"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
cache "k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
clusterlisterv1beta1 "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1"
clusterapiv1 "open-cluster-management.io/api/cluster/v1"
clusterapiv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
)

type clusterEventHandler struct {
Expand Down Expand Up @@ -38,8 +39,8 @@ func (h *clusterEventHandler) OnUpdate(oldObj, newObj interface{}) {
return
}

// if the clusterset of the cluster changes, process the original clusterset
if newCluster.Labels[clusterSetLabel] != oldCluster.Labels[clusterSetLabel] {
// if the cluster labels changes, process the original clusterset
if !reflect.DeepEqual(newCluster.Labels, oldCluster.Labels) {
h.onChange(oldCluster)
}
}
Expand All @@ -62,46 +63,43 @@ func (h *clusterEventHandler) onChange(obj interface{}) {
return
}

clusterSetName, err := h.getClusterSetName(cluster)
clusterSetNames, err := h.getClusterSetNames(cluster)
if err != nil {
klog.V(4).Infof("Unable to get clusterset of cluster %q: %v", cluster.GetName(), err)
return
}

// skip cluster belongs to no clusterset
if len(clusterSetName) == 0 {
if len(clusterSetNames) == 0 {
return
}

// enqueue placements which might be impacted
err = enqueuePlacementsByClusterSet(
clusterSetName,
h.clusterSetBindingLister,
h.placementLister,
h.enqueuePlacementFunc,
)
if err != nil {
klog.Errorf("Unable to enqueue placements with access to clusterset %q: %v", clusterSetName, err)
for _, clusterSetName := range clusterSetNames {
// enqueue placements which might be impacted
err = enqueuePlacementsByClusterSet(
clusterSetName,
h.clusterSetBindingLister,
h.placementLister,
h.enqueuePlacementFunc,
)
if err != nil {
klog.Errorf("Unable to enqueue placements with access to clusterset %q: %v", clusterSetName, err)
}
}
}

// getClusterSetName returns the name of the clusterset the cluster belongs to. It also checks the existence
// of the clusterset.
func (h *clusterEventHandler) getClusterSetName(cluster metav1.Object) (string, error) {
// skip cluster belongs to no clusterset
labels := cluster.GetLabels()
clusterSetName, ok := labels[clusterSetLabel]
if !ok {
return "", nil
}
_, err := h.clusterSetLister.Get(clusterSetName)
// skip if the clusterset does not exist
if errors.IsNotFound(err) {
return "", nil
}
func (h *clusterEventHandler) getClusterSetNames(cluster metav1.Object) ([]string, error) {
clusterSetNames := []string{}
clusterSets, err := clusterapiv1beta1.GetClusterSetsOfCluster(cluster.(*clusterapiv1.ManagedCluster), h.clusterSetLister)
if err != nil {
return "", err
return clusterSetNames, err
}

for _, cs := range clusterSets {
clusterSetNames = append(clusterSetNames, cs.Name)
}

return clusterSetName, nil
return clusterSetNames, nil
}
119 changes: 111 additions & 8 deletions pkg/controllers/scheduling/cluster_event_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"strings"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/cache"

clusterfake "open-cluster-management.io/api/client/cluster/clientset/versioned/fake"
clusterapiv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
testinghelpers "open-cluster-management.io/placement/pkg/helpers/testing"
)

Expand Down Expand Up @@ -55,6 +57,36 @@ func TestOnClusterChange(t *testing.T) {
"ns2/placement2",
},
},
{
name: "cluster blongs to multiple clusterset",
obj: testinghelpers.NewManagedCluster("cluster1").WithLabel("cloud", "Amazon").WithLabel(clusterSetLabel, "clusterset2").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSet("global").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{},
}).Build(),
testinghelpers.NewClusterSet("clusterset1").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"cloud": "Amazon",
},
},
}).Build(),
testinghelpers.NewClusterSet("clusterset2").Build(),
testinghelpers.NewClusterSetBinding("ns1", "global"),
testinghelpers.NewClusterSetBinding("ns2", "clusterset1"),
testinghelpers.NewClusterSetBinding("ns3", "clusterset2"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
testinghelpers.NewPlacement("ns2", "placement2").Build(),
testinghelpers.NewPlacement("ns3", "placement3").Build(),
},
queuedKeys: []string{
"ns1/placement1",
"ns2/placement2",
"ns3/placement3",
},
},
}

for _, c := range cases {
Expand Down Expand Up @@ -93,6 +125,42 @@ func TestOnClusterUpdate(t *testing.T) {
name: "cluster belongs to no clusterset",
newObj: testinghelpers.NewManagedCluster("cluster1").WithLabel("cloud", "Amazon").Build(),
oldObj: testinghelpers.NewManagedCluster("cluster1").WithLabel("cloud", "Google").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSet("clusterset1").Build(),
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
},
},
{
name: "cluster blongs to multiple clusterset",
newObj: testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, "clusterset2").WithLabel("cloud", "Google").Build(),
oldObj: testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, "clusterset2").WithLabel("cloud", "Amazon").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSet("global").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{},
}).Build(),
testinghelpers.NewClusterSet("clusterset1").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"cloud": "Amazon",
},
},
}).Build(),
testinghelpers.NewClusterSet("clusterset2").Build(),
testinghelpers.NewClusterSetBinding("ns1", "global"),
testinghelpers.NewClusterSetBinding("ns2", "clusterset1"),
testinghelpers.NewClusterSetBinding("ns3", "clusterset2"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
testinghelpers.NewPlacement("ns2", "placement2").Build(),
testinghelpers.NewPlacement("ns3", "placement3").Build(),
},
queuedKeys: []string{
"ns1/placement1",
"ns2/placement2",
"ns3/placement3",
},
},
{
name: "assign a cluster to a clusterset",
Expand All @@ -110,16 +178,33 @@ func TestOnClusterUpdate(t *testing.T) {
},
{
name: "remove cluster from a clusterset",
newObj: testinghelpers.NewManagedCluster("cluster1").WithLabel("cloud", "Amazon").Build(),
oldObj: testinghelpers.NewManagedCluster("cluster1").
WithLabel(clusterSetLabel, "clusterset1").WithLabel("cloud", "Amazon").Build(),
newObj: testinghelpers.NewManagedCluster("cluster1").Build(),
oldObj: testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, "clusterset2").WithLabel("cloud", "Amazon").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSet("clusterset1").Build(),
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
testinghelpers.NewClusterSet("global").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{},
}).Build(),
testinghelpers.NewClusterSet("clusterset1").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"cloud": "Amazon",
},
},
}).Build(),
testinghelpers.NewClusterSet("clusterset2").Build(),
testinghelpers.NewClusterSetBinding("ns1", "global"),
testinghelpers.NewClusterSetBinding("ns2", "clusterset1"),
testinghelpers.NewClusterSetBinding("ns3", "clusterset2"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
testinghelpers.NewPlacement("ns2", "placement2").Build(),
testinghelpers.NewPlacement("ns3", "placement3").Build(),
},
queuedKeys: []string{
"ns1/placement1",
"ns2/placement2",
"ns3/placement3",
},
},
{
Expand Down Expand Up @@ -195,14 +280,32 @@ func TestOnClusterDelete(t *testing.T) {
},
{
name: "cluster",
obj: testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, "clusterset1").Build(),
obj: testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, "clusterset2").WithLabel("cloud", "Amazon").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSet("clusterset1").Build(),
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
testinghelpers.NewClusterSet("global").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{},
}).Build(),
testinghelpers.NewClusterSet("clusterset1").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"cloud": "Amazon",
},
},
}).Build(),
testinghelpers.NewClusterSet("clusterset2").Build(),
testinghelpers.NewClusterSetBinding("ns1", "global"),
testinghelpers.NewClusterSetBinding("ns2", "clusterset1"),
testinghelpers.NewClusterSetBinding("ns3", "clusterset2"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
testinghelpers.NewPlacement("ns2", "placement2").Build(),
testinghelpers.NewPlacement("ns3", "placement3").Build(),
},
queuedKeys: []string{
"ns1/placement1",
"ns2/placement2",
"ns3/placement3",
},
},
{
Expand Down
42 changes: 40 additions & 2 deletions pkg/controllers/scheduling/clusterset_event_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"strings"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
cache "k8s.io/client-go/tools/cache"

clusterfake "open-cluster-management.io/api/client/cluster/clientset/versioned/fake"
clusterapiv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
testinghelpers "open-cluster-management.io/placement/pkg/helpers/testing"
)

Expand Down Expand Up @@ -75,7 +77,7 @@ func TestOnClusterSetAdd(t *testing.T) {
obj: "invalid object type",
},
{
name: "clusterset",
name: "clusterset selector type is LegacyClusterSetLabel",
obj: testinghelpers.NewClusterSet("clusterset1").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
Expand All @@ -85,6 +87,24 @@ func TestOnClusterSetAdd(t *testing.T) {
"ns1/placement1",
},
},
{
name: "clusterset selector type is LabelSelector",
obj: testinghelpers.NewClusterSet("clusterset1").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"cloud": "Amazon",
},
},
}).Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
},
queuedKeys: []string{
"ns1/placement1",
},
},
}

for _, c := range cases {
Expand Down Expand Up @@ -122,7 +142,7 @@ func TestOnClusterSetDelete(t *testing.T) {
obj: "invalid object type",
},
{
name: "clusterset",
name: "clusterset selector type is LegacyClusterSetLabel",
obj: testinghelpers.NewClusterSet("clusterset1").Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
Expand All @@ -132,6 +152,24 @@ func TestOnClusterSetDelete(t *testing.T) {
"ns1/placement1",
},
},
{
name: "clusterset selector type is LabelSelector",
obj: testinghelpers.NewClusterSet("clusterset1").WithClusterSelector(clusterapiv1beta1.ManagedClusterSelector{
SelectorType: clusterapiv1beta1.LabelSelector,
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"cloud": "Amazon",
},
},
}).Build(),
initObjs: []runtime.Object{
testinghelpers.NewClusterSetBinding("ns1", "clusterset1"),
testinghelpers.NewPlacement("ns1", "placement1").Build(),
},
queuedKeys: []string{
"ns1/placement1",
},
},
{
name: "tombstone",
obj: cache.DeletedFinalStateUnknown{
Expand Down
Loading

0 comments on commit 4f1e7c3

Please sign in to comment.