Files
Morven CaoandClaude d1221c4a79
Scorecard supply-chain security / Scorecard analysis (push) Failing after 25s
Post / images (amd64, placement) (push) Failing after 47s
Post / images (amd64, registration) (push) Failing after 44s
Post / images (amd64, registration-operator) (push) Failing after 44s
Post / images (amd64, work) (push) Failing after 43s
Post / images (arm64, addon-manager) (push) Failing after 42s
Post / images (arm64, placement) (push) Failing after 41s
Post / images (arm64, registration) (push) Failing after 43s
Post / images (arm64, registration-operator) (push) Failing after 41s
Post / images (arm64, work) (push) Failing after 41s
Post / images (amd64, addon-manager) (push) Failing after 7m45s
Post / image manifest (addon-manager) (push) Has been skipped
Post / image manifest (placement) (push) Has been skipped
Post / image manifest (registration) (push) Has been skipped
Post / image manifest (registration-operator) (push) Has been skipped
Post / image manifest (work) (push) Has been skipped
Post / trigger clusteradm e2e (push) Has been skipped
Post / coverage (push) Failing after 38m55s
Close stale issues and PRs / stale (push) Successful in 50s
🌱 sync clusterprofile based on managedclusterset and managedclustersetbinding (#1351)
* sync clusterprofile based on managedclusterset and managedclustersetbinding

Co-authored-by: Claude <claude@anthropic.com>

Signed-off-by: Morven Cao <lcao@redhat.com>

* Refactor ClusterProfile controller into two separate controllers.

Signed-off-by: Morven Cao <lcao@redhat.com>

* address comments.

Signed-off-by: Morven Cao <lcao@redhat.com>

* fix lint issues.

Signed-off-by: Morven Cao <lcao@redhat.com>

* address comments.

Signed-off-by: Morven Cao <lcao@redhat.com>

* address comments.

Signed-off-by: Morven Cao <lcao@redhat.com>

---------

Signed-off-by: Morven Cao <lcao@redhat.com>
2026-01-28 15:37:46 +00:00

192 lines
6.0 KiB
Go

package managedclustersetbinding
import (
"context"
"fmt"
"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/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
clientset "open-cluster-management.io/api/client/cluster/clientset/versioned"
clusterinformerv1beta2 "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1beta2"
clusterlisterv1beta2 "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta2"
clusterv1beta2 "open-cluster-management.io/api/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 (
// ByClusterSetIndex is the indexer name for ManagedClusterSetBinding by ClusterSet
ByClusterSetIndex = "by-clusterset"
)
const byClusterSet = ByClusterSetIndex // Use exported constant internally
// managedClusterSetController reconciles instances of ManagedClusterSet on the hub.
type managedClusterSetBindingController struct {
clusterClient clientset.Interface
clusterSetBindingLister clusterlisterv1beta2.ManagedClusterSetBindingLister
clusterSetLister clusterlisterv1beta2.ManagedClusterSetLister
clusterSetBindingIndexers cache.Indexer
queue workqueue.TypedRateLimitingInterface[string]
}
func NewManagedClusterSetBindingController(
clusterClient clientset.Interface,
clusterSetInformer clusterinformerv1beta2.ManagedClusterSetInformer,
clusterSetBindingInformer clusterinformerv1beta2.ManagedClusterSetBindingInformer) factory.Controller {
controllerName := "managed-clusterset-binding-controller"
syncCtx := factory.NewSyncContext(controllerName)
err := clusterSetBindingInformer.Informer().AddIndexers(cache.Indexers{
byClusterSet: indexByClusterset,
})
if err != nil {
utilruntime.HandleError(err)
}
c := &managedClusterSetBindingController{
clusterClient: clusterClient,
clusterSetLister: clusterSetInformer.Lister(),
clusterSetBindingLister: clusterSetBindingInformer.Lister(),
clusterSetBindingIndexers: clusterSetBindingInformer.Informer().GetIndexer(),
queue: syncCtx.Queue(),
}
_, err = clusterSetInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: c.enqueueBindingsByClusterSet,
UpdateFunc: func(oldObj, newObj interface{}) {
c.enqueueBindingsByClusterSet(newObj)
},
DeleteFunc: c.enqueueBindingsByClusterSet,
},
)
if err != nil {
utilruntime.HandleError(err)
}
return factory.New().
WithSyncContext(syncCtx).
WithInformersQueueKeysFunc(queue.QueueKeyByMetaNamespaceName, clusterSetBindingInformer.Informer()).
WithBareInformers(clusterSetInformer.Informer()).
WithSync(c.sync).
ToController("ManagedClusterSetController")
}
func indexByClusterset(obj interface{}) ([]string, error) {
binding, ok := obj.(*clusterv1beta2.ManagedClusterSetBinding)
if !ok {
return []string{}, fmt.Errorf("obj is supposed to be a ManagedClusterSetBinding, but is %T", obj)
}
return []string{binding.Spec.ClusterSet}, nil
}
func (c *managedClusterSetBindingController) getClusterBindingsByClusterSet(name string) ([]*clusterv1beta2.ManagedClusterSetBinding, error) {
objs, err := c.clusterSetBindingIndexers.ByIndex(byClusterSet, name)
if err != nil {
return nil, err
}
bindings := make([]*clusterv1beta2.ManagedClusterSetBinding, len(objs))
for _, obj := range objs {
binding := obj.(*clusterv1beta2.ManagedClusterSetBinding)
bindings = append(bindings, binding)
}
return bindings, nil
}
func (c *managedClusterSetBindingController) enqueueBindingsByClusterSet(obj interface{}) {
name, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err != nil {
utilruntime.HandleError(fmt.Errorf("error to get accessor of object: %v", obj))
return
}
bindings, err := c.getClusterBindingsByClusterSet(name)
if err != nil {
utilruntime.HandleError(fmt.Errorf("error to get bindings of object: %v", obj))
return
}
for _, binding := range bindings {
// TODO(qiujian16) it is weird that index can return nil. Needs more investigation.
if binding == nil {
continue
}
key, _ := cache.MetaNamespaceKeyFunc(binding)
c.queue.Add(key)
}
}
func (c *managedClusterSetBindingController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error {
logger := klog.FromContext(ctx).WithValues("key", key)
if len(key) == 0 {
return nil
}
logger.V(4).Info("Reconciling ManagedClusterSetBinding")
bindingNamespace, bindingName, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return err
}
patcher := patcher.NewPatcher[
*clusterv1beta2.ManagedClusterSetBinding, clusterv1beta2.ManagedClusterSetBindingSpec, clusterv1beta2.ManagedClusterSetBindingStatus](
c.clusterClient.ClusterV1beta2().ManagedClusterSetBindings(bindingNamespace))
if len(bindingNamespace) == 0 {
return nil
}
binding, err := c.clusterSetBindingLister.ManagedClusterSetBindings(bindingNamespace).Get(bindingName)
switch {
case errors.IsNotFound(err):
return nil
case err != nil:
return err
}
_, err = c.clusterSetLister.Get(binding.Spec.ClusterSet)
bindingCopy := binding.DeepCopy()
switch {
case errors.IsNotFound(err):
meta.SetStatusCondition(&bindingCopy.Status.Conditions, metav1.Condition{
Type: clusterv1beta2.ClusterSetBindingBoundType,
Status: metav1.ConditionFalse,
Reason: "ClusterSetNotFound",
})
if _, err := patcher.PatchStatus(ctx, bindingCopy, bindingCopy.Status, binding.Status); err != nil {
return err
}
return nil
case err != nil:
return err
}
meta.SetStatusCondition(&bindingCopy.Status.Conditions, metav1.Condition{
Type: clusterv1beta2.ClusterSetBindingBoundType,
Status: metav1.ConditionTrue,
Reason: "ClusterSetBound",
})
if _, err := patcher.PatchStatus(ctx, bindingCopy, bindingCopy.Status, binding.Status); err != nil {
return err
}
return nil
}