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  }