Reduce the number of reconciliations on update in detector
Signed-off-by: z50023861 <zhuwentao15@huawei.com>
This commit is contained in:
parent
6969a5d643
commit
4469d39aa5
|
@ -320,6 +320,23 @@ func (d *ResourceDetector) OnAdd(obj interface{}) {
|
|||
|
||||
// OnUpdate handles object update event and push the object to queue.
|
||||
func (d *ResourceDetector) OnUpdate(oldObj, newObj interface{}) {
|
||||
unstructuredOldObj, err := helper.ToUnstructured(oldObj)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to transform oldObj, error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
unstructuredNewObj, err := helper.ToUnstructured(newObj)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to transform newObj, error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !SpecificationChanged(unstructuredOldObj, unstructuredNewObj) {
|
||||
klog.V(4).Infof("Ignore update event of object (kind=%s, %s/%s) as specification no change", unstructuredOldObj.GetKind(), unstructuredOldObj.GetNamespace(), unstructuredOldObj.GetName())
|
||||
return
|
||||
}
|
||||
|
||||
d.OnAdd(newObj)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package detector
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// SpecificationChanged check if the specification of the given object change or not
|
||||
func SpecificationChanged(oldObj, newObj *unstructured.Unstructured) bool {
|
||||
oldBackup := oldObj.DeepCopy()
|
||||
newBackup := newObj.DeepCopy()
|
||||
|
||||
// Remove the status and some system defined mutable fields in metadata, including managedFields and resourceVersion.
|
||||
// Refer to https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/object-meta/#ObjectMeta for more details.
|
||||
removedFields := [][]string{{"status"}, {"metadata", "managedFields"}, {"metadata", "resourceVersion"}}
|
||||
for _, r := range removedFields {
|
||||
unstructured.RemoveNestedField(oldBackup.Object, r...)
|
||||
unstructured.RemoveNestedField(newBackup.Object, r...)
|
||||
}
|
||||
|
||||
return !reflect.DeepEqual(oldBackup, newBackup)
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
package detector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
workloadv1alpha1 "github.com/karmada-io/karmada/examples/customresourceinterpreter/apis/workload/v1alpha1"
|
||||
"github.com/karmada-io/karmada/pkg/util/helper"
|
||||
)
|
||||
|
||||
func TestSpecificationChanged(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
oldObj interface{}
|
||||
newObj interface{}
|
||||
wantChange bool
|
||||
}{
|
||||
{
|
||||
name: "Only user defined fields changed(Deployment)",
|
||||
oldObj: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "nginx_v1"},
|
||||
},
|
||||
},
|
||||
Status: appsv1.DeploymentStatus{},
|
||||
},
|
||||
newObj: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "nginx_v2"},
|
||||
},
|
||||
},
|
||||
Status: appsv1.DeploymentStatus{},
|
||||
},
|
||||
wantChange: true,
|
||||
},
|
||||
{
|
||||
name: "Only status fields changed(Deployment)",
|
||||
oldObj: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{},
|
||||
Status: appsv1.DeploymentStatus{Replicas: 3, UpdatedReplicas: 3, ReadyReplicas: 0},
|
||||
},
|
||||
newObj: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123498",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}, {}},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{},
|
||||
Status: appsv1.DeploymentStatus{Replicas: 3, UpdatedReplicas: 1, ReadyReplicas: 2},
|
||||
},
|
||||
wantChange: false,
|
||||
},
|
||||
{
|
||||
name: "Both user defined fields and status fields changed(Deployment)",
|
||||
oldObj: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "nginx_v1"},
|
||||
},
|
||||
},
|
||||
Status: appsv1.DeploymentStatus{Replicas: 3, UpdatedReplicas: 3, ReadyReplicas: 0},
|
||||
},
|
||||
newObj: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123498",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}, {}},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "nginx_v2"},
|
||||
},
|
||||
},
|
||||
Status: appsv1.DeploymentStatus{Replicas: 3, UpdatedReplicas: 1, ReadyReplicas: 2},
|
||||
},
|
||||
wantChange: true,
|
||||
},
|
||||
{
|
||||
name: "Only user defined fields changed(CRD_WorkLoad)",
|
||||
oldObj: &workloadv1alpha1.Workload{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}},
|
||||
},
|
||||
Spec: workloadv1alpha1.WorkloadSpec{
|
||||
Replicas: pointer.Int32(3),
|
||||
Template: v1.PodTemplateSpec{},
|
||||
Paused: false,
|
||||
},
|
||||
Status: workloadv1alpha1.WorkloadStatus{ReadyReplicas: 3},
|
||||
},
|
||||
newObj: &workloadv1alpha1.Workload{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}},
|
||||
},
|
||||
Spec: workloadv1alpha1.WorkloadSpec{
|
||||
Replicas: pointer.Int32(5),
|
||||
Template: v1.PodTemplateSpec{},
|
||||
Paused: false,
|
||||
},
|
||||
Status: workloadv1alpha1.WorkloadStatus{ReadyReplicas: 3},
|
||||
},
|
||||
wantChange: true,
|
||||
},
|
||||
{
|
||||
name: "Only status fields changed(CRD_WorkLoad)",
|
||||
oldObj: &workloadv1alpha1.Workload{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}},
|
||||
},
|
||||
Spec: workloadv1alpha1.WorkloadSpec{
|
||||
Replicas: pointer.Int32(3),
|
||||
Template: v1.PodTemplateSpec{},
|
||||
Paused: false,
|
||||
},
|
||||
Status: workloadv1alpha1.WorkloadStatus{ReadyReplicas: 1},
|
||||
},
|
||||
newObj: &workloadv1alpha1.Workload{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123498",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}, {}},
|
||||
},
|
||||
Spec: workloadv1alpha1.WorkloadSpec{
|
||||
Replicas: pointer.Int32(3),
|
||||
Template: v1.PodTemplateSpec{},
|
||||
Paused: false,
|
||||
},
|
||||
Status: workloadv1alpha1.WorkloadStatus{ReadyReplicas: 3},
|
||||
},
|
||||
wantChange: false,
|
||||
},
|
||||
{
|
||||
name: "Both user defined fields and status fields changed(CRD_WorkLoad)",
|
||||
oldObj: &workloadv1alpha1.Workload{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123456",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}},
|
||||
},
|
||||
Spec: workloadv1alpha1.WorkloadSpec{
|
||||
Replicas: pointer.Int32(3),
|
||||
Template: v1.PodTemplateSpec{},
|
||||
Paused: false,
|
||||
},
|
||||
Status: workloadv1alpha1.WorkloadStatus{ReadyReplicas: 1},
|
||||
},
|
||||
newObj: &workloadv1alpha1.Workload{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "123498",
|
||||
ManagedFields: []metav1.ManagedFieldsEntry{{}, {}},
|
||||
},
|
||||
Spec: workloadv1alpha1.WorkloadSpec{
|
||||
Replicas: pointer.Int32(5),
|
||||
Template: v1.PodTemplateSpec{},
|
||||
Paused: false,
|
||||
},
|
||||
Status: workloadv1alpha1.WorkloadStatus{ReadyReplicas: 3},
|
||||
},
|
||||
wantChange: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
unstructuredOldObj, err := helper.ToUnstructured(tt.oldObj)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to transform oldObj, error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
unstructuredNewObj, err := helper.ToUnstructured(tt.newObj)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to transform newObj, error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
got := SpecificationChanged(unstructuredOldObj, unstructuredNewObj)
|
||||
if tt.wantChange != got {
|
||||
t.Fatalf("SpecificationChanged() got %v, want %v", got, tt.wantChange)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue