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  }