sigs.k8s.io/kueue@v0.6.2/pkg/visibility/api/rest/pending_workloads_lq_test.go (about) 1 // Copyright 2023 The Kubernetes Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package rest 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/google/go-cmp/cmp/cmpopts" 24 "k8s.io/apimachinery/pkg/api/errors" 25 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apiserver/pkg/endpoints/request" 28 29 kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1" 30 visibility "sigs.k8s.io/kueue/apis/visibility/v1alpha1" 31 "sigs.k8s.io/kueue/pkg/constants" 32 "sigs.k8s.io/kueue/pkg/queue" 33 utiltesting "sigs.k8s.io/kueue/pkg/util/testing" 34 ) 35 36 func TestPendingWorkloadsInLQ(t *testing.T) { 37 const ( 38 nsNameA = "nsA" 39 nsNameB = "nsB" 40 cqNameA = "cqA" 41 cqNameB = "cqB" 42 lqNameA = "lqA" 43 lqNameB = "lqB" 44 lowPrio = 50 45 highPrio = 100 46 ) 47 48 defaultQueryParams := &visibility.PendingWorkloadOptions{ 49 Offset: 0, 50 Limit: constants.DefaultPendingWorkloadsLimit, 51 } 52 53 scheme := runtime.NewScheme() 54 if err := kueue.AddToScheme(scheme); err != nil { 55 t.Fatalf("Failed adding kueue scheme: %s", err) 56 } 57 if err := visibility.AddToScheme(scheme); err != nil { 58 t.Fatalf("Failed adding kueue scheme: %s", err) 59 } 60 61 now := time.Now() 62 cases := map[string]struct { 63 clusterQueues []*kueue.ClusterQueue 64 queues []*kueue.LocalQueue 65 workloads []*kueue.Workload 66 req *req 67 wantResp *resp 68 wantErrMatch func(error) bool 69 }{ 70 "single ClusterQueue and single LocalQueue setup with two workloads and default query parameters": { 71 clusterQueues: []*kueue.ClusterQueue{ 72 utiltesting.MakeClusterQueue(cqNameA).Obj(), 73 }, 74 queues: []*kueue.LocalQueue{ 75 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 76 }, 77 workloads: []*kueue.Workload{ 78 utiltesting.MakeWorkload("a", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 79 utiltesting.MakeWorkload("b", nsNameA).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 80 }, 81 req: &req{ 82 nsName: nsNameA, 83 queueName: lqNameA, 84 queryParams: defaultQueryParams, 85 }, 86 wantResp: &resp{ 87 wantPendingWorkloads: []visibility.PendingWorkload{ 88 { 89 ObjectMeta: v1.ObjectMeta{ 90 Name: "a", 91 Namespace: nsNameA, 92 CreationTimestamp: v1.NewTime(now), 93 }, 94 LocalQueueName: lqNameA, 95 Priority: highPrio, 96 PositionInClusterQueue: 0, 97 PositionInLocalQueue: 0, 98 }, 99 { 100 ObjectMeta: v1.ObjectMeta{ 101 Name: "b", 102 Namespace: nsNameA, 103 CreationTimestamp: v1.NewTime(now), 104 }, 105 LocalQueueName: lqNameA, 106 Priority: lowPrio, 107 PositionInClusterQueue: 1, 108 PositionInLocalQueue: 1, 109 }}, 110 }, 111 }, 112 "single ClusterQueue and two LocalQueue setup with four workloads and default query parameters; LocalQueue A request": { 113 clusterQueues: []*kueue.ClusterQueue{ 114 utiltesting.MakeClusterQueue(cqNameA).Obj(), 115 }, 116 queues: []*kueue.LocalQueue{ 117 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 118 utiltesting.MakeLocalQueue(lqNameB, nsNameA).ClusterQueue(cqNameA).Obj(), 119 }, 120 workloads: []*kueue.Workload{ 121 utiltesting.MakeWorkload("lqA-high-prio", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 122 utiltesting.MakeWorkload("lqA-low-prio", nsNameA).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 123 utiltesting.MakeWorkload("lqB-high-prio", nsNameA).Queue(lqNameB).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 124 utiltesting.MakeWorkload("lqB-low-prio", nsNameA).Queue(lqNameB).Priority(lowPrio).Creation(now.Add(time.Second)).Obj(), 125 }, 126 req: &req{ 127 nsName: nsNameA, 128 queueName: lqNameA, 129 queryParams: defaultQueryParams, 130 }, 131 wantResp: &resp{ 132 wantPendingWorkloads: []visibility.PendingWorkload{ 133 { 134 ObjectMeta: v1.ObjectMeta{ 135 Name: "lqA-high-prio", 136 Namespace: nsNameA, 137 CreationTimestamp: v1.NewTime(now), 138 }, 139 LocalQueueName: lqNameA, 140 Priority: highPrio, 141 PositionInClusterQueue: 0, 142 PositionInLocalQueue: 0, 143 }, 144 { 145 ObjectMeta: v1.ObjectMeta{ 146 Name: "lqA-low-prio", 147 Namespace: nsNameA, 148 CreationTimestamp: v1.NewTime(now), 149 }, 150 LocalQueueName: lqNameA, 151 Priority: lowPrio, 152 PositionInClusterQueue: 2, 153 PositionInLocalQueue: 1, 154 }}, 155 }, 156 }, 157 "single ClusterQueue and two LocalQueue setup with four workloads and default query parameters; LocalQueue B request": { 158 clusterQueues: []*kueue.ClusterQueue{ 159 utiltesting.MakeClusterQueue(cqNameA).Obj(), 160 }, 161 queues: []*kueue.LocalQueue{ 162 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 163 utiltesting.MakeLocalQueue(lqNameB, nsNameA).ClusterQueue(cqNameA).Obj(), 164 }, 165 workloads: []*kueue.Workload{ 166 utiltesting.MakeWorkload("lqA-high-prio", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 167 utiltesting.MakeWorkload("lqA-low-prio", nsNameA).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 168 utiltesting.MakeWorkload("lqB-high-prio", nsNameA).Queue(lqNameB).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 169 utiltesting.MakeWorkload("lqB-low-prio", nsNameA).Queue(lqNameB).Priority(lowPrio).Creation(now.Add(time.Second)).Obj(), 170 }, 171 req: &req{ 172 nsName: nsNameA, 173 queueName: lqNameB, 174 queryParams: defaultQueryParams, 175 }, 176 wantResp: &resp{ 177 wantPendingWorkloads: []visibility.PendingWorkload{ 178 { 179 ObjectMeta: v1.ObjectMeta{ 180 Name: "lqB-high-prio", 181 Namespace: nsNameA, 182 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 183 }, 184 LocalQueueName: lqNameB, 185 Priority: highPrio, 186 PositionInClusterQueue: 1, 187 PositionInLocalQueue: 0, 188 }, 189 { 190 ObjectMeta: v1.ObjectMeta{ 191 Name: "lqB-low-prio", 192 Namespace: nsNameA, 193 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 194 }, 195 LocalQueueName: lqNameB, 196 Priority: lowPrio, 197 PositionInClusterQueue: 3, 198 PositionInLocalQueue: 1, 199 }, 200 }, 201 }, 202 }, 203 "two Namespaces, two ClusterQueue and two LocalQueue setup with four workloads and default query parameters, LocalQueue A request": { 204 clusterQueues: []*kueue.ClusterQueue{ 205 utiltesting.MakeClusterQueue(cqNameA).Obj(), 206 utiltesting.MakeClusterQueue(cqNameB).Obj(), 207 }, 208 queues: []*kueue.LocalQueue{ 209 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 210 utiltesting.MakeLocalQueue(lqNameB, nsNameB).ClusterQueue(cqNameB).Obj(), 211 }, 212 workloads: []*kueue.Workload{ 213 utiltesting.MakeWorkload("lqA-high-prio", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 214 utiltesting.MakeWorkload("lqA-low-prio", nsNameA).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 215 utiltesting.MakeWorkload("lqB-high-prio", nsNameB).Queue(lqNameB).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 216 utiltesting.MakeWorkload("lqB-low-prio", nsNameB).Queue(lqNameB).Priority(lowPrio).Creation(now.Add(time.Second)).Obj(), 217 }, 218 req: &req{ 219 nsName: nsNameA, 220 queueName: lqNameA, 221 queryParams: defaultQueryParams, 222 }, 223 wantResp: &resp{ 224 wantPendingWorkloads: []visibility.PendingWorkload{ 225 { 226 ObjectMeta: v1.ObjectMeta{ 227 Name: "lqA-high-prio", 228 Namespace: nsNameA, 229 CreationTimestamp: v1.NewTime(now), 230 }, 231 LocalQueueName: lqNameA, 232 Priority: highPrio, 233 PositionInClusterQueue: 0, 234 PositionInLocalQueue: 0, 235 }, 236 { 237 ObjectMeta: v1.ObjectMeta{ 238 Name: "lqA-low-prio", 239 Namespace: nsNameA, 240 CreationTimestamp: v1.NewTime(now), 241 }, 242 LocalQueueName: lqNameA, 243 Priority: lowPrio, 244 PositionInClusterQueue: 1, 245 PositionInLocalQueue: 1, 246 }, 247 }, 248 }, 249 }, 250 "two Namespaces, two ClusterQueue and two LocalQueue setup with four workloads and default query parameters, LocalQueue B request": { 251 clusterQueues: []*kueue.ClusterQueue{ 252 utiltesting.MakeClusterQueue(cqNameA).Obj(), 253 utiltesting.MakeClusterQueue(cqNameB).Obj(), 254 }, 255 queues: []*kueue.LocalQueue{ 256 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 257 utiltesting.MakeLocalQueue(lqNameB, nsNameB).ClusterQueue(cqNameB).Obj(), 258 }, 259 workloads: []*kueue.Workload{ 260 utiltesting.MakeWorkload("lqA-high-prio", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 261 utiltesting.MakeWorkload("lqA-low-prio", nsNameA).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 262 utiltesting.MakeWorkload("lqB-high-prio", nsNameB).Queue(lqNameB).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 263 utiltesting.MakeWorkload("lqB-low-prio", nsNameB).Queue(lqNameB).Priority(lowPrio).Creation(now.Add(time.Second)).Obj(), 264 }, 265 req: &req{ 266 nsName: nsNameB, 267 queueName: lqNameB, 268 queryParams: defaultQueryParams, 269 }, 270 wantResp: &resp{ 271 wantPendingWorkloads: []visibility.PendingWorkload{ 272 { 273 ObjectMeta: v1.ObjectMeta{ 274 Name: "lqB-high-prio", 275 Namespace: nsNameB, 276 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 277 }, 278 LocalQueueName: lqNameB, 279 Priority: highPrio, 280 PositionInClusterQueue: 0, 281 PositionInLocalQueue: 0, 282 }, 283 { 284 ObjectMeta: v1.ObjectMeta{ 285 Name: "lqB-low-prio", 286 Namespace: nsNameB, 287 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 288 }, 289 LocalQueueName: lqNameB, 290 Priority: lowPrio, 291 PositionInClusterQueue: 1, 292 PositionInLocalQueue: 1, 293 }, 294 }, 295 }, 296 }, 297 "limit query parameter set": { 298 clusterQueues: []*kueue.ClusterQueue{ 299 utiltesting.MakeClusterQueue(cqNameA).Obj(), 300 }, 301 queues: []*kueue.LocalQueue{ 302 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 303 }, 304 workloads: []*kueue.Workload{ 305 utiltesting.MakeWorkload("a", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 306 utiltesting.MakeWorkload("b", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 307 utiltesting.MakeWorkload("c", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second * 2)).Obj(), 308 }, 309 req: &req{ 310 nsName: nsNameA, 311 queueName: lqNameA, 312 queryParams: &visibility.PendingWorkloadOptions{ 313 Limit: 2, 314 }, 315 }, 316 wantResp: &resp{ 317 wantPendingWorkloads: []visibility.PendingWorkload{ 318 { 319 ObjectMeta: v1.ObjectMeta{ 320 Name: "a", 321 Namespace: nsNameA, 322 CreationTimestamp: v1.NewTime(now), 323 }, 324 LocalQueueName: lqNameA, 325 Priority: highPrio, 326 PositionInClusterQueue: 0, 327 PositionInLocalQueue: 0, 328 }, 329 { 330 ObjectMeta: v1.ObjectMeta{ 331 Name: "b", 332 Namespace: nsNameA, 333 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 334 }, 335 LocalQueueName: lqNameA, 336 Priority: highPrio, 337 PositionInClusterQueue: 1, 338 PositionInLocalQueue: 1, 339 }, 340 }, 341 }, 342 }, 343 "offset query parameter set": { 344 clusterQueues: []*kueue.ClusterQueue{ 345 utiltesting.MakeClusterQueue(cqNameA).Obj(), 346 }, 347 queues: []*kueue.LocalQueue{ 348 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 349 }, 350 workloads: []*kueue.Workload{ 351 utiltesting.MakeWorkload("a", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 352 utiltesting.MakeWorkload("b", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 353 utiltesting.MakeWorkload("c", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second * 2)).Obj(), 354 }, 355 req: &req{ 356 nsName: nsNameA, 357 queueName: lqNameA, 358 queryParams: &visibility.PendingWorkloadOptions{ 359 Offset: 1, 360 Limit: constants.DefaultPendingWorkloadsLimit, 361 }, 362 }, 363 wantResp: &resp{ 364 wantPendingWorkloads: []visibility.PendingWorkload{ 365 { 366 ObjectMeta: v1.ObjectMeta{ 367 Name: "b", 368 Namespace: nsNameA, 369 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 370 }, 371 LocalQueueName: lqNameA, 372 Priority: highPrio, 373 PositionInClusterQueue: 1, 374 PositionInLocalQueue: 1, 375 }, 376 { 377 ObjectMeta: v1.ObjectMeta{ 378 Name: "c", 379 Namespace: nsNameA, 380 CreationTimestamp: v1.NewTime(now.Add(time.Second * 2)), 381 }, 382 LocalQueueName: lqNameA, 383 Priority: highPrio, 384 PositionInClusterQueue: 2, 385 PositionInLocalQueue: 2, 386 }, 387 }, 388 }, 389 }, 390 "limit and offset query parameters set": { 391 clusterQueues: []*kueue.ClusterQueue{ 392 utiltesting.MakeClusterQueue(cqNameA).Obj(), 393 }, 394 queues: []*kueue.LocalQueue{ 395 utiltesting.MakeLocalQueue(lqNameA, nsNameA).ClusterQueue(cqNameA).Obj(), 396 }, 397 workloads: []*kueue.Workload{ 398 utiltesting.MakeWorkload("a", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 399 utiltesting.MakeWorkload("b", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 400 utiltesting.MakeWorkload("c", nsNameA).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second * 2)).Obj(), 401 }, 402 req: &req{ 403 nsName: nsNameA, 404 queueName: lqNameA, 405 queryParams: &visibility.PendingWorkloadOptions{ 406 Offset: 1, 407 Limit: 1, 408 }, 409 }, 410 wantResp: &resp{ 411 wantPendingWorkloads: []visibility.PendingWorkload{ 412 { 413 ObjectMeta: v1.ObjectMeta{ 414 Name: "b", 415 Namespace: nsNameA, 416 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 417 }, 418 LocalQueueName: lqNameA, 419 Priority: highPrio, 420 PositionInClusterQueue: 1, 421 PositionInLocalQueue: 1, 422 }, 423 }, 424 }, 425 }, 426 "nonexistent queue name": { 427 req: &req{ 428 queueName: "nonexistent-queue", 429 queryParams: defaultQueryParams, 430 }, 431 wantResp: &resp{ 432 wantErr: errors.NewNotFound(visibility.Resource("localqueue"), "invalid-name"), 433 }, 434 wantErrMatch: errors.IsNotFound, 435 }, 436 } 437 for name, tc := range cases { 438 t.Run(name, func(t *testing.T) { 439 manager := queue.NewManager(utiltesting.NewFakeClient(), nil) 440 ctx, cancel := context.WithCancel(context.Background()) 441 defer cancel() 442 go manager.CleanUpOnContext(ctx) 443 pendingWorkloadsInLqRest := NewPendingWorkloadsInLqREST(manager) 444 for _, cq := range tc.clusterQueues { 445 if err := manager.AddClusterQueue(ctx, cq); err != nil { 446 t.Fatalf("Adding cluster queue %s: %v", cq.Name, err) 447 } 448 } 449 for _, q := range tc.queues { 450 if err := manager.AddLocalQueue(ctx, q); err != nil { 451 t.Fatalf("Adding queue %q: %v", q.Name, err) 452 } 453 } 454 for _, w := range tc.workloads { 455 manager.AddOrUpdateWorkload(w) 456 } 457 458 ctx = request.WithNamespace(ctx, tc.req.nsName) 459 info, err := pendingWorkloadsInLqRest.Get(ctx, tc.req.queueName, tc.req.queryParams) 460 if tc.wantErrMatch != nil { 461 if !tc.wantErrMatch(err) { 462 t.Errorf("Error differs: (-want,+got):\n%s", cmp.Diff(tc.wantResp.wantErr.Error(), err.Error())) 463 } 464 } else if err != nil { 465 t.Error(err) 466 } else { 467 pendingWorkloadsInfo := info.(*visibility.PendingWorkloadsSummary) 468 if diff := cmp.Diff(tc.wantResp.wantPendingWorkloads, pendingWorkloadsInfo.Items, cmpopts.EquateEmpty()); diff != "" { 469 t.Errorf("Pending workloads differ: (-want,+got):\n%s", diff) 470 } 471 } 472 }) 473 } 474 }