sigs.k8s.io/kueue@v0.6.2/test/integration/controller/core/localqueue_controller_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 core
    18  
    19  import (
    20  	"github.com/onsi/ginkgo/v2"
    21  	"github.com/onsi/gomega"
    22  	corev1 "k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/api/resource"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"sigs.k8s.io/controller-runtime/pkg/client"
    26  
    27  	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
    28  	"sigs.k8s.io/kueue/pkg/util/testing"
    29  	"sigs.k8s.io/kueue/test/integration/framework"
    30  	"sigs.k8s.io/kueue/test/util"
    31  )
    32  
    33  // +kubebuilder:docs-gen:collapse=Imports
    34  
    35  var _ = ginkgo.Describe("Queue controller", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
    36  	const (
    37  		flavorModelC = "model-c"
    38  		flavorModelD = "model-d"
    39  	)
    40  	var (
    41  		ns              *corev1.Namespace
    42  		queue           *kueue.LocalQueue
    43  		clusterQueue    *kueue.ClusterQueue
    44  		resourceFlavors = []kueue.ResourceFlavor{
    45  			*testing.MakeResourceFlavor(flavorModelC).Label(resourceGPU.String(), flavorModelC).Obj(),
    46  			*testing.MakeResourceFlavor(flavorModelD).Label(resourceGPU.String(), flavorModelD).Obj(),
    47  		}
    48  		ac *kueue.AdmissionCheck
    49  	)
    50  
    51  	ginkgo.BeforeAll(func() {
    52  		fwk = &framework.Framework{CRDPath: crdPath, WebhookPath: webhookPath}
    53  		cfg = fwk.Init()
    54  		ctx, k8sClient = fwk.RunManager(cfg, managerSetup)
    55  	})
    56  	ginkgo.AfterAll(func() {
    57  		fwk.Teardown()
    58  	})
    59  
    60  	ginkgo.BeforeEach(func() {
    61  		ns = &corev1.Namespace{
    62  			ObjectMeta: metav1.ObjectMeta{
    63  				GenerateName: "core-queue-",
    64  			},
    65  		}
    66  		gomega.Expect(k8sClient.Create(ctx, ns)).To(gomega.Succeed())
    67  	})
    68  
    69  	ginkgo.BeforeEach(func() {
    70  		ac = testing.MakeAdmissionCheck("ac").ControllerName("ac-controller").Obj()
    71  		gomega.Expect(k8sClient.Create(ctx, ac)).To(gomega.Succeed())
    72  		util.SetAdmissionCheckActive(ctx, k8sClient, ac, metav1.ConditionTrue)
    73  
    74  		clusterQueue = testing.MakeClusterQueue("cluster-queue.queue-controller").
    75  			ResourceGroup(
    76  				*testing.MakeFlavorQuotas(flavorModelC).Resource(resourceGPU, "5", "5").Obj(),
    77  				*testing.MakeFlavorQuotas(flavorModelD).Resource(resourceGPU, "5", "5").Obj(),
    78  			).
    79  			Cohort("cohort").
    80  			AdmissionChecks(ac.Name).
    81  			Obj()
    82  		queue = testing.MakeLocalQueue("queue", ns.Name).ClusterQueue(clusterQueue.Name).Obj()
    83  		gomega.Expect(k8sClient.Create(ctx, queue)).To(gomega.Succeed())
    84  	})
    85  
    86  	ginkgo.AfterEach(func() {
    87  		gomega.Expect(util.DeleteLocalQueue(ctx, k8sClient, queue)).To(gomega.Succeed())
    88  		util.ExpectClusterQueueToBeDeleted(ctx, k8sClient, clusterQueue, true)
    89  		for _, rf := range resourceFlavors {
    90  			util.ExpectResourceFlavorToBeDeleted(ctx, k8sClient, &rf, true)
    91  		}
    92  		util.ExpectAdmissionCheckToBeDeleted(ctx, k8sClient, ac, true)
    93  	})
    94  
    95  	ginkgo.It("Should update conditions when clusterQueues that its localQueue references are updated", func() {
    96  		gomega.Eventually(func() []metav1.Condition {
    97  			var updatedQueue kueue.LocalQueue
    98  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
    99  			return updatedQueue.Status.Conditions
   100  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo([]metav1.Condition{
   101  			{
   102  				Type:    kueue.LocalQueueActive,
   103  				Status:  metav1.ConditionFalse,
   104  				Reason:  "ClusterQueueDoesNotExist",
   105  				Message: "Can't submit new workloads to clusterQueue",
   106  			},
   107  		}, ignoreConditionTimestamps))
   108  
   109  		ginkgo.By("Creating a clusterQueue")
   110  		gomega.Expect(k8sClient.Create(ctx, clusterQueue)).To(gomega.Succeed())
   111  		gomega.Eventually(func() []metav1.Condition {
   112  			var updatedQueue kueue.LocalQueue
   113  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   114  			return updatedQueue.Status.Conditions
   115  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo([]metav1.Condition{
   116  			{
   117  				Type:    kueue.LocalQueueActive,
   118  				Status:  metav1.ConditionFalse,
   119  				Reason:  "ClusterQueueIsInactive",
   120  				Message: "Can't submit new workloads to clusterQueue",
   121  			},
   122  		}, ignoreConditionTimestamps))
   123  
   124  		ginkgo.By("Creating resourceFlavors")
   125  		for _, rf := range resourceFlavors {
   126  			gomega.Expect(k8sClient.Create(ctx, &rf)).To(gomega.Succeed())
   127  		}
   128  		gomega.Eventually(func() []metav1.Condition {
   129  			var updatedCQ kueue.ClusterQueue
   130  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(clusterQueue), &updatedCQ)).To(gomega.Succeed())
   131  			return updatedCQ.Status.Conditions
   132  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo([]metav1.Condition{
   133  			{
   134  				Type:    kueue.ClusterQueueActive,
   135  				Status:  metav1.ConditionTrue,
   136  				Reason:  "Ready",
   137  				Message: "Can admit new workloads",
   138  			},
   139  		}, ignoreConditionTimestamps))
   140  		gomega.Eventually(func() []metav1.Condition {
   141  			var updatedQueue kueue.LocalQueue
   142  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   143  			return updatedQueue.Status.Conditions
   144  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo([]metav1.Condition{
   145  			{
   146  				Type:    kueue.LocalQueueActive,
   147  				Status:  metav1.ConditionTrue,
   148  				Reason:  "Ready",
   149  				Message: "Can submit new workloads to clusterQueue",
   150  			},
   151  		}, ignoreConditionTimestamps))
   152  
   153  		ginkgo.By("Deleting a clusterQueue")
   154  		gomega.Expect(k8sClient.Delete(ctx, clusterQueue)).To(gomega.Succeed())
   155  		gomega.Eventually(func() []metav1.Condition {
   156  			var updatedQueue kueue.LocalQueue
   157  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   158  			return updatedQueue.Status.Conditions
   159  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo([]metav1.Condition{
   160  			{
   161  				Type:    kueue.LocalQueueActive,
   162  				Status:  metav1.ConditionFalse,
   163  				Reason:  "ClusterQueueDoesNotExist",
   164  				Message: "Can't submit new workloads to clusterQueue",
   165  			},
   166  		}, ignoreConditionTimestamps))
   167  	})
   168  
   169  	ginkgo.It("Should update status when workloads are created", func() {
   170  		ginkgo.By("Creating resourceFlavors")
   171  		for _, rf := range resourceFlavors {
   172  			gomega.Expect(k8sClient.Create(ctx, &rf)).To(gomega.Succeed())
   173  		}
   174  		ginkgo.By("Creating a clusterQueue")
   175  		gomega.Expect(k8sClient.Create(ctx, clusterQueue)).To(gomega.Succeed())
   176  
   177  		workloads := []*kueue.Workload{
   178  			testing.MakeWorkload("one", ns.Name).
   179  				Queue(queue.Name).
   180  				Request(resourceGPU, "2").
   181  				Obj(),
   182  			testing.MakeWorkload("two", ns.Name).
   183  				Queue(queue.Name).
   184  				Request(resourceGPU, "3").
   185  				Obj(),
   186  			testing.MakeWorkload("three", ns.Name).
   187  				Queue(queue.Name).
   188  				Request(resourceGPU, "1").
   189  				Obj(),
   190  		}
   191  		admissions := []*kueue.Admission{
   192  			testing.MakeAdmission(clusterQueue.Name).
   193  				Assignment(resourceGPU, flavorModelC, "2").Obj(),
   194  			testing.MakeAdmission(clusterQueue.Name).
   195  				Assignment(resourceGPU, flavorModelC, "3").Obj(),
   196  			testing.MakeAdmission(clusterQueue.Name).
   197  				Assignment(resourceGPU, flavorModelD, "1").Obj(),
   198  		}
   199  
   200  		ginkgo.By("Creating workloads")
   201  		for _, w := range workloads {
   202  			gomega.Expect(k8sClient.Create(ctx, w)).To(gomega.Succeed())
   203  		}
   204  
   205  		emptyUsage := []kueue.LocalQueueFlavorUsage{
   206  			{
   207  				Name: flavorModelC,
   208  				Resources: []kueue.LocalQueueResourceUsage{
   209  					{
   210  						Name:  resourceGPU,
   211  						Total: resource.MustParse("0"),
   212  					},
   213  				},
   214  			},
   215  			{
   216  				Name: flavorModelD,
   217  				Resources: []kueue.LocalQueueResourceUsage{
   218  					{
   219  						Name:  resourceGPU,
   220  						Total: resource.MustParse("0"),
   221  					},
   222  				},
   223  			},
   224  		}
   225  
   226  		gomega.Eventually(func() kueue.LocalQueueStatus {
   227  			var updatedQueue kueue.LocalQueue
   228  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   229  			return updatedQueue.Status
   230  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo(kueue.LocalQueueStatus{
   231  			ReservingWorkloads: 0,
   232  			AdmittedWorkloads:  0,
   233  			PendingWorkloads:   3,
   234  			Conditions: []metav1.Condition{
   235  				{
   236  					Type:    kueue.LocalQueueActive,
   237  					Status:  metav1.ConditionTrue,
   238  					Reason:  "Ready",
   239  					Message: "Can submit new workloads to clusterQueue",
   240  				},
   241  			},
   242  			FlavorsReservation: emptyUsage,
   243  			FlavorUsage:        emptyUsage,
   244  		}, ignoreConditionTimestamps))
   245  
   246  		ginkgo.By("Setting the workloads quota reservation")
   247  		for i, w := range workloads {
   248  			gomega.Eventually(func() error {
   249  				var newWL kueue.Workload
   250  				gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(w), &newWL)).To(gomega.Succeed())
   251  				return util.SetQuotaReservation(ctx, k8sClient, &newWL, admissions[i])
   252  			}, util.Timeout, util.Interval).Should(gomega.Succeed())
   253  		}
   254  
   255  		fullUsage := []kueue.LocalQueueFlavorUsage{
   256  			{
   257  				Name: flavorModelC,
   258  				Resources: []kueue.LocalQueueResourceUsage{
   259  					{
   260  						Name:  resourceGPU,
   261  						Total: resource.MustParse("5"),
   262  					},
   263  				},
   264  			},
   265  			{
   266  				Name: flavorModelD,
   267  				Resources: []kueue.LocalQueueResourceUsage{
   268  					{
   269  						Name:  resourceGPU,
   270  						Total: resource.MustParse("1"),
   271  					},
   272  				},
   273  			},
   274  		}
   275  
   276  		gomega.Eventually(func() kueue.LocalQueueStatus {
   277  			var updatedQueue kueue.LocalQueue
   278  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   279  			return updatedQueue.Status
   280  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo(kueue.LocalQueueStatus{
   281  			ReservingWorkloads: 3,
   282  			AdmittedWorkloads:  0,
   283  			PendingWorkloads:   0,
   284  			Conditions: []metav1.Condition{
   285  				{
   286  					Type:    kueue.LocalQueueActive,
   287  					Status:  metav1.ConditionTrue,
   288  					Reason:  "Ready",
   289  					Message: "Can submit new workloads to clusterQueue",
   290  				},
   291  			},
   292  			FlavorsReservation: fullUsage,
   293  			FlavorUsage:        emptyUsage,
   294  		}, ignoreConditionTimestamps))
   295  
   296  		ginkgo.By("Setting the workloads admission checks")
   297  		for _, w := range workloads {
   298  			util.SetWorkloadsAdmissionCheck(ctx, k8sClient, w, ac.Name, kueue.CheckStateReady, true)
   299  		}
   300  
   301  		gomega.Eventually(func() kueue.LocalQueueStatus {
   302  			var updatedQueue kueue.LocalQueue
   303  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   304  			return updatedQueue.Status
   305  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo(kueue.LocalQueueStatus{
   306  			ReservingWorkloads: 3,
   307  			AdmittedWorkloads:  3,
   308  			PendingWorkloads:   0,
   309  			Conditions: []metav1.Condition{
   310  				{
   311  					Type:    kueue.LocalQueueActive,
   312  					Status:  metav1.ConditionTrue,
   313  					Reason:  "Ready",
   314  					Message: "Can submit new workloads to clusterQueue",
   315  				},
   316  			},
   317  			FlavorsReservation: fullUsage,
   318  			FlavorUsage:        fullUsage,
   319  		}, ignoreConditionTimestamps))
   320  
   321  		ginkgo.By("Finishing workloads")
   322  		util.FinishWorkloads(ctx, k8sClient, workloads...)
   323  		gomega.Eventually(func() kueue.LocalQueueStatus {
   324  			var updatedQueue kueue.LocalQueue
   325  			gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(queue), &updatedQueue)).To(gomega.Succeed())
   326  			return updatedQueue.Status
   327  		}, util.Timeout, util.Interval).Should(gomega.BeComparableTo(kueue.LocalQueueStatus{
   328  			Conditions: []metav1.Condition{
   329  				{
   330  					Type:    kueue.LocalQueueActive,
   331  					Status:  metav1.ConditionTrue,
   332  					Reason:  "Ready",
   333  					Message: "Can submit new workloads to clusterQueue",
   334  				},
   335  			},
   336  			FlavorsReservation: emptyUsage,
   337  			FlavorUsage:        emptyUsage,
   338  		}, ignoreConditionTimestamps))
   339  	})
   340  })