k8s.io/kubernetes@v1.29.3/pkg/scheduler/profile/profile_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package profile 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "testing" 24 25 v1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/client-go/tools/events" 28 "k8s.io/klog/v2/ktesting" 29 "k8s.io/kubernetes/pkg/scheduler/apis/config" 30 "k8s.io/kubernetes/pkg/scheduler/framework" 31 frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime" 32 ) 33 34 var fakeRegistry = frameworkruntime.Registry{ 35 "QueueSort": newFakePlugin("QueueSort"), 36 "Bind1": newFakePlugin("Bind1"), 37 "Bind2": newFakePlugin("Bind2"), 38 "Another": newFakePlugin("Another"), 39 } 40 41 func TestNewMap(t *testing.T) { 42 cases := []struct { 43 name string 44 cfgs []config.KubeSchedulerProfile 45 wantErr string 46 }{ 47 { 48 name: "valid", 49 cfgs: []config.KubeSchedulerProfile{ 50 { 51 SchedulerName: "profile-1", 52 Plugins: &config.Plugins{ 53 QueueSort: config.PluginSet{ 54 Enabled: []config.Plugin{ 55 {Name: "QueueSort"}, 56 }, 57 }, 58 Bind: config.PluginSet{ 59 Enabled: []config.Plugin{ 60 {Name: "Bind1"}, 61 }, 62 }, 63 }, 64 }, 65 { 66 SchedulerName: "profile-2", 67 Plugins: &config.Plugins{ 68 QueueSort: config.PluginSet{ 69 Enabled: []config.Plugin{ 70 {Name: "QueueSort"}, 71 }, 72 }, 73 Bind: config.PluginSet{ 74 Enabled: []config.Plugin{ 75 {Name: "Bind2"}, 76 }, 77 }, 78 }, 79 PluginConfig: []config.PluginConfig{ 80 { 81 Name: "Bind2", 82 Args: &runtime.Unknown{Raw: []byte("{}")}, 83 }, 84 }, 85 }, 86 }, 87 }, 88 { 89 name: "different queue sort", 90 cfgs: []config.KubeSchedulerProfile{ 91 { 92 SchedulerName: "profile-1", 93 Plugins: &config.Plugins{ 94 QueueSort: config.PluginSet{ 95 Enabled: []config.Plugin{ 96 {Name: "QueueSort"}, 97 }, 98 }, 99 Bind: config.PluginSet{ 100 Enabled: []config.Plugin{ 101 {Name: "Bind1"}, 102 }, 103 }, 104 }, 105 }, 106 { 107 SchedulerName: "profile-2", 108 Plugins: &config.Plugins{ 109 QueueSort: config.PluginSet{ 110 Enabled: []config.Plugin{ 111 {Name: "Another"}, 112 }, 113 }, 114 Bind: config.PluginSet{ 115 Enabled: []config.Plugin{ 116 {Name: "Bind2"}, 117 }, 118 }, 119 }, 120 }, 121 }, 122 wantErr: "different queue sort plugins", 123 }, 124 { 125 name: "different queue sort args", 126 cfgs: []config.KubeSchedulerProfile{ 127 { 128 SchedulerName: "profile-1", 129 Plugins: &config.Plugins{ 130 QueueSort: config.PluginSet{ 131 Enabled: []config.Plugin{ 132 {Name: "QueueSort"}, 133 }, 134 }, 135 Bind: config.PluginSet{ 136 Enabled: []config.Plugin{ 137 {Name: "Bind1"}, 138 }, 139 }, 140 }, 141 PluginConfig: []config.PluginConfig{ 142 { 143 Name: "QueueSort", 144 Args: &runtime.Unknown{Raw: []byte("{}")}, 145 }, 146 }, 147 }, 148 { 149 SchedulerName: "profile-2", 150 Plugins: &config.Plugins{ 151 QueueSort: config.PluginSet{ 152 Enabled: []config.Plugin{ 153 {Name: "QueueSort"}, 154 }, 155 }, 156 Bind: config.PluginSet{ 157 Enabled: []config.Plugin{ 158 {Name: "Bind2"}, 159 }, 160 }, 161 }, 162 }, 163 }, 164 wantErr: "different queue sort plugin args", 165 }, 166 { 167 name: "duplicate scheduler name", 168 cfgs: []config.KubeSchedulerProfile{ 169 { 170 SchedulerName: "profile-1", 171 Plugins: &config.Plugins{ 172 QueueSort: config.PluginSet{ 173 Enabled: []config.Plugin{ 174 {Name: "QueueSort"}, 175 }, 176 }, 177 Bind: config.PluginSet{ 178 Enabled: []config.Plugin{ 179 {Name: "Bind1"}, 180 }, 181 }, 182 }, 183 }, 184 { 185 SchedulerName: "profile-1", 186 Plugins: &config.Plugins{ 187 QueueSort: config.PluginSet{ 188 Enabled: []config.Plugin{ 189 {Name: "QueueSort"}, 190 }, 191 }, 192 Bind: config.PluginSet{ 193 Enabled: []config.Plugin{ 194 {Name: "Bind2"}, 195 }, 196 }, 197 }, 198 }, 199 }, 200 wantErr: "duplicate profile", 201 }, 202 { 203 name: "scheduler name is needed", 204 cfgs: []config.KubeSchedulerProfile{ 205 { 206 Plugins: &config.Plugins{ 207 QueueSort: config.PluginSet{ 208 Enabled: []config.Plugin{ 209 {Name: "QueueSort"}, 210 }, 211 }, 212 Bind: config.PluginSet{ 213 Enabled: []config.Plugin{ 214 {Name: "Bind1"}, 215 }, 216 }, 217 }, 218 }, 219 }, 220 wantErr: "scheduler name is needed", 221 }, 222 { 223 name: "plugins required for profile", 224 cfgs: []config.KubeSchedulerProfile{ 225 { 226 SchedulerName: "profile-1", 227 }, 228 }, 229 wantErr: "plugins required for profile", 230 }, 231 { 232 name: "invalid framework configuration", 233 cfgs: []config.KubeSchedulerProfile{ 234 { 235 SchedulerName: "invalid-profile", 236 Plugins: &config.Plugins{ 237 QueueSort: config.PluginSet{ 238 Enabled: []config.Plugin{ 239 {Name: "QueueSort"}, 240 }, 241 }, 242 }, 243 }, 244 }, 245 wantErr: "at least one bind plugin is needed", 246 }, 247 } 248 for _, tc := range cases { 249 t.Run(tc.name, func(t *testing.T) { 250 _, ctx := ktesting.NewTestContext(t) 251 ctx, cancel := context.WithCancel(ctx) 252 defer cancel() 253 m, err := NewMap(ctx, tc.cfgs, fakeRegistry, nilRecorderFactory) 254 if err := checkErr(err, tc.wantErr); err != nil { 255 t.Fatal(err) 256 } 257 if len(tc.wantErr) != 0 { 258 return 259 } 260 if len(m) != len(tc.cfgs) { 261 t.Errorf("got %d profiles, want %d", len(m), len(tc.cfgs)) 262 } 263 }) 264 } 265 } 266 267 type fakePlugin struct { 268 name string 269 } 270 271 func (p *fakePlugin) Name() string { 272 return p.name 273 } 274 275 func (p *fakePlugin) Less(*framework.QueuedPodInfo, *framework.QueuedPodInfo) bool { 276 return false 277 } 278 279 func (p *fakePlugin) Bind(context.Context, *framework.CycleState, *v1.Pod, string) *framework.Status { 280 return nil 281 } 282 283 func newFakePlugin(name string) func(ctx context.Context, object runtime.Object, handle framework.Handle) (framework.Plugin, error) { 284 return func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) { 285 return &fakePlugin{name: name}, nil 286 } 287 } 288 289 func nilRecorderFactory(_ string) events.EventRecorder { 290 return nil 291 } 292 293 func checkErr(err error, wantErr string) error { 294 if len(wantErr) == 0 { 295 return err 296 } 297 if err == nil || !strings.Contains(err.Error(), wantErr) { 298 return fmt.Errorf("got error %q, want %q", err, wantErr) 299 } 300 return nil 301 }