sigs.k8s.io/kueue@v0.6.2/pkg/visibility/api/rest/pending_workloads_cq_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 28 kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1" 29 visibility "sigs.k8s.io/kueue/apis/visibility/v1alpha1" 30 "sigs.k8s.io/kueue/pkg/constants" 31 "sigs.k8s.io/kueue/pkg/queue" 32 utiltesting "sigs.k8s.io/kueue/pkg/util/testing" 33 ) 34 35 func TestPendingWorkloadsInCQ(t *testing.T) { 36 const ( 37 nsName = "foo" 38 cqNameA = "cqA" 39 cqNameB = "cqB" 40 lqNameA = "lqA" 41 lqNameB = "lqB" 42 lowPrio = 50 43 highPrio = 100 44 ) 45 46 var ( 47 defaultQueryParams = &visibility.PendingWorkloadOptions{ 48 Offset: 0, 49 Limit: constants.DefaultPendingWorkloadsLimit, 50 } 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, nsName).ClusterQueue(cqNameA).Obj(), 76 }, 77 workloads: []*kueue.Workload{ 78 utiltesting.MakeWorkload("a", nsName).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 79 utiltesting.MakeWorkload("b", nsName).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 80 }, 81 req: &req{ 82 queueName: cqNameA, 83 queryParams: defaultQueryParams, 84 }, 85 wantResp: &resp{ 86 wantPendingWorkloads: []visibility.PendingWorkload{ 87 { 88 ObjectMeta: v1.ObjectMeta{ 89 Name: "a", 90 Namespace: nsName, 91 CreationTimestamp: v1.NewTime(now), 92 }, 93 LocalQueueName: lqNameA, 94 Priority: highPrio, 95 PositionInClusterQueue: 0, 96 PositionInLocalQueue: 0, 97 }, 98 { 99 ObjectMeta: v1.ObjectMeta{ 100 Name: "b", 101 Namespace: nsName, 102 CreationTimestamp: v1.NewTime(now), 103 }, 104 LocalQueueName: lqNameA, 105 Priority: lowPrio, 106 PositionInClusterQueue: 1, 107 PositionInLocalQueue: 1, 108 }}, 109 }, 110 }, 111 "single ClusterQueue and two LocalQueue setup with four workloads and default query parameters": { 112 clusterQueues: []*kueue.ClusterQueue{ 113 utiltesting.MakeClusterQueue(cqNameA).Obj(), 114 }, 115 queues: []*kueue.LocalQueue{ 116 utiltesting.MakeLocalQueue(lqNameA, nsName).ClusterQueue(cqNameA).Obj(), 117 utiltesting.MakeLocalQueue(lqNameB, nsName).ClusterQueue(cqNameA).Obj(), 118 }, 119 workloads: []*kueue.Workload{ 120 utiltesting.MakeWorkload("lqA-high-prio", nsName).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 121 utiltesting.MakeWorkload("lqA-low-prio", nsName).Queue(lqNameA).Priority(lowPrio).Creation(now).Obj(), 122 utiltesting.MakeWorkload("lqB-high-prio", nsName).Queue(lqNameB).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 123 utiltesting.MakeWorkload("lqB-low-prio", nsName).Queue(lqNameB).Priority(lowPrio).Creation(now.Add(time.Second)).Obj(), 124 }, 125 req: &req{ 126 queueName: cqNameA, 127 queryParams: defaultQueryParams, 128 }, 129 wantResp: &resp{ 130 wantPendingWorkloads: []visibility.PendingWorkload{ 131 { 132 ObjectMeta: v1.ObjectMeta{ 133 Name: "lqA-high-prio", 134 Namespace: nsName, 135 CreationTimestamp: v1.NewTime(now), 136 }, 137 LocalQueueName: lqNameA, 138 Priority: highPrio, 139 PositionInClusterQueue: 0, 140 PositionInLocalQueue: 0, 141 }, 142 { 143 ObjectMeta: v1.ObjectMeta{ 144 Name: "lqB-high-prio", 145 Namespace: nsName, 146 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 147 }, 148 LocalQueueName: lqNameB, 149 Priority: highPrio, 150 PositionInClusterQueue: 1, 151 PositionInLocalQueue: 0, 152 }, 153 { 154 ObjectMeta: v1.ObjectMeta{ 155 Name: "lqA-low-prio", 156 Namespace: nsName, 157 CreationTimestamp: v1.NewTime(now), 158 }, 159 LocalQueueName: lqNameA, 160 Priority: lowPrio, 161 PositionInClusterQueue: 2, 162 PositionInLocalQueue: 1, 163 }, 164 { 165 ObjectMeta: v1.ObjectMeta{ 166 Name: "lqB-low-prio", 167 Namespace: nsName, 168 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 169 }, 170 LocalQueueName: lqNameB, 171 Priority: lowPrio, 172 PositionInClusterQueue: 3, 173 PositionInLocalQueue: 1, 174 }}, 175 }, 176 }, 177 "limit query parameter set": { 178 clusterQueues: []*kueue.ClusterQueue{ 179 utiltesting.MakeClusterQueue(cqNameA).Obj(), 180 }, 181 queues: []*kueue.LocalQueue{ 182 utiltesting.MakeLocalQueue(lqNameA, nsName).ClusterQueue(cqNameA).Obj(), 183 }, 184 workloads: []*kueue.Workload{ 185 utiltesting.MakeWorkload("a", nsName).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 186 utiltesting.MakeWorkload("b", nsName).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 187 utiltesting.MakeWorkload("c", nsName).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second * 2)).Obj(), 188 }, 189 req: &req{ 190 queueName: cqNameA, 191 queryParams: &visibility.PendingWorkloadOptions{ 192 Limit: 2, 193 }, 194 }, 195 wantResp: &resp{ 196 wantPendingWorkloads: []visibility.PendingWorkload{ 197 { 198 ObjectMeta: v1.ObjectMeta{ 199 Name: "a", 200 Namespace: nsName, 201 CreationTimestamp: v1.NewTime(now), 202 }, 203 LocalQueueName: lqNameA, 204 Priority: highPrio, 205 PositionInClusterQueue: 0, 206 PositionInLocalQueue: 0, 207 }, 208 { 209 ObjectMeta: v1.ObjectMeta{ 210 Name: "b", 211 Namespace: nsName, 212 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 213 }, 214 LocalQueueName: lqNameA, 215 Priority: highPrio, 216 PositionInClusterQueue: 1, 217 PositionInLocalQueue: 1, 218 }}, 219 }, 220 }, 221 "offset query parameter set": { 222 clusterQueues: []*kueue.ClusterQueue{ 223 utiltesting.MakeClusterQueue(cqNameA).Obj(), 224 }, 225 queues: []*kueue.LocalQueue{ 226 utiltesting.MakeLocalQueue(lqNameA, nsName).ClusterQueue(cqNameA).Obj(), 227 }, 228 workloads: []*kueue.Workload{ 229 utiltesting.MakeWorkload("a", nsName).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 230 utiltesting.MakeWorkload("b", nsName).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 231 utiltesting.MakeWorkload("c", nsName).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second * 2)).Obj(), 232 }, 233 req: &req{ 234 queueName: cqNameA, 235 queryParams: &visibility.PendingWorkloadOptions{ 236 Offset: 1, 237 Limit: constants.DefaultPendingWorkloadsLimit, 238 }, 239 }, 240 wantResp: &resp{ 241 wantPendingWorkloads: []visibility.PendingWorkload{ 242 { 243 ObjectMeta: v1.ObjectMeta{ 244 Name: "b", 245 Namespace: nsName, 246 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 247 }, 248 LocalQueueName: lqNameA, 249 Priority: highPrio, 250 PositionInClusterQueue: 1, 251 PositionInLocalQueue: 1, 252 }, 253 { 254 ObjectMeta: v1.ObjectMeta{ 255 Name: "c", 256 Namespace: nsName, 257 CreationTimestamp: v1.NewTime(now.Add(time.Second * 2)), 258 }, 259 LocalQueueName: lqNameA, 260 Priority: highPrio, 261 PositionInClusterQueue: 2, 262 PositionInLocalQueue: 2, 263 }}, 264 }, 265 }, 266 "limit offset query parameters set": { 267 clusterQueues: []*kueue.ClusterQueue{ 268 utiltesting.MakeClusterQueue(cqNameA).Obj(), 269 }, 270 queues: []*kueue.LocalQueue{ 271 utiltesting.MakeLocalQueue(lqNameA, nsName).ClusterQueue(cqNameA).Obj(), 272 }, 273 workloads: []*kueue.Workload{ 274 utiltesting.MakeWorkload("a", nsName).Queue(lqNameA).Priority(highPrio).Creation(now).Obj(), 275 utiltesting.MakeWorkload("b", nsName).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second)).Obj(), 276 utiltesting.MakeWorkload("c", nsName).Queue(lqNameA).Priority(highPrio).Creation(now.Add(time.Second * 2)).Obj(), 277 }, 278 req: &req{ 279 queueName: cqNameA, 280 queryParams: &visibility.PendingWorkloadOptions{ 281 Offset: 1, 282 Limit: 1, 283 }, 284 }, 285 wantResp: &resp{ 286 wantPendingWorkloads: []visibility.PendingWorkload{ 287 { 288 ObjectMeta: v1.ObjectMeta{ 289 Name: "b", 290 Namespace: nsName, 291 CreationTimestamp: v1.NewTime(now.Add(time.Second)), 292 }, 293 LocalQueueName: lqNameA, 294 Priority: highPrio, 295 PositionInClusterQueue: 1, 296 PositionInLocalQueue: 1, 297 }}, 298 }, 299 }, 300 "empty cluster queue": { 301 clusterQueues: []*kueue.ClusterQueue{ 302 utiltesting.MakeClusterQueue(cqNameA).Obj(), 303 }, 304 req: &req{ 305 queueName: cqNameA, 306 queryParams: defaultQueryParams, 307 }, 308 wantResp: &resp{}, 309 }, 310 "nonexistent queue name": { 311 req: &req{ 312 queueName: "nonexistent-queue", 313 queryParams: defaultQueryParams, 314 }, 315 wantResp: &resp{ 316 wantErr: errors.NewNotFound(visibility.Resource("clusterqueue"), "nonexistent-queue"), 317 }, 318 wantErrMatch: errors.IsNotFound, 319 }, 320 } 321 for name, tc := range cases { 322 t.Run(name, func(t *testing.T) { 323 manager := queue.NewManager(utiltesting.NewFakeClient(), nil) 324 ctx, cancel := context.WithCancel(context.Background()) 325 defer cancel() 326 go manager.CleanUpOnContext(ctx) 327 pendingWorkloadsInCqRest := NewPendingWorkloadsInCqREST(manager) 328 for _, cq := range tc.clusterQueues { 329 if err := manager.AddClusterQueue(ctx, cq); err != nil { 330 t.Fatalf("Adding cluster queue %s: %v", cq.Name, err) 331 } 332 } 333 for _, q := range tc.queues { 334 if err := manager.AddLocalQueue(ctx, q); err != nil { 335 t.Fatalf("Adding queue %q: %v", q.Name, err) 336 } 337 } 338 for _, w := range tc.workloads { 339 manager.AddOrUpdateWorkload(w) 340 } 341 342 info, err := pendingWorkloadsInCqRest.Get(ctx, tc.req.queueName, tc.req.queryParams) 343 if tc.wantErrMatch != nil { 344 if !tc.wantErrMatch(err) { 345 t.Errorf("Error differs: (-want,+got):\n%s", cmp.Diff(tc.wantResp.wantErr.Error(), err.Error())) 346 } 347 } else if err != nil { 348 t.Error(err) 349 } else { 350 pendingWorkloadsInfo := info.(*visibility.PendingWorkloadsSummary) 351 if diff := cmp.Diff(tc.wantResp.wantPendingWorkloads, pendingWorkloadsInfo.Items, cmpopts.EquateEmpty()); diff != "" { 352 t.Errorf("Pending workloads differ: (-want,+got):\n%s", diff) 353 } 354 } 355 }) 356 } 357 }