Fix resource binding get reconciled multiple times
Signed-off-by: pigletfly <wangbing.adam@gmail.com>
This commit is contained in:
parent
f3b1142599
commit
d8470c2c52
|
@ -3,11 +3,14 @@ package binding
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/errors"
|
"k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
@ -21,6 +24,7 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||||
|
|
||||||
|
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
||||||
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
|
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
|
||||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||||
|
@ -30,6 +34,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/util/informermanager"
|
"github.com/karmada-io/karmada/pkg/util/informermanager"
|
||||||
"github.com/karmada-io/karmada/pkg/util/overridemanager"
|
"github.com/karmada-io/karmada/pkg/util/overridemanager"
|
||||||
"github.com/karmada-io/karmada/pkg/util/ratelimiter"
|
"github.com/karmada-io/karmada/pkg/util/ratelimiter"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/restmapper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ControllerName is the controller name that will be used when reporting events.
|
// ControllerName is the controller name that will be used when reporting events.
|
||||||
|
@ -145,9 +150,52 @@ func (c *ResourceBindingController) syncBinding(binding *workv1alpha2.ResourceBi
|
||||||
return controllerruntime.Result{Requeue: true}, errors.NewAggregate(errs)
|
return controllerruntime.Result{Requeue: true}, errors.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = c.updateResourceStatus(binding)
|
||||||
|
if err != nil {
|
||||||
|
return controllerruntime.Result{Requeue: true}, err
|
||||||
|
}
|
||||||
|
|
||||||
return controllerruntime.Result{}, nil
|
return controllerruntime.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateResourceStatus will try to calculate the summary status and update to original object
|
||||||
|
// that the ResourceBinding refer to.
|
||||||
|
func (c *ResourceBindingController) updateResourceStatus(binding *workv1alpha2.ResourceBinding) error {
|
||||||
|
resource := binding.Spec.Resource
|
||||||
|
gvr, err := restmapper.GetGroupVersionResource(
|
||||||
|
c.RESTMapper, schema.FromAPIVersionAndKind(resource.APIVersion, resource.Kind),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to get GVR from GVK %s %s. Error: %v", resource.APIVersion, resource.Kind, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
obj, err := helper.FetchWorkload(c.DynamicClient, c.InformerManager, c.RESTMapper, resource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to get resource(%s/%s/%s), Error: %v", resource.Kind, resource.Namespace, resource.Name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.ResourceInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationAggregateStatus) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newObj, err := c.ResourceInterpreter.AggregateStatus(obj, binding.Status.AggregatedStatus)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("AggregateStatus for resource(%s/%s/%s) failed: %v", resource.Kind, resource.Namespace, resource.Name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reflect.DeepEqual(obj, newObj) {
|
||||||
|
klog.V(3).Infof("ignore update resource(%s/%s/%s) status as up to date", resource.Kind, resource.Namespace, resource.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = c.DynamicClient.Resource(gvr).Namespace(resource.Namespace).UpdateStatus(context.TODO(), newObj, metav1.UpdateOptions{}); err != nil {
|
||||||
|
klog.Errorf("Failed to update resource(%s/%s/%s), Error: %v", resource.Kind, resource.Namespace, resource.Name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
klog.V(3).Infof("update resource status successfully for resource(%s/%s/%s)", resource.Kind, resource.Namespace, resource.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetupWithManager creates a controller and register to controller manager.
|
// SetupWithManager creates a controller and register to controller manager.
|
||||||
func (c *ResourceBindingController) SetupWithManager(mgr controllerruntime.Manager) error {
|
func (c *ResourceBindingController) SetupWithManager(mgr controllerruntime.Manager) error {
|
||||||
workFn := handler.MapFunc(
|
workFn := handler.MapFunc(
|
||||||
|
|
|
@ -3,7 +3,6 @@ package detector
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -66,11 +65,6 @@ type ResourceDetector struct {
|
||||||
clusterPolicyReconcileWorker util.AsyncWorker
|
clusterPolicyReconcileWorker util.AsyncWorker
|
||||||
clusterPropagationPolicyLister cache.GenericLister
|
clusterPropagationPolicyLister cache.GenericLister
|
||||||
|
|
||||||
// bindingReconcileWorker maintains a rate limited queue which used to store ResourceBinding's key and
|
|
||||||
// a reconcile function to consume the items in queue.
|
|
||||||
bindingReconcileWorker util.AsyncWorker
|
|
||||||
resourceBindingLister cache.GenericLister
|
|
||||||
|
|
||||||
RESTMapper meta.RESTMapper
|
RESTMapper meta.RESTMapper
|
||||||
|
|
||||||
// waitingObjects tracks of objects which haven't be propagated yet as lack of appropriate policies.
|
// waitingObjects tracks of objects which haven't be propagated yet as lack of appropriate policies.
|
||||||
|
@ -130,39 +124,12 @@ func (d *ResourceDetector) Start(ctx context.Context) error {
|
||||||
d.InformerManager.ForResource(clusterPropagationPolicyGVR, clusterPolicyHandler)
|
d.InformerManager.ForResource(clusterPropagationPolicyGVR, clusterPolicyHandler)
|
||||||
d.clusterPropagationPolicyLister = d.InformerManager.Lister(clusterPropagationPolicyGVR)
|
d.clusterPropagationPolicyLister = d.InformerManager.Lister(clusterPropagationPolicyGVR)
|
||||||
|
|
||||||
// setup binding reconcile worker
|
|
||||||
bindingWorkerOptions := util.Options{
|
|
||||||
Name: "resourceBinding reconciler",
|
|
||||||
KeyFunc: ClusterWideKeyFunc,
|
|
||||||
ReconcileFunc: d.ReconcileResourceBinding,
|
|
||||||
}
|
|
||||||
d.bindingReconcileWorker = util.NewAsyncWorker(bindingWorkerOptions)
|
|
||||||
d.bindingReconcileWorker.Run(d.ConcurrentResourceBindingSyncs, d.stopCh)
|
|
||||||
|
|
||||||
// watch and enqueue ResourceBinding changes.
|
|
||||||
resourceBindingGVR := schema.GroupVersionResource{
|
|
||||||
Group: workv1alpha2.GroupVersion.Group,
|
|
||||||
Version: workv1alpha2.GroupVersion.Version,
|
|
||||||
Resource: "resourcebindings",
|
|
||||||
}
|
|
||||||
bindingHandler := informermanager.NewHandlerOnEvents(d.OnResourceBindingAdd, d.OnResourceBindingUpdate, nil)
|
|
||||||
d.InformerManager.ForResource(resourceBindingGVR, bindingHandler)
|
|
||||||
d.resourceBindingLister = d.InformerManager.Lister(resourceBindingGVR)
|
|
||||||
|
|
||||||
// watch and enqueue ClusterResourceBinding changes.
|
|
||||||
clusterResourceBindingGVR := schema.GroupVersionResource{
|
|
||||||
Group: workv1alpha2.GroupVersion.Group,
|
|
||||||
Version: workv1alpha2.GroupVersion.Version,
|
|
||||||
Resource: "clusterresourcebindings",
|
|
||||||
}
|
|
||||||
detectorWorkerOptions := util.Options{
|
detectorWorkerOptions := util.Options{
|
||||||
Name: "resource detector",
|
Name: "resource detector",
|
||||||
KeyFunc: ClusterWideKeyFunc,
|
KeyFunc: ClusterWideKeyFunc,
|
||||||
ReconcileFunc: d.Reconcile,
|
ReconcileFunc: d.Reconcile,
|
||||||
RatelimiterOptions: d.RatelimiterOptions,
|
RatelimiterOptions: d.RatelimiterOptions,
|
||||||
}
|
}
|
||||||
clusterBindingHandler := informermanager.NewHandlerOnEvents(d.OnClusterResourceBindingAdd, d.OnClusterResourceBindingUpdate, nil)
|
|
||||||
d.InformerManager.ForResource(clusterResourceBindingGVR, clusterBindingHandler)
|
|
||||||
|
|
||||||
d.EventHandler = informermanager.NewFilteringHandlerOnAllEvents(d.EventFilter, d.OnAdd, d.OnUpdate, d.OnDelete)
|
d.EventHandler = informermanager.NewFilteringHandlerOnAllEvents(d.EventFilter, d.OnAdd, d.OnUpdate, d.OnDelete)
|
||||||
d.Processor = util.NewAsyncWorker(detectorWorkerOptions)
|
d.Processor = util.NewAsyncWorker(detectorWorkerOptions)
|
||||||
|
@ -1049,89 +1016,6 @@ func (d *ResourceDetector) HandleClusterPropagationPolicyCreation(policy *policy
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnResourceBindingAdd handles object add event.
|
|
||||||
func (d *ResourceDetector) OnResourceBindingAdd(obj interface{}) {
|
|
||||||
key, err := ClusterWideKeyFunc(obj)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
d.bindingReconcileWorker.Add(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnResourceBindingUpdate handles object update event and push the object to queue.
|
|
||||||
func (d *ResourceDetector) OnResourceBindingUpdate(_, newObj interface{}) {
|
|
||||||
d.OnResourceBindingAdd(newObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReconcileResourceBinding handles ResourceBinding object changes.
|
|
||||||
// For each ResourceBinding changes, we will try to calculate the summary status and update to original object
|
|
||||||
// that the ResourceBinding refer to.
|
|
||||||
func (d *ResourceDetector) ReconcileResourceBinding(key util.QueueKey) error {
|
|
||||||
ckey, ok := key.(keys.ClusterWideKey)
|
|
||||||
if !ok { // should not happen
|
|
||||||
klog.Error("Found invalid key when reconciling resource binding.")
|
|
||||||
return fmt.Errorf("invalid key")
|
|
||||||
}
|
|
||||||
|
|
||||||
unstructuredObj, err := d.resourceBindingLister.Get(ckey.NamespaceKey())
|
|
||||||
if err != nil {
|
|
||||||
if apierrors.IsNotFound(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
binding, err := helper.ConvertToResourceBinding(unstructuredObj.(*unstructured.Unstructured))
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Failed to convert ResourceBinding(%s) from unstructured object: %v", ckey.NamespaceKey(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.Infof("Reconciling resource binding(%s/%s)", binding.Namespace, binding.Name)
|
|
||||||
resource := binding.Spec.Resource
|
|
||||||
gvr, err := restmapper.GetGroupVersionResource(
|
|
||||||
d.RESTMapper, schema.FromAPIVersionAndKind(resource.APIVersion, resource.Kind),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Failed to get GVR from GVK %s %s. Error: %v", resource.APIVersion, resource.Kind, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
obj, err := helper.FetchWorkload(d.DynamicClient, d.InformerManager, d.RESTMapper, resource)
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Failed to get resource(%s/%s/%s), Error: %v", resource.Kind, resource.Namespace, resource.Name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !d.ResourceInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationAggregateStatus) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
newObj, err := d.ResourceInterpreter.AggregateStatus(obj, binding.Status.AggregatedStatus)
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("AggregateStatus for resource(%s/%s/%s) failed: %v", resource.Kind, resource.Namespace, resource.Name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if reflect.DeepEqual(obj, newObj) {
|
|
||||||
klog.V(3).Infof("ignore update resource(%s/%s/%s) status as up to date", resource.Kind, resource.Namespace, resource.Name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = d.DynamicClient.Resource(gvr).Namespace(resource.Namespace).UpdateStatus(context.TODO(), newObj, metav1.UpdateOptions{}); err != nil {
|
|
||||||
klog.Errorf("Failed to update resource(%s/%s/%s), Error: %v", resource.Kind, resource.Namespace, resource.Name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnClusterResourceBindingAdd handles object add event.
|
|
||||||
func (d *ResourceDetector) OnClusterResourceBindingAdd(obj interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnClusterResourceBindingUpdate handles object update event and push the object to queue.
|
|
||||||
func (d *ResourceDetector) OnClusterResourceBindingUpdate(oldObj, newObj interface{}) {
|
|
||||||
d.OnClusterResourceBindingAdd(newObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanupLabels removes labels from object referencing by objRef.
|
// CleanupLabels removes labels from object referencing by objRef.
|
||||||
func (d *ResourceDetector) CleanupLabels(objRef workv1alpha2.ObjectReference, labels ...string) error {
|
func (d *ResourceDetector) CleanupLabels(objRef workv1alpha2.ObjectReference, labels ...string) error {
|
||||||
workload, err := helper.FetchWorkload(d.DynamicClient, d.InformerManager, d.RESTMapper, objRef)
|
workload, err := helper.FetchWorkload(d.DynamicClient, d.InformerManager, d.RESTMapper, objRef)
|
||||||
|
|
Loading…
Reference in New Issue