mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-02-14 18:09:57 +00:00
* Use basecontroller in sdk-go instead for better logging Signed-off-by: Jian Qiu <jqiu@redhat.com> * Rename to fakeSyncContext Signed-off-by: Jian Qiu <jqiu@redhat.com> --------- Signed-off-by: Jian Qiu <jqiu@redhat.com>
231 lines
7.9 KiB
Go
231 lines
7.9 KiB
Go
package managedclusterset
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/client-go/tools/cache"
|
|
"k8s.io/client-go/util/workqueue"
|
|
"k8s.io/klog/v2"
|
|
|
|
clientset "open-cluster-management.io/api/client/cluster/clientset/versioned"
|
|
clusterinformerv1 "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1"
|
|
clusterinformerv1beta2 "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1beta2"
|
|
clusterlisterv1 "open-cluster-management.io/api/client/cluster/listers/cluster/v1"
|
|
clusterlisterv1beta2 "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta2"
|
|
v1 "open-cluster-management.io/api/cluster/v1"
|
|
clusterv1beta2 "open-cluster-management.io/api/cluster/v1beta2"
|
|
clustersdkv1beta2 "open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta2"
|
|
"open-cluster-management.io/sdk-go/pkg/basecontroller/factory"
|
|
"open-cluster-management.io/sdk-go/pkg/patcher"
|
|
|
|
"open-cluster-management.io/ocm/pkg/common/queue"
|
|
)
|
|
|
|
const (
|
|
// TODO move these to api repos
|
|
ReasonClusterSelected = "ClustersSelected"
|
|
ReasonNoClusterMatchced = "NoClusterMatched"
|
|
)
|
|
|
|
// managedClusterSetController reconciles instances of ManagedClusterSet on the hub.
|
|
type managedClusterSetController struct {
|
|
patcher patcher.Patcher[*clusterv1beta2.ManagedClusterSet, clusterv1beta2.ManagedClusterSetSpec, clusterv1beta2.ManagedClusterSetStatus]
|
|
clusterLister clusterlisterv1.ManagedClusterLister
|
|
clusterSetLister clusterlisterv1beta2.ManagedClusterSetLister
|
|
queue workqueue.TypedRateLimitingInterface[string]
|
|
}
|
|
|
|
// NewManagedClusterSetController creates a new managed cluster set controller
|
|
func NewManagedClusterSetController(
|
|
clusterClient clientset.Interface,
|
|
clusterInformer clusterinformerv1.ManagedClusterInformer,
|
|
clusterSetInformer clusterinformerv1beta2.ManagedClusterSetInformer) factory.Controller {
|
|
|
|
controllerName := "managed-clusterset-controller"
|
|
syncCtx := factory.NewSyncContext(controllerName)
|
|
|
|
c := &managedClusterSetController{
|
|
patcher: patcher.NewPatcher[
|
|
*clusterv1beta2.ManagedClusterSet, clusterv1beta2.ManagedClusterSetSpec, clusterv1beta2.ManagedClusterSetStatus](
|
|
clusterClient.ClusterV1beta2().ManagedClusterSets()),
|
|
clusterLister: clusterInformer.Lister(),
|
|
clusterSetLister: clusterSetInformer.Lister(),
|
|
queue: syncCtx.Queue(),
|
|
}
|
|
|
|
_, err := clusterInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
AddFunc: func(obj interface{}) {
|
|
cluster, ok := obj.(*v1.ManagedCluster)
|
|
if !ok {
|
|
utilruntime.HandleError(fmt.Errorf("error to get object: %v", obj))
|
|
return
|
|
}
|
|
// enqueue all cluster related clustersets
|
|
c.enqueueClusterClusterSet(cluster)
|
|
},
|
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
// only need handle label update
|
|
oldCluster, ok := oldObj.(*v1.ManagedCluster)
|
|
if !ok {
|
|
utilruntime.HandleError(fmt.Errorf("error to get object: %v", oldObj))
|
|
return
|
|
}
|
|
newCluster, ok := newObj.(*v1.ManagedCluster)
|
|
if !ok {
|
|
utilruntime.HandleError(fmt.Errorf("error to get object: %v", newObj))
|
|
return
|
|
}
|
|
if reflect.DeepEqual(oldCluster.Labels, newCluster.Labels) {
|
|
return
|
|
}
|
|
c.enqueueUpdateClusterClusterSet(oldCluster, newCluster)
|
|
},
|
|
DeleteFunc: func(obj interface{}) {
|
|
switch t := obj.(type) {
|
|
case *v1.ManagedCluster:
|
|
// enqueue all cluster related clustersets
|
|
c.enqueueClusterClusterSet(t)
|
|
case cache.DeletedFinalStateUnknown:
|
|
cluster, ok := t.Obj.(*v1.ManagedCluster)
|
|
if !ok {
|
|
utilruntime.HandleError(fmt.Errorf("error to get object: %v", obj))
|
|
return
|
|
}
|
|
// enqueue all cluster related clustersets
|
|
c.enqueueClusterClusterSet(cluster)
|
|
default:
|
|
utilruntime.HandleError(fmt.Errorf("error decoding object, invalid type"))
|
|
return
|
|
}
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
utilruntime.HandleError(err)
|
|
}
|
|
|
|
return factory.New().
|
|
WithSyncContext(syncCtx).
|
|
WithInformersQueueKeysFunc(queue.QueueKeyByMetaName, clusterSetInformer.Informer()).
|
|
WithBareInformers(clusterInformer.Informer()).
|
|
WithSync(c.sync).
|
|
ToController("ManagedClusterSetController")
|
|
}
|
|
|
|
func (c *managedClusterSetController) sync(ctx context.Context, syncCtx factory.SyncContext, clusterSetName string) error {
|
|
logger := klog.FromContext(ctx)
|
|
if len(clusterSetName) == 0 {
|
|
return nil
|
|
}
|
|
logger.WithValues("clusterSetName", clusterSetName)
|
|
ctx = klog.NewContext(ctx, logger)
|
|
clusterSet, err := c.clusterSetLister.Get(clusterSetName)
|
|
if errors.IsNotFound(err) {
|
|
// cluster set not found, could have been deleted, do nothing.
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// no work to do if the cluster set is deleted
|
|
if !clusterSet.DeletionTimestamp.IsZero() {
|
|
return nil
|
|
}
|
|
|
|
if err := c.syncClusterSet(ctx, clusterSet); err != nil {
|
|
return fmt.Errorf("failed to sync ManagedClusterSet %q: %w", clusterSet.Name, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// syncClusterSet syncs a particular cluster set
|
|
func (c *managedClusterSetController) syncClusterSet(ctx context.Context, originalClusterSet *clusterv1beta2.ManagedClusterSet) error {
|
|
clusterSet := originalClusterSet.DeepCopy()
|
|
clusters, err := clustersdkv1beta2.GetClustersFromClusterSet(clusterSet, c.clusterLister)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count := len(clusters)
|
|
// update clusterset status
|
|
emptyCondition := metav1.Condition{
|
|
Type: clusterv1beta2.ManagedClusterSetConditionEmpty,
|
|
}
|
|
if count == 0 {
|
|
emptyCondition.Status = metav1.ConditionTrue
|
|
emptyCondition.Reason = ReasonNoClusterMatchced
|
|
emptyCondition.Message = "No ManagedCluster selected"
|
|
} else {
|
|
emptyCondition.Status = metav1.ConditionFalse
|
|
emptyCondition.Reason = ReasonClusterSelected
|
|
emptyCondition.Message = fmt.Sprintf("%d ManagedClusters selected", count)
|
|
}
|
|
meta.SetStatusCondition(&clusterSet.Status.Conditions, emptyCondition)
|
|
|
|
_, err = c.patcher.PatchStatus(ctx, clusterSet, clusterSet.Status, originalClusterSet.Status)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update status of ManagedClusterSet %q: %w", clusterSet.Name, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// enqueueClusterClusterSet enqueue a cluster related clusterset
|
|
func (c *managedClusterSetController) enqueueClusterClusterSet(cluster *v1.ManagedCluster) {
|
|
clusterSets, err := clustersdkv1beta2.GetClusterSetsOfCluster(cluster, c.clusterSetLister)
|
|
if err != nil {
|
|
utilruntime.HandleError(fmt.Errorf("error to get GetClusterSetsOfCluster. Error %v", err))
|
|
return
|
|
}
|
|
for _, clusterSet := range clusterSets {
|
|
c.queue.Add(clusterSet.Name)
|
|
}
|
|
}
|
|
|
|
// enqueueUpdateClusterClusterSet get the oldCluster related clustersets and newCluster related clustersets,
|
|
// then enqueue the diff clustersets(added clustersets and removed clustersets)
|
|
func (c *managedClusterSetController) enqueueUpdateClusterClusterSet(oldCluster, newCluster *v1.ManagedCluster) {
|
|
oldClusterSets, err := clustersdkv1beta2.GetClusterSetsOfCluster(oldCluster, c.clusterSetLister)
|
|
if err != nil {
|
|
utilruntime.HandleError(fmt.Errorf("error to get GetClusterSetsOfCluster. Error %v", err))
|
|
return
|
|
}
|
|
newClusterSets, err := clustersdkv1beta2.GetClusterSetsOfCluster(newCluster, c.clusterSetLister)
|
|
if err != nil {
|
|
utilruntime.HandleError(fmt.Errorf("error to get GetClusterSetsOfCluster. Error %v", err))
|
|
return
|
|
}
|
|
diffClusterSets := getDiffClusterSetsNames(oldClusterSets, newClusterSets)
|
|
|
|
for diffSet := range diffClusterSets {
|
|
c.queue.Add(diffSet)
|
|
}
|
|
}
|
|
|
|
// getDiffClusterSetsNames return the diff clustersets names
|
|
func getDiffClusterSetsNames(oldSets, newSets []*clusterv1beta2.ManagedClusterSet) sets.Set[string] {
|
|
oldSetsMap := sets.New[string]()
|
|
newSetsMap := sets.New[string]()
|
|
|
|
for _, oldSet := range oldSets {
|
|
oldSetsMap.Insert(oldSet.Name)
|
|
}
|
|
|
|
for _, newSet := range newSets {
|
|
newSetsMap.Insert(newSet.Name)
|
|
}
|
|
|
|
diffOldSets := oldSetsMap.Difference(newSetsMap)
|
|
diffNewSets := newSetsMap.Difference(oldSetsMap)
|
|
|
|
return diffOldSets.Union(diffNewSets)
|
|
}
|