169 lines
6.1 KiB
Go
169 lines
6.1 KiB
Go
package helper
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/klog/v2"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
|
|
"github.com/karmada-io/karmada/pkg/util"
|
|
"github.com/karmada-io/karmada/pkg/util/names"
|
|
)
|
|
|
|
// AggregateResourceBindingWorkStatus will collect all work statuses with current ResourceBinding objects,
|
|
// then aggregate status info to current ResourceBinding status.
|
|
func AggregateResourceBindingWorkStatus(c client.Client, binding *workv1alpha1.ResourceBinding, workload *unstructured.Unstructured) error {
|
|
aggregatedStatuses, err := assembleWorkStatus(c, labels.SelectorFromSet(labels.Set{
|
|
util.ResourceBindingNamespaceLabel: binding.Namespace,
|
|
util.ResourceBindingNameLabel: binding.Name,
|
|
}), workload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if reflect.DeepEqual(binding.Status.AggregatedStatus, aggregatedStatuses) {
|
|
klog.V(4).Infof("New aggregatedStatuses are equal with old resourceBinding(%s/%s) AggregatedStatus, no update required.",
|
|
binding.Namespace, binding.Name)
|
|
return nil
|
|
}
|
|
|
|
binding.Status.AggregatedStatus = aggregatedStatuses
|
|
err = c.Status().Update(context.TODO(), binding)
|
|
if err != nil {
|
|
klog.Errorf("Failed update resourceBinding(%s/%s). Error: %v.", binding.Namespace, binding.Name, err)
|
|
return err
|
|
}
|
|
klog.Infof("Update resourceBinding(%s/%s) with AggregatedStatus successfully.", binding.Namespace, binding.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// AggregateClusterResourceBindingWorkStatus will collect all work statuses with current ClusterResourceBinding objects,
|
|
// then aggregate status info to current ClusterResourceBinding status.
|
|
func AggregateClusterResourceBindingWorkStatus(c client.Client, binding *workv1alpha1.ClusterResourceBinding, workload *unstructured.Unstructured) error {
|
|
aggregatedStatuses, err := assembleWorkStatus(c, labels.SelectorFromSet(labels.Set{
|
|
util.ClusterResourceBindingLabel: binding.Name,
|
|
}), workload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if reflect.DeepEqual(binding.Status.AggregatedStatus, aggregatedStatuses) {
|
|
klog.Infof("New aggregatedStatuses are equal with old clusterResourceBinding(%s) AggregatedStatus, no update required.", binding.Name)
|
|
return nil
|
|
}
|
|
|
|
binding.Status.AggregatedStatus = aggregatedStatuses
|
|
err = c.Status().Update(context.TODO(), binding)
|
|
if err != nil {
|
|
klog.Errorf("Failed update clusterResourceBinding(%s). Error: %v.", binding.Name, err)
|
|
return err
|
|
}
|
|
klog.Infof("Update clusterResourceBinding(%s) with AggregatedStatus successfully.", binding.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// assemble workStatuses from workList which list by selector and match with workload.
|
|
func assembleWorkStatus(c client.Client, selector labels.Selector, workload *unstructured.Unstructured) ([]workv1alpha1.AggregatedStatusItem, error) {
|
|
workList := &workv1alpha1.WorkList{}
|
|
if err := c.List(context.TODO(), workList, &client.ListOptions{LabelSelector: selector}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
statuses := make([]workv1alpha1.AggregatedStatusItem, 0)
|
|
for _, work := range workList.Items {
|
|
identifierIndex, err := GetManifestIndex(work.Spec.Workload.Manifests, workload)
|
|
if err != nil {
|
|
klog.Errorf("Failed to get manifestIndex of workload in work.Spec.Workload.Manifests. Error: %v.", err)
|
|
return nil, err
|
|
}
|
|
|
|
for _, manifestStatus := range work.Status.ManifestStatuses {
|
|
equal, err := equalIdentifier(&manifestStatus.Identifier, identifierIndex, workload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if equal {
|
|
clusterName, err := names.GetClusterName(work.Namespace)
|
|
if err != nil {
|
|
klog.Errorf("Failed to get clusterName from work namespace %s. Error: %v.", work.Namespace, err)
|
|
return nil, err
|
|
}
|
|
|
|
aggregatedStatus := workv1alpha1.AggregatedStatusItem{
|
|
ClusterName: clusterName,
|
|
Status: manifestStatus.Status,
|
|
}
|
|
statuses = append(statuses, aggregatedStatus)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return statuses, nil
|
|
}
|
|
|
|
// GetManifestIndex get the index of clusterObj in manifest list, if not exist return -1.
|
|
func GetManifestIndex(manifests []workv1alpha1.Manifest, clusterObj *unstructured.Unstructured) (int, error) {
|
|
for index, rawManifest := range manifests {
|
|
manifest := &unstructured.Unstructured{}
|
|
if err := manifest.UnmarshalJSON(rawManifest.Raw); err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
if manifest.GetAPIVersion() == clusterObj.GetAPIVersion() &&
|
|
manifest.GetKind() == clusterObj.GetKind() &&
|
|
manifest.GetNamespace() == clusterObj.GetNamespace() &&
|
|
manifest.GetName() == clusterObj.GetName() {
|
|
return index, nil
|
|
}
|
|
}
|
|
|
|
return -1, fmt.Errorf("no such manifest exist")
|
|
}
|
|
|
|
func equalIdentifier(targetIdentifier *workv1alpha1.ResourceIdentifier, ordinal int, workload *unstructured.Unstructured) (bool, error) {
|
|
groupVersion, err := schema.ParseGroupVersion(workload.GetAPIVersion())
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if targetIdentifier.Ordinal == ordinal &&
|
|
targetIdentifier.Group == groupVersion.Group &&
|
|
targetIdentifier.Version == groupVersion.Version &&
|
|
targetIdentifier.Kind == workload.GetKind() &&
|
|
targetIdentifier.Namespace == workload.GetNamespace() &&
|
|
targetIdentifier.Name == workload.GetName() {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// IsResourceApplied checks whether resource has been dispatched to member cluster or not
|
|
func IsResourceApplied(workStatus *workv1alpha1.WorkStatus) bool {
|
|
return meta.IsStatusConditionTrue(workStatus.Conditions, workv1alpha1.WorkApplied)
|
|
}
|
|
|
|
// IsWorkContains checks if the target resource exists in a work.
|
|
// Note: This function checks the Work object's status to detect the target resource, so the Work should be 'Applied',
|
|
// otherwise always returns false.
|
|
func IsWorkContains(workStatus *workv1alpha1.WorkStatus, targetResource schema.GroupVersionKind) bool {
|
|
for _, manifestStatuses := range workStatus.ManifestStatuses {
|
|
if targetResource.Group == manifestStatuses.Identifier.Group &&
|
|
targetResource.Version == manifestStatuses.Identifier.Version &&
|
|
targetResource.Kind == manifestStatuses.Identifier.Kind {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|