diff --git a/pkg/apis/work/v1alpha2/binding_types.go b/pkg/apis/work/v1alpha2/binding_types.go index ca292ab4a82c..9d0f12de39ca 100644 --- a/pkg/apis/work/v1alpha2/binding_types.go +++ b/pkg/apis/work/v1alpha2/binding_types.go @@ -139,6 +139,10 @@ type AggregatedStatusItem struct { const ( // Scheduled represents the condition that the ResourceBinding or ClusterResourceBinding has been scheduled. Scheduled string = "Scheduled" + + // FullyApplied represents the condition that the resource referencing by ResourceBinding or ClusterResourceBinding + // has been applied to all scheduled clusters. + FullyApplied string = "FullyApplied" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/controllers/binding/binding_controller.go b/pkg/controllers/binding/binding_controller.go index e9554c7c1506..57756e51472e 100644 --- a/pkg/controllers/binding/binding_controller.go +++ b/pkg/controllers/binding/binding_controller.go @@ -8,6 +8,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/dynamic" @@ -141,6 +142,18 @@ func (c *ResourceBindingController) syncBinding(binding *workv1alpha2.ResourceBi if len(errs) > 0 { return controllerruntime.Result{Requeue: true}, errors.NewAggregate(errs) } + + fullyAppliedCondition := meta.FindStatusCondition(binding.Status.Conditions, workv1alpha2.FullyApplied) + if fullyAppliedCondition == nil { + if len(binding.Spec.Clusters) == len(binding.Status.AggregatedStatus) && areWorksFullyApplied(binding.Status.AggregatedStatus) { + err := c.updateFullyAppliedCondition(binding) + if err != nil { + klog.Errorf("Failed to update FullyApplied status for given resourceBinding(%s/%s). Error: %v.", binding.Namespace, binding.Name, err) + return controllerruntime.Result{Requeue: true}, err + } + } + } + return controllerruntime.Result{}, nil } @@ -252,3 +265,18 @@ func (c *ResourceBindingController) newReplicaSchedulingPolicyFunc() handler.Map return requests } } + +// updateFullyAppliedCondition update the FullyApplied condition for the given ResourceBinding +func (c *ResourceBindingController) updateFullyAppliedCondition(binding *workv1alpha2.ResourceBinding) error { + newBindingFullyAppliedCondition := metav1.Condition{ + Type: workv1alpha2.FullyApplied, + Status: metav1.ConditionTrue, + Reason: FullyAppliedSuccessReason, + Message: FullyAppliedSuccessMessage, + } + + meta.SetStatusCondition(&binding.Status.Conditions, newBindingFullyAppliedCondition) + err := c.Client.Status().Update(context.TODO(), binding) + + return err +} diff --git a/pkg/controllers/binding/cluster_resource_binding_controller.go b/pkg/controllers/binding/cluster_resource_binding_controller.go index 963fd6afde55..fb63a642cefd 100644 --- a/pkg/controllers/binding/cluster_resource_binding_controller.go +++ b/pkg/controllers/binding/cluster_resource_binding_controller.go @@ -8,6 +8,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/dynamic" @@ -137,6 +138,17 @@ func (c *ClusterResourceBindingController) syncBinding(binding *workv1alpha2.Clu return controllerruntime.Result{Requeue: true}, errors.NewAggregate(errs) } + fullyAppliedCondition := meta.FindStatusCondition(binding.Status.Conditions, workv1alpha2.FullyApplied) + if fullyAppliedCondition == nil { + if len(binding.Spec.Clusters) == len(binding.Status.AggregatedStatus) && areWorksFullyApplied(binding.Status.AggregatedStatus) { + err := c.updateFullyAppliedCondition(binding) + if err != nil { + klog.Errorf("Failed to update FullyApplied status for given clusterResourceBinding(%s), Error: %v", binding.Name, err) + return controllerruntime.Result{Requeue: true}, err + } + } + } + return controllerruntime.Result{}, nil } @@ -244,3 +256,18 @@ func (c *ClusterResourceBindingController) newReplicaSchedulingPolicyFunc() hand return requests } } + +// updateFullyAppliedCondition update the FullyApplied condition for the given ClusterResourceBinding +func (c *ClusterResourceBindingController) updateFullyAppliedCondition(binding *workv1alpha2.ClusterResourceBinding) error { + newBindingFullyAppliedCondition := metav1.Condition{ + Type: workv1alpha2.FullyApplied, + Status: metav1.ConditionTrue, + Reason: FullyAppliedSuccessReason, + Message: FullyAppliedSuccessMessage, + } + + meta.SetStatusCondition(&binding.Status.Conditions, newBindingFullyAppliedCondition) + err := c.Client.Status().Update(context.TODO(), binding) + + return err +} diff --git a/pkg/controllers/binding/common.go b/pkg/controllers/binding/common.go index 6af7cae14940..e9866ddda705 100644 --- a/pkg/controllers/binding/common.go +++ b/pkg/controllers/binding/common.go @@ -24,6 +24,12 @@ import ( "github.com/karmada-io/karmada/pkg/util/overridemanager" ) +const ( + FullyAppliedSuccessReason = "FullyAppliedSuccess" + + FullyAppliedSuccessMessage = "All works have been successfully applied" +) + var workPredicateFn = builder.WithPredicates(predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { return false @@ -323,3 +329,13 @@ func calculateReplicas(c client.Client, policy *policyv1alpha1.ReplicaScheduling return desireReplicaInfos, nil } + +// areWorksFullyApplied checks if all works are applied for a Binding +func areWorksFullyApplied(aggregatedStatuses []workv1alpha2.AggregatedStatusItem) bool { + for _, aggregatedSatusItem := range aggregatedStatuses { + if !aggregatedSatusItem.Applied { + return false + } + } + return true +}