Repository: clusternet/clusternet
Pull request #560 (2022-12-13 12:33:36)

fix merging TargetClusters
Signed-off-by: yinsenyan <yinsenyan@tencent.com>

<!--  Thanks for sending a pull request!

-->

#### What type of PR is this?
bugfix
#### What this PR does / why we need it:
fix #487 bug
In `func (t *TargetClusters) Merge(b *TargetClusters)` , if t is 
```
  bindingClusters:
  - c1
  - c2
```
and b is 
```
  bindingClusters:
  - c2
  - c1
```
panic occuce at `t.Replicas[feed][ti] += bReplicas[bi]`
#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

#### Special notes for your reviewer:


yinsenyan: crash log
```
I1213 03:51:51.664531       1 trace.go:205] Trace[1342383918]: "Scheduling" namespace:test,name:test (13-Dec-2022 03:51:50.406) (total time: 1257ms):
Trace[1342383918]: [1.25799472s] [1.25799472s] END
E1213 03:51:51.664639       1 runtime.go:78] Observed a panic: runtime.boundsError{x:1, y:1, signed:true, code:0x0} (runtime error: index out of range [1] with length 1)
goroutine 102 [running]:
k8s.io/apimachinery/pkg/util/runtime.logPanic({0x195da40, 0xc000368c18})
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/runtime/runtime.go:74 +0x7d
k8s.io/apimachinery/pkg/util/runtime.HandleCrash({0x0, 0x0, 0x11293c0})
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/runtime/runtime.go:48 +0x75
panic({0x195da40, 0xc000368c18})
        /usr/local/go/src/runtime/panic.go:1038 +0x215
github.com/clusternet/clusternet/pkg/scheduler/framework/interfaces.(*TargetClusters).Merge(0xc0005422a0, 0xc0004a51e8)
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/framework/interfaces/types.go:149 +0x705
github.com/clusternet/clusternet/pkg/scheduler/framework/plugins/defaultassigner.DynamicDivideReplicas(0xc0005943a0, {0xc000544120, 0x1, 0x1}, {{0xc0005420c0, 0x7fad05ebf778, 0xc00038de58}, 0xc000538c60})
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/framework/plugins/defaultassigner/algorithm.go:59 +0x557
github.com/clusternet/clusternet/pkg/scheduler/framework/plugins/defaultassigner.(*DynamicAssigner).Assign(0xe, {0xc, 0x3ec13aee7114f3b6}, 0xc0004a5478, 0xc0005943a0, 0xc000440060, {{0xc0005420c0, 0x2, 0x2}, 0xc000538c60})
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/framework/plugins/defaultassigner/dynamic.go:119 +0x347
github.com/clusternet/clusternet/pkg/scheduler/framework/runtime.(*frameworkImpl).runAssignPlugin(0xc00037cb40, {0x1cc0858, 0xc0008188c0}, {0x7fad05f080d0, 0xc0002c8560}, 0x1cc0858, 0xc0008188c0, 0x7fad05f08080, {{0xc0005420c0, 0x2, ...}, ...})
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/framework/runtime/framework.go:637 +0xf2
github.com/clusternet/clusternet/pkg/scheduler/framework/runtime.(*frameworkImpl).RunAssignPlugins(0xc00037cb40, {0x1cc0858, 0xc0008188c0}, 0x5, 0x16625e7, 0xa, {{0xc0005420c0, 0x2, 0x2}, 0xc000538c60})
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/framework/runtime/framework.go:621 +0x26e
github.com/clusternet/clusternet/pkg/scheduler/algorithm.(*genericScheduler).selectClusters(0xc0008ff4a0, {0x1cc0858, 0xc0008188c0}, 0x1d0e170, {0xc0008ff4a0, 0x2, 0xc0005943a0}, {0x1d0e170, 0xc00037cb40}, 0xc0005943a0, ...)
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/algorithm/generic.go:215 +0x39a
github.com/clusternet/clusternet/pkg/scheduler/algorithm.(*genericScheduler).Schedule(0xc0000d0aa0, {0x1cc0858, 0xc0008188c0}, {0x1d0e170, 0xc00037cb40}, 0xc000047b10, 0xc0005943a0, 0x1d0db)
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/algorithm/generic.go:116 +0x5c5
github.com/clusternet/clusternet/pkg/scheduler.(*Scheduler).scheduleOne(0xc000426210, {0x1cc0858, 0xc0002ceec0})
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/scheduler.go:303 +0x7d7
k8s.io/apimachinery/pkg/util/wait.JitterUntilWithContext.func1()
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/wait/wait.go:188 +0x25
k8s.io/apimachinery/pkg/util/wait.BackoffUntil.func1(0xc0004a5e80)
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/wait/wait.go:155 +0x67
k8s.io/apimachinery/pkg/util/wait.BackoffUntil(0xc00009a700, {0x1c90120, 0xc0006374a0}, 0x1, 0xc0005dca80)
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/wait/wait.go:156 +0xb6
k8s.io/apimachinery/pkg/util/wait.JitterUntil(0xc00009a700, 0x0, 0x0, 0x0, 0x10000000067e2e0)
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/wait/wait.go:133 +0x89
k8s.io/apimachinery/pkg/util/wait.JitterUntilWithContext({0x1cc0858, 0xc0002ceec0}, 0xc000053fa0, 0xc000402e80, 0xc000440a20, 0xc0)
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/wait/wait.go:188 +0x99
k8s.io/apimachinery/pkg/util/wait.UntilWithContext(...)
        /go/pkg/mod/github.com/clusternet/apimachinery@v0.23.0-alpha.0.0.20220224022903-dc3dec363e8c/pkg/util/wait/wait.go:99
github.com/clusternet/clusternet/pkg/scheduler.(*Scheduler).Run.func1({0x1cc0858, 0xc0002ceec0})
        /go/src/github.com/clusternet/clusternet/pkg/scheduler/scheduler.go:219 +0x4d
created by k8s.io/client-go/tools/leaderelection.(*LeaderElector).Run
        /go/pkg/mod/k8s.io/client-go@v0.23.5/tools/leaderelection/leaderelection.go:211 +0x154
panic: runtime error: index out of range [1] with length 1 [recovered]
        panic: runtime error: index out of range [1] with length 1

```
dixudx: cc @Garrybest 
Garrybest: Good job👍 LGTM

Patch:
diff --git a/pkg/scheduler/framework/interfaces/types.go b/pkg/scheduler/framework/interfaces/types.go
index dfc6cf6..12dbd5d 100644
--- a/pkg/scheduler/framework/interfaces/types.go
+++ b/pkg/scheduler/framework/interfaces/types.go
@@ -132,7 +132,7 @@ func (t *TargetClusters) Merge(b *TargetClusters) {
 			// this cluster is a new one, we should append
 			t.BindingClusters = append(t.BindingClusters, bCluster)
 			for feed, tReplicas := range t.Replicas {
-				if len(tReplicas) != 0 {
+				if len(tReplicas) != 0 && len(tReplicas) < len(t.BindingClusters) {
 					t.Replicas[feed] = append(t.Replicas[feed], 0)
 				}
 			}
@@ -142,7 +142,7 @@ func (t *TargetClusters) Merge(b *TargetClusters) {
 		for feed, bReplicas := range b.Replicas {
 			if len(bReplicas) != 0 {
 				if len(t.Replicas[feed]) == 0 {
-					t.Replicas[feed] = append(t.Replicas[feed], 0)
+					t.Replicas[feed] = make([]int32, len(b.Replicas[feed]))
 				}
 				t.Replicas[feed][ti] += bReplicas[bi]
 			}


Test patch:
diff --git a/pkg/scheduler/framework/interfaces/types_test.go b/pkg/scheduler/framework/interfaces/types_test.go
index 60a88f8..30b9feb 100644
--- a/pkg/scheduler/framework/interfaces/types_test.go
+++ b/pkg/scheduler/framework/interfaces/types_test.go
@@ -138,6 +138,23 @@ func TestTargetClusters_Merge(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "unsorted cluster",
+			b: &TargetClusters{
+				BindingClusters: []string{"c1", "c3", "c2"},
+				Replicas: map[string][]int32{
+					"f2": {1, 2, 0},
+				},
+			},
+			result: &TargetClusters{
+				BindingClusters: []string{"c1", "c2", "c3"},
+				Replicas: map[string][]int32{
+					"f1": {1, 2, 3},
+					"f2": {1, 0, 2},
+					"f3": {1, 0, 1},
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t1 *testing.T) {