From a4753cd77ca052feef04a8df2bc8bd7e879e24cf Mon Sep 17 00:00:00 2001 From: changzhen Date: Thu, 9 Mar 2023 15:05:10 +0800 Subject: [PATCH] add generation for cluster object Signed-off-by: changzhen --- pkg/registry/cluster/strategy.go | 19 +++++++-- pkg/registry/cluster/strategy_test.go | 60 ++++++++++++++++++++------- pkg/scheduler/event_handler.go | 2 +- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/pkg/registry/cluster/strategy.go b/pkg/registry/cluster/strategy.go index afc1a4ba1..fc914e7e5 100644 --- a/pkg/registry/cluster/strategy.go +++ b/pkg/registry/cluster/strategy.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -73,6 +74,10 @@ func (Strategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { // PrepareForCreate is invoked on create before validation to normalize the object. func (Strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { cluster := obj.(*clusterapis.Cluster) + cluster.Status = clusterapis.ClusterStatus{} + + cluster.Generation = 1 + if utilfeature.DefaultMutableFeatureGate.Enabled(features.CustomizedClusterResourceModeling) { if len(cluster.Spec.ResourceModels) == 0 { mutation.SetDefaultClusterResourceModels(cluster) @@ -84,10 +89,18 @@ func (Strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { // PrepareForUpdate is invoked on update before validation to normalize the object. func (Strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { - cluster := obj.(*clusterapis.Cluster) + newCluster := obj.(*clusterapis.Cluster) + oldCluster := old.(*clusterapis.Cluster) + newCluster.Status = oldCluster.Status + + // Any changes to the spec increases the generation number. + if !apiequality.Semantic.DeepEqual(newCluster.Spec, oldCluster.Spec) { + newCluster.Generation = oldCluster.Generation + 1 + } + if utilfeature.DefaultMutableFeatureGate.Enabled(features.CustomizedClusterResourceModeling) { - if len(cluster.Spec.ResourceModels) != 0 { - mutation.StandardizeClusterResourceModels(cluster.Spec.ResourceModels) + if len(newCluster.Spec.ResourceModels) != 0 { + mutation.StandardizeClusterResourceModels(newCluster.Spec.ResourceModels) } } } diff --git a/pkg/registry/cluster/strategy_test.go b/pkg/registry/cluster/strategy_test.go index 1306feb6d..c5cf2f504 100644 --- a/pkg/registry/cluster/strategy_test.go +++ b/pkg/registry/cluster/strategy_test.go @@ -34,6 +34,12 @@ func getValidCluster(name string) *clusterapis.Cluster { } } +func getValidClusterWithGeneration(name string, generation int64) *clusterapis.Cluster { + cluster := getValidCluster(name) + cluster.Generation = generation + return cluster +} + func setFeatureGateDuringTest(tb testing.TB, gate featuregate.FeatureGate, f featuregate.Feature, value bool) func() { originalValue := gate.Enabled(f) @@ -59,7 +65,7 @@ func TestStrategy_PrepareForCreate(t *testing.T) { clusterStrategy := NewStrategy(clusterscheme.Scheme) ctx := request.NewContext() - defaultResourceModelCluster := getValidCluster("m1") + defaultResourceModelCluster := getValidClusterWithGeneration("m1", 1) mutation.SetDefaultClusterResourceModels(defaultResourceModelCluster) standardResourceModelClusterBefore := getValidCluster("m2") @@ -85,7 +91,7 @@ func TestStrategy_PrepareForCreate(t *testing.T) { }, }} - standardResourceModelClusterAfter := getValidCluster("m2") + standardResourceModelClusterAfter := getValidClusterWithGeneration("m2", 1) standardResourceModelClusterAfter.Spec.ResourceModels = []clusterapis.ResourceModel{ { Grade: 1, @@ -118,7 +124,7 @@ func TestStrategy_PrepareForCreate(t *testing.T) { { name: "featureGate CustomizedClusterResourceModeling is false", object: getValidCluster("cluster"), - expect: getValidCluster("cluster"), + expect: getValidClusterWithGeneration("cluster", 1), gateFlag: false, }, { @@ -152,7 +158,7 @@ func TestStrategy_PrepareForUpdate(t *testing.T) { clusterStrategy := NewStrategy(clusterscheme.Scheme) ctx := request.NewContext() - standardResourceModelClusterBefore := getValidCluster("m2") + standardResourceModelClusterBefore := getValidClusterWithGeneration("m2", 2) standardResourceModelClusterBefore.Spec.ResourceModels = []clusterapis.ResourceModel{ { Grade: 2, @@ -175,7 +181,7 @@ func TestStrategy_PrepareForUpdate(t *testing.T) { }, }} - standardResourceModelClusterAfter := getValidCluster("m2") + standardResourceModelClusterAfter := getValidClusterWithGeneration("m2", 2) standardResourceModelClusterAfter.Spec.ResourceModels = []clusterapis.ResourceModel{ { Grade: 1, @@ -201,25 +207,51 @@ func TestStrategy_PrepareForUpdate(t *testing.T) { tests := []struct { name string - object runtime.Object + oldObj runtime.Object + newObj runtime.Object expect runtime.Object gateFlag bool }{ + { + name: "cluster spec has no change", + oldObj: getValidClusterWithGeneration("cluster", 2), + newObj: getValidClusterWithGeneration("cluster", 2), + expect: getValidClusterWithGeneration("cluster", 2), + gateFlag: false, + }, + { + name: "cluster spec has changed", + oldObj: getValidClusterWithGeneration("cluster", 2), + newObj: func() *clusterapis.Cluster { + cluster := getValidClusterWithGeneration("cluster", 2) + cluster.Spec.Provider = "a" + return cluster + }(), + expect: func() *clusterapis.Cluster { + cluster := getValidClusterWithGeneration("cluster", 3) + cluster.Spec.Provider = "a" + return cluster + }(), + gateFlag: false, + }, { name: "featureGate CustomizedClusterResourceModeling is false", - object: getValidCluster("cluster"), - expect: getValidCluster("cluster"), + oldObj: getValidClusterWithGeneration("cluster", 2), + newObj: getValidClusterWithGeneration("cluster", 2), + expect: getValidClusterWithGeneration("cluster", 2), gateFlag: false, }, { name: "featureGate CustomizedClusterResourceModeling is true and cluster.Spec.ResourceModels is nil", - object: getValidCluster("m1"), - expect: getValidCluster("m1"), + oldObj: getValidClusterWithGeneration("m1", 2), + newObj: getValidClusterWithGeneration("m1", 2), + expect: getValidClusterWithGeneration("m1", 2), gateFlag: true, }, { name: "featureGate CustomizedClusterResourceModeling is true and cluster.Spec.ResourceModels is not nil", - object: standardResourceModelClusterBefore, + oldObj: standardResourceModelClusterBefore, + newObj: standardResourceModelClusterBefore, expect: standardResourceModelClusterAfter, gateFlag: true, }, @@ -230,9 +262,9 @@ func TestStrategy_PrepareForUpdate(t *testing.T) { runtimeutil.Must(utilfeature.DefaultMutableFeatureGate.Add(features.DefaultFeatureGates)) setFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, features.CustomizedClusterResourceModeling, tt.gateFlag) - clusterStrategy.PrepareForUpdate(ctx, tt.object, nil) - if !reflect.DeepEqual(tt.expect, tt.object) { - t.Errorf("Object mismatch! Excepted: \n%#v \ngot: \n%#b", tt.expect, tt.object) + clusterStrategy.PrepareForUpdate(ctx, tt.newObj, tt.oldObj) + if !reflect.DeepEqual(tt.expect, tt.newObj) { + t.Errorf("Object mismatch! Excepted: \n%#v \ngot: \n%#b", tt.expect, tt.newObj) } }) } diff --git a/pkg/scheduler/event_handler.go b/pkg/scheduler/event_handler.go index df2e6f95b..829f7b351 100644 --- a/pkg/scheduler/event_handler.go +++ b/pkg/scheduler/event_handler.go @@ -167,7 +167,7 @@ func (s *Scheduler) updateCluster(oldObj, newObj interface{}) { switch { case !equality.Semantic.DeepEqual(oldCluster.Labels, newCluster.Labels): fallthrough - case !equality.Semantic.DeepEqual(oldCluster.Spec, newCluster.Spec): + case oldCluster.Generation != newCluster.Generation: s.enqueueAffectedBindings(oldCluster, newCluster) } }