sigs.k8s.io/kueue@v0.6.2/test/integration/webhook/resourceflavor_test.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 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 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package webhook 15 16 import ( 17 "fmt" 18 19 "github.com/onsi/ginkgo/v2" 20 "github.com/onsi/gomega" 21 corev1 "k8s.io/api/core/v1" 22 "k8s.io/apimachinery/pkg/api/errors" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "sigs.k8s.io/controller-runtime/pkg/client" 25 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 26 27 kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1" 28 "sigs.k8s.io/kueue/pkg/util/testing" 29 "sigs.k8s.io/kueue/test/util" 30 ) 31 32 const ( 33 nodeSelectorMaxProperties = 8 34 taintsMaxItems = 8 35 ) 36 37 var _ = ginkgo.Describe("ResourceFlavor Webhook", func() { 38 var ns *corev1.Namespace 39 40 ginkgo.BeforeEach(func() { 41 ns = &corev1.Namespace{ 42 ObjectMeta: metav1.ObjectMeta{ 43 GenerateName: "core-", 44 }, 45 } 46 gomega.Expect(k8sClient.Create(ctx, ns)).To(gomega.Succeed()) 47 }) 48 49 ginkgo.AfterEach(func() { 50 gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed()) 51 }) 52 53 ginkgo.When("Creating a ResourceFlavor", func() { 54 ginkgo.It("Should have a finalizer", func() { 55 ginkgo.By("Creating a new resourceFlavor") 56 resourceFlavor := testing.MakeResourceFlavor("resource-flavor").Obj() 57 gomega.Expect(k8sClient.Create(ctx, resourceFlavor)).Should(gomega.Succeed()) 58 defer func() { 59 var rf kueue.ResourceFlavor 60 gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(resourceFlavor), &rf)).Should(gomega.Succeed()) 61 controllerutil.RemoveFinalizer(&rf, kueue.ResourceInUseFinalizerName) 62 gomega.Expect(k8sClient.Update(ctx, &rf)).Should(gomega.Succeed()) 63 util.ExpectResourceFlavorToBeDeleted(ctx, k8sClient, resourceFlavor, true) 64 }() 65 66 var created kueue.ResourceFlavor 67 gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(resourceFlavor), &created)).Should(gomega.Succeed()) 68 gomega.Expect(created.GetFinalizers()).Should(gomega.Equal([]string{kueue.ResourceInUseFinalizerName})) 69 }) 70 }) 71 72 ginkgo.When("Creating a ResourceFlavor with invalid taints", func() { 73 ginkgo.It("Should fail to create", func() { 74 ginkgo.By("Creating a new resourceFlavor") 75 resourceFlavor := testing.MakeResourceFlavor("resource-flavor").Taint(corev1.Taint{ 76 Key: "@foo", 77 Value: "bar", 78 Effect: corev1.TaintEffectNoSchedule, 79 }).Obj() 80 err := k8sClient.Create(ctx, resourceFlavor) 81 gomega.Expect(err).To(gomega.HaveOccurred()) 82 gomega.Expect(errors.IsForbidden(err)).To(gomega.BeTrue(), "error: %v", err) 83 }) 84 }) 85 86 ginkgo.DescribeTable("invalid number of properties", func(taintsCount int, nodeSelectorCount int, isInvalid bool) { 87 rf := testing.MakeResourceFlavor("resource-flavor") 88 for i := 0; i < taintsCount; i++ { 89 rf = rf.Taint(corev1.Taint{ 90 Key: fmt.Sprintf("t%d", i), 91 Effect: corev1.TaintEffectNoExecute, 92 }) 93 } 94 for i := 0; i < nodeSelectorCount; i++ { 95 rf = rf.Label(fmt.Sprintf("l%d", i), "") 96 } 97 resourceFlavor := rf.Obj() 98 err := k8sClient.Create(ctx, resourceFlavor) 99 if isInvalid { 100 gomega.Expect(err).To(gomega.HaveOccurred()) 101 gomega.Expect(errors.IsInvalid(err)).To(gomega.BeTrue(), "error: %v", err) 102 } else { 103 gomega.Expect(err).To(gomega.Succeed()) 104 defer func() { 105 var rf kueue.ResourceFlavor 106 gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(resourceFlavor), &rf)).To(gomega.Succeed()) 107 controllerutil.RemoveFinalizer(&rf, kueue.ResourceInUseFinalizerName) 108 gomega.Expect(k8sClient.Update(ctx, &rf)).Should(gomega.Succeed()) 109 util.ExpectResourceFlavorToBeDeleted(ctx, k8sClient, resourceFlavor, true) 110 }() 111 } 112 }, 113 ginkgo.Entry("invalid number of taint", taintsMaxItems+1, 0, true), 114 ginkgo.Entry("invalid number of nodeSelector", 0, nodeSelectorMaxProperties+1, true), 115 ginkgo.Entry("valid number of nodeSelector and taint", taintsMaxItems, nodeSelectorMaxProperties, false), 116 ) 117 118 ginkgo.When("Updating a ResourceFlavor with invalid taints", func() { 119 ginkgo.It("Should fail to update", func() { 120 ginkgo.By("Creating a new resourceFlavor") 121 resourceFlavor := testing.MakeResourceFlavor("resource-flavor").Obj() 122 gomega.Expect(k8sClient.Create(ctx, resourceFlavor)).Should(gomega.Succeed()) 123 defer func() { 124 var rf kueue.ResourceFlavor 125 gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(resourceFlavor), &rf)).To(gomega.Succeed()) 126 controllerutil.RemoveFinalizer(&rf, kueue.ResourceInUseFinalizerName) 127 gomega.Expect(k8sClient.Update(ctx, &rf)).Should(gomega.Succeed()) 128 util.ExpectResourceFlavorToBeDeleted(ctx, k8sClient, resourceFlavor, true) 129 }() 130 131 var created kueue.ResourceFlavor 132 gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(resourceFlavor), &created)).To(gomega.Succeed()) 133 created.Spec.NodeTaints = []corev1.Taint{{ 134 Key: "foo", 135 Value: "bar", 136 Effect: "Invalid", 137 }} 138 139 ginkgo.By("Updating the resourceFlavor with invalid labels") 140 err := k8sClient.Update(ctx, &created) 141 gomega.Expect(err).To(gomega.HaveOccurred()) 142 gomega.Expect(errors.IsForbidden(err)).To(gomega.BeTrue(), "error: %v", err) 143 }) 144 }) 145 })