sigs.k8s.io/kueue@v0.6.2/pkg/queue/cluster_queue_strict_fifo_test.go (about) 1 /* 2 Copyright 2022 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 queue 18 19 import ( 20 "testing" 21 "time" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/utils/ptr" 25 26 config "sigs.k8s.io/kueue/apis/config/v1beta1" 27 kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1" 28 utiltesting "sigs.k8s.io/kueue/pkg/util/testing" 29 "sigs.k8s.io/kueue/pkg/workload" 30 ) 31 32 const ( 33 lowPriority int32 = 0 34 highPriority int32 = 1000 35 ) 36 37 func TestFIFOClusterQueue(t *testing.T) { 38 q, err := newClusterQueue( 39 &kueue.ClusterQueue{ 40 Spec: kueue.ClusterQueueSpec{ 41 QueueingStrategy: kueue.StrictFIFO, 42 }, 43 }, 44 workload.Ordering{ 45 PodsReadyRequeuingTimestamp: config.EvictionTimestamp, 46 }) 47 if err != nil { 48 t.Fatalf("Failed creating ClusterQueue %v", err) 49 } 50 now := metav1.Now() 51 ws := []*kueue.Workload{ 52 { 53 ObjectMeta: metav1.ObjectMeta{ 54 Name: "now", 55 CreationTimestamp: now, 56 }, 57 }, 58 { 59 ObjectMeta: metav1.ObjectMeta{ 60 Name: "before", 61 CreationTimestamp: metav1.NewTime(now.Add(-time.Second)), 62 }, 63 }, 64 { 65 ObjectMeta: metav1.ObjectMeta{ 66 Name: "after", 67 CreationTimestamp: metav1.NewTime(now.Add(time.Second)), 68 }, 69 }, 70 } 71 for _, w := range ws { 72 q.PushOrUpdate(workload.NewInfo(w)) 73 } 74 got := q.Pop() 75 if got == nil { 76 t.Fatal("Queue is empty") 77 } 78 if got.Obj.Name != "before" { 79 t.Errorf("Popped workload %q want %q", got.Obj.Name, "before") 80 } 81 wlInfo := workload.NewInfo(&kueue.Workload{ 82 ObjectMeta: metav1.ObjectMeta{ 83 Name: "after", 84 CreationTimestamp: metav1.NewTime(now.Add(-time.Minute)), 85 }, 86 }) 87 q.PushOrUpdate(wlInfo) 88 got = q.Pop() 89 if got == nil { 90 t.Fatal("Queue is empty") 91 } 92 if got.Obj.Name != "after" { 93 t.Errorf("Popped workload %q want %q", got.Obj.Name, "after") 94 } 95 96 q.Delete(&kueue.Workload{ 97 ObjectMeta: metav1.ObjectMeta{Name: "now"}, 98 }) 99 got = q.Pop() 100 if got != nil { 101 t.Errorf("Queue is not empty, popped workload %q", got.Obj.Name) 102 } 103 } 104 105 func TestStrictFIFO(t *testing.T) { 106 t1 := time.Now() 107 t2 := t1.Add(time.Second) 108 t3 := t2.Add(time.Second) 109 for _, tt := range []struct { 110 name string 111 w1 *kueue.Workload 112 w2 *kueue.Workload 113 workloadOrdering *workload.Ordering 114 expected string 115 }{ 116 { 117 name: "w1.priority is higher than w2.priority", 118 w1: &kueue.Workload{ 119 ObjectMeta: metav1.ObjectMeta{ 120 Name: "w1", 121 CreationTimestamp: metav1.NewTime(t1), 122 }, 123 Spec: kueue.WorkloadSpec{ 124 PriorityClassName: "highPriority", 125 Priority: ptr.To(highPriority), 126 }, 127 }, 128 w2: &kueue.Workload{ 129 ObjectMeta: metav1.ObjectMeta{ 130 Name: "w2", 131 CreationTimestamp: metav1.NewTime(t2), 132 }, 133 Spec: kueue.WorkloadSpec{ 134 PriorityClassName: "lowPriority", 135 Priority: ptr.To(lowPriority), 136 }, 137 }, 138 expected: "w1", 139 }, 140 { 141 name: "w1.priority equals w2.priority and w1.create time is earlier than w2.create time", 142 w1: &kueue.Workload{ 143 ObjectMeta: metav1.ObjectMeta{ 144 Name: "w1", 145 CreationTimestamp: metav1.NewTime(t1), 146 }, 147 }, 148 w2: &kueue.Workload{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Name: "w2", 151 CreationTimestamp: metav1.NewTime(t2), 152 }, 153 }, 154 expected: "w1", 155 }, 156 { 157 name: "w1.priority equals w2.priority and w1.create time is earlier than w2.create time but w1 was evicted", 158 w1: &kueue.Workload{ 159 ObjectMeta: metav1.ObjectMeta{ 160 Name: "w1", 161 CreationTimestamp: metav1.NewTime(t1), 162 }, 163 Status: kueue.WorkloadStatus{ 164 Conditions: []metav1.Condition{ 165 { 166 Type: kueue.WorkloadEvicted, 167 Status: metav1.ConditionTrue, 168 LastTransitionTime: metav1.NewTime(t3), 169 Reason: kueue.WorkloadEvictedByPodsReadyTimeout, 170 Message: "by test", 171 }, 172 }, 173 }, 174 }, 175 w2: &kueue.Workload{ 176 ObjectMeta: metav1.ObjectMeta{ 177 Name: "w2", 178 CreationTimestamp: metav1.NewTime(t2), 179 }, 180 }, 181 expected: "w2", 182 }, 183 { 184 name: "w1.priority equals w2.priority and w1.create time is earlier than w2.create time and w1 was evicted but kueue is configured to always use the creation timestamp", 185 w1: &kueue.Workload{ 186 ObjectMeta: metav1.ObjectMeta{ 187 Name: "w1", 188 CreationTimestamp: metav1.NewTime(t1), 189 }, 190 Status: kueue.WorkloadStatus{ 191 Conditions: []metav1.Condition{ 192 { 193 Type: kueue.WorkloadEvicted, 194 Status: metav1.ConditionTrue, 195 LastTransitionTime: metav1.NewTime(t3), 196 Reason: kueue.WorkloadEvictedByPodsReadyTimeout, 197 Message: "by test", 198 }, 199 }, 200 }, 201 }, 202 w2: &kueue.Workload{ 203 ObjectMeta: metav1.ObjectMeta{ 204 Name: "w2", 205 CreationTimestamp: metav1.NewTime(t2), 206 }, 207 }, 208 workloadOrdering: &workload.Ordering{ 209 PodsReadyRequeuingTimestamp: config.CreationTimestamp, 210 }, 211 expected: "w1", 212 }, 213 { 214 name: "p1.priority is lower than p2.priority and w1.create time is earlier than w2.create time", 215 w1: &kueue.Workload{ 216 ObjectMeta: metav1.ObjectMeta{ 217 Name: "w1", 218 CreationTimestamp: metav1.NewTime(t1), 219 }, 220 Spec: kueue.WorkloadSpec{ 221 PriorityClassName: "lowPriority", 222 Priority: ptr.To(lowPriority), 223 }, 224 }, 225 w2: &kueue.Workload{ 226 ObjectMeta: metav1.ObjectMeta{ 227 Name: "w2", 228 CreationTimestamp: metav1.NewTime(t2), 229 }, 230 Spec: kueue.WorkloadSpec{ 231 PriorityClassName: "highPriority", 232 Priority: ptr.To(highPriority), 233 }, 234 }, 235 expected: "w2", 236 }, 237 } { 238 t.Run(tt.name, func(t *testing.T) { 239 if tt.workloadOrdering == nil { 240 // The default ordering: 241 tt.workloadOrdering = &workload.Ordering{PodsReadyRequeuingTimestamp: config.EvictionTimestamp} 242 } 243 q, err := newClusterQueue( 244 &kueue.ClusterQueue{ 245 Spec: kueue.ClusterQueueSpec{ 246 QueueingStrategy: kueue.StrictFIFO, 247 }, 248 }, 249 *tt.workloadOrdering) 250 if err != nil { 251 t.Fatalf("Failed creating ClusterQueue %v", err) 252 } 253 254 q.PushOrUpdate(workload.NewInfo(tt.w1)) 255 q.PushOrUpdate(workload.NewInfo(tt.w2)) 256 257 got := q.Pop() 258 if got == nil { 259 t.Fatal("Queue is empty") 260 } 261 if got.Obj.Name != tt.expected { 262 t.Errorf("Popped workload %q want %q", got.Obj.Name, tt.expected) 263 } 264 }) 265 } 266 } 267 268 func TestStrictFIFORequeueIfNotPresent(t *testing.T) { 269 tests := map[RequeueReason]struct { 270 wantInadmissible bool 271 }{ 272 RequeueReasonFailedAfterNomination: { 273 wantInadmissible: false, 274 }, 275 RequeueReasonNamespaceMismatch: { 276 wantInadmissible: true, 277 }, 278 RequeueReasonGeneric: { 279 wantInadmissible: false, 280 }, 281 } 282 283 for reason, test := range tests { 284 t.Run(string(reason), func(t *testing.T) { 285 cq, _ := newClusterQueueStrictFIFO( 286 &kueue.ClusterQueue{ 287 Spec: kueue.ClusterQueueSpec{ 288 QueueingStrategy: kueue.StrictFIFO, 289 }, 290 }, 291 workload.Ordering{PodsReadyRequeuingTimestamp: config.EvictionTimestamp}, 292 ) 293 wl := utiltesting.MakeWorkload("workload-1", defaultNamespace).Obj() 294 if ok := cq.RequeueIfNotPresent(workload.NewInfo(wl), reason); !ok { 295 t.Error("failed to requeue nonexistent workload") 296 } 297 298 _, gotInadmissible := cq.(*ClusterQueueStrictFIFO).inadmissibleWorkloads[workload.Key(wl)] 299 if test.wantInadmissible != gotInadmissible { 300 t.Errorf("Got inadmissible after requeue %t, want %t", gotInadmissible, test.wantInadmissible) 301 } 302 303 if ok := cq.RequeueIfNotPresent(workload.NewInfo(wl), reason); ok { 304 t.Error("Re-queued a workload that was already present") 305 } 306 }) 307 } 308 }