diff --git a/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus.go b/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus.go index 487b6e026..34c9091f9 100644 --- a/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus.go +++ b/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus.go @@ -28,6 +28,7 @@ func getAllDefaultAggregateStatusInterpreter() map[schema.GroupVersionKind]aggre s[corev1.SchemeGroupVersion.WithKind(util.ServiceKind)] = aggregateServiceStatus s[networkingv1.SchemeGroupVersion.WithKind(util.IngressKind)] = aggregateIngressStatus s[batchv1.SchemeGroupVersion.WithKind(util.JobKind)] = aggregateJobStatus + s[batchv1.SchemeGroupVersion.WithKind(util.CronJobKind)] = aggregateCronJobStatus s[appsv1.SchemeGroupVersion.WithKind(util.DaemonSetKind)] = aggregateDaemonSetStatus s[appsv1.SchemeGroupVersion.WithKind(util.StatefulSetKind)] = aggregateStatefulSetStatus s[corev1.SchemeGroupVersion.WithKind(util.PodKind)] = aggregatePodStatus @@ -198,6 +199,47 @@ func aggregateJobStatus(object *unstructured.Unstructured, aggregatedStatusItems return helper.ToUnstructured(job) } +func aggregateCronJobStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (*unstructured.Unstructured, error) { + cronjob := &batchv1.CronJob{} + err := helper.ConvertToTypedObject(object, cronjob) + if err != nil { + return nil, err + } + newStatus := &batchv1.CronJobStatus{} + for _, item := range aggregatedStatusItems { + if item.Status == nil { + continue + } + temp := &batchv1.CronJobStatus{} + if err = json.Unmarshal(item.Status.Raw, temp); err != nil { + return nil, err + } + klog.V(3).Infof("Grab cronJob(%s/%s) status from cluster(%s), active: %+v, lastScheduleTime: %s, lastSuccessfulTime: %s", + cronjob.Namespace, cronjob.Name, item.ClusterName, temp.Active, temp.LastScheduleTime.String(), temp.LastSuccessfulTime.String()) + newStatus.Active = append(newStatus.Active, temp.Active...) + if newStatus.LastScheduleTime == nil { + newStatus.LastScheduleTime = temp.LastScheduleTime + } + if newStatus.LastScheduleTime != nil && temp.LastScheduleTime != nil && newStatus.LastScheduleTime.Before(temp.LastScheduleTime) { + newStatus.LastScheduleTime = temp.LastScheduleTime + } + if newStatus.LastSuccessfulTime == nil { + newStatus.LastSuccessfulTime = temp.LastSuccessfulTime + } + if newStatus.LastSuccessfulTime != nil && temp.LastSuccessfulTime != nil && newStatus.LastSuccessfulTime.Before(temp.LastSuccessfulTime) { + newStatus.LastSuccessfulTime = temp.LastSuccessfulTime + } + } + if reflect.DeepEqual(cronjob.Status, *newStatus) { + klog.V(3).Infof("Ignore update cronjob(%s/%s) status as up to date", cronjob.Namespace, cronjob.Name) + return object, nil + } + cronjob.Status.Active = newStatus.Active + cronjob.Status.LastScheduleTime = newStatus.LastScheduleTime + cronjob.Status.LastSuccessfulTime = newStatus.LastSuccessfulTime + return helper.ToUnstructured(cronjob) +} + func aggregateDaemonSetStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (*unstructured.Unstructured, error) { daemonSet := &appsv1.DaemonSet{} err := helper.ConvertToTypedObject(object, daemonSet) diff --git a/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus_test.go b/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus_test.go index 381375f34..586620cad 100644 --- a/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus_test.go +++ b/pkg/resourceinterpreter/defaultinterpreter/aggregatestatus_test.go @@ -937,3 +937,91 @@ func TestAggregatedPodDisruptionBudgetStatus(t *testing.T) { }) } } + +func Test_aggregateCronJobStatus(t *testing.T) { + currCronJobObj, _ := helper.ToUnstructured(&batchv1.CronJob{ + Status: batchv1.CronJobStatus{ + Active: []corev1.ObjectReference{}, + LastScheduleTime: nil, + LastSuccessfulTime: nil, + }, + }) + + cronjobStatusRaw1, _ := helper.BuildStatusRawExtension(map[string]interface{}{ + "active": []corev1.ObjectReference{ + { + APIVersion: "batch/v1", + Kind: "Job", + Name: "foo", + Namespace: "default", + ResourceVersion: "1", + UID: "1d5db04f-f2e8-4807-b6d4-7b78f402250d", + }, + }, + "lastScheduleTime": "2023-02-08T07:16:00Z", + "lastSuccessfulTime": "2023-02-08T07:15:00Z", + }) + cronjobStatusRaw2, _ := helper.BuildStatusRawExtension(map[string]interface{}{ + "active": []corev1.ObjectReference{ + { + APIVersion: "batch/v1", + Kind: "Job", + Name: "foo", + Namespace: "default", + ResourceVersion: "1", + UID: "1d5db04f-f2e8-4807-b6d4-7b78f402250d", + }, + }, + "lastScheduleTime": "2023-02-08T07:17:00Z", + "lastSuccessfulTime": "2023-02-08T07:17:00Z", + }) + parse, _ := time.Parse("2006-01-02 15:04:05", "2023-02-08 07:17:00") + successfulTime := metav1.NewTime(parse) + expectedCronJobObj, _ := helper.ToUnstructured(&batchv1.CronJob{ + Status: batchv1.CronJobStatus{ + Active: []corev1.ObjectReference{ + { + APIVersion: "batch/v1", + Kind: "Job", + Name: "foo", + Namespace: "default", + ResourceVersion: "1", + UID: "1d5db04f-f2e8-4807-b6d4-7b78f402250d", + }, + { + APIVersion: "batch/v1", + Kind: "Job", + Name: "foo", + Namespace: "default", + ResourceVersion: "1", + UID: "1d5db04f-f2e8-4807-b6d4-7b78f402250d", + }, + }, + LastScheduleTime: &successfulTime, + LastSuccessfulTime: &successfulTime, + }, + }) + + aggregateStatusItems := []workv1alpha2.AggregatedStatusItem{ + {ClusterName: "member1", Status: cronjobStatusRaw1, Applied: true}, + {ClusterName: "member2", Status: cronjobStatusRaw2, Applied: true}, + } + for _, tt := range []struct { + name string + curObj *unstructured.Unstructured + aggregatedStatusItems []workv1alpha2.AggregatedStatusItem + expectedObj *unstructured.Unstructured + }{ + { + name: "update cronjob status", + curObj: currCronJobObj, + expectedObj: expectedCronJobObj, + aggregatedStatusItems: aggregateStatusItems, + }, + } { + t.Run(tt.name, func(t *testing.T) { + actualObj, _ := aggregateCronJobStatus(tt.curObj, tt.aggregatedStatusItems) + assert.Equal(t, tt.expectedObj, actualObj) + }) + } +}