sigs.k8s.io/kueue@v0.6.2/test/integration/controller/jobs/pod/pod_webhook_test.go (about)

     1  /*
     2  Copyright 2023 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 pod
    18  
    19  import (
    20  	"github.com/onsi/ginkgo/v2"
    21  	"github.com/onsi/gomega"
    22  	corev1 "k8s.io/api/core/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/types"
    25  	"k8s.io/client-go/discovery"
    26  
    27  	configapi "sigs.k8s.io/kueue/apis/config/v1beta1"
    28  	"sigs.k8s.io/kueue/pkg/controller/jobframework"
    29  	"sigs.k8s.io/kueue/pkg/util/kubeversion"
    30  	testingpod "sigs.k8s.io/kueue/pkg/util/testingjobs/pod"
    31  	"sigs.k8s.io/kueue/test/integration/framework"
    32  	"sigs.k8s.io/kueue/test/util"
    33  )
    34  
    35  var _ = ginkgo.Describe("Pod Webhook", func() {
    36  	var ns *corev1.Namespace
    37  
    38  	ginkgo.When("with manageJobsWithoutQueueName disabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
    39  		ginkgo.BeforeAll(func() {
    40  			fwk = &framework.Framework{
    41  				CRDPath:     crdPath,
    42  				WebhookPath: webhookPath,
    43  			}
    44  			cfg = fwk.Init()
    45  
    46  			discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
    47  			gomega.Expect(err).NotTo(gomega.HaveOccurred())
    48  			serverVersionFetcher = kubeversion.NewServerVersionFetcher(discoveryClient)
    49  			err = serverVersionFetcher.FetchServerVersion()
    50  			gomega.Expect(err).NotTo(gomega.HaveOccurred())
    51  
    52  			ctx, k8sClient = fwk.RunManager(cfg, managerSetup(
    53  				jobframework.WithManageJobsWithoutQueueName(false),
    54  				jobframework.WithKubeServerVersion(serverVersionFetcher),
    55  				jobframework.WithIntegrationOptions(corev1.SchemeGroupVersion.WithKind("Pod").String(), &configapi.PodIntegrationOptions{
    56  					PodSelector: &metav1.LabelSelector{},
    57  					NamespaceSelector: &metav1.LabelSelector{
    58  						MatchExpressions: []metav1.LabelSelectorRequirement{
    59  							{
    60  								Key:      "kubernetes.io/metadata.name",
    61  								Operator: metav1.LabelSelectorOpNotIn,
    62  								Values:   []string{"kube-system", "kueue-system"},
    63  							},
    64  						},
    65  					},
    66  				}),
    67  			))
    68  		})
    69  		ginkgo.BeforeEach(func() {
    70  			ns = &corev1.Namespace{
    71  				ObjectMeta: metav1.ObjectMeta{
    72  					GenerateName: "pod-",
    73  				},
    74  			}
    75  			gomega.Expect(k8sClient.Create(ctx, ns)).To(gomega.Succeed())
    76  		})
    77  
    78  		ginkgo.AfterEach(func() {
    79  			gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
    80  		})
    81  		ginkgo.AfterAll(func() {
    82  			fwk.Teardown()
    83  		})
    84  
    85  		ginkgo.When("The queue-name label is set", func() {
    86  			var (
    87  				pod       *corev1.Pod
    88  				lookupKey types.NamespacedName
    89  			)
    90  
    91  			ginkgo.BeforeEach(func() {
    92  				pod = testingpod.MakePod("pod-with-queue-name", ns.Name).Queue("user-queue").Obj()
    93  				lookupKey = types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}
    94  			})
    95  
    96  			ginkgo.It("Should inject scheduling gate, 'managed' label and finalizer into created pod", func() {
    97  				gomega.Expect(k8sClient.Create(ctx, pod)).Should(gomega.Succeed())
    98  
    99  				createdPod := &corev1.Pod{}
   100  				gomega.Eventually(func() error {
   101  					return k8sClient.Get(ctx, lookupKey, createdPod)
   102  				}, util.Timeout, util.Interval).Should(gomega.Succeed())
   103  
   104  				gomega.Expect(createdPod.Spec.SchedulingGates).To(
   105  					gomega.ContainElement(corev1.PodSchedulingGate{Name: "kueue.x-k8s.io/admission"}),
   106  					"Pod should have scheduling gate",
   107  				)
   108  
   109  				gomega.Expect(createdPod.Labels).To(
   110  					gomega.HaveKeyWithValue("kueue.x-k8s.io/managed", "true"),
   111  					"Pod should have the label",
   112  				)
   113  
   114  				gomega.Expect(createdPod.Finalizers).To(gomega.ContainElement("kueue.x-k8s.io/managed"),
   115  					"Pod should have finalizer set")
   116  			})
   117  
   118  			ginkgo.It("Should skip a Pod created in the forbidden 'kube-system' namespace", func() {
   119  				pod.Namespace = "kube-system"
   120  				gomega.Expect(k8sClient.Create(ctx, pod)).Should(gomega.Succeed())
   121  
   122  				lookupKey := types.NamespacedName{Name: pod.Name, Namespace: "kube-system"}
   123  				createdPod := &corev1.Pod{}
   124  				gomega.Eventually(func() error {
   125  					return k8sClient.Get(ctx, lookupKey, createdPod)
   126  				}, util.Timeout, util.Interval).Should(gomega.Succeed())
   127  
   128  				gomega.Expect(createdPod.Spec.SchedulingGates).NotTo(
   129  					gomega.ContainElement(corev1.PodSchedulingGate{Name: "kueue.x-k8s.io/admission"}),
   130  					"Pod shouldn't have scheduling gate",
   131  				)
   132  
   133  				gomega.Expect(createdPod.Labels).NotTo(
   134  					gomega.HaveKeyWithValue("kueue.x-k8s.io/managed", "true"),
   135  					"Pod shouldn't have the label",
   136  				)
   137  
   138  				gomega.Expect(createdPod.Finalizers).NotTo(gomega.ContainElement("kueue.x-k8s.io/managed"),
   139  					"Pod shouldn't have finalizer set")
   140  			})
   141  		})
   142  
   143  		ginkgo.When("The queue-name label is not set", func() {
   144  			var (
   145  				pod       *corev1.Pod
   146  				lookupKey types.NamespacedName
   147  			)
   148  
   149  			ginkgo.BeforeEach(func() {
   150  				pod = testingpod.MakePod("pod-with-queue-name", ns.Name).Obj()
   151  				lookupKey = types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}
   152  			})
   153  
   154  			ginkgo.It("Should not inject scheduling gate, 'managed' label and finalizer into created pod", func() {
   155  				gomega.Expect(k8sClient.Create(ctx, pod)).Should(gomega.Succeed())
   156  
   157  				createdPod := &corev1.Pod{}
   158  				gomega.Eventually(func() error {
   159  					return k8sClient.Get(ctx, lookupKey, createdPod)
   160  				}, util.Timeout, util.Interval).Should(gomega.Succeed())
   161  
   162  				gomega.Expect(createdPod.Spec.SchedulingGates).NotTo(
   163  					gomega.ContainElement(corev1.PodSchedulingGate{Name: "kueue.x-k8s.io/admission"}),
   164  					"Pod shouldn't have scheduling gate",
   165  				)
   166  
   167  				gomega.Expect(createdPod.Labels).NotTo(
   168  					gomega.HaveKeyWithValue("kueue.x-k8s.io/managed", "true"),
   169  					"Pod shouldn't have the label",
   170  				)
   171  
   172  				gomega.Expect(createdPod.Finalizers).NotTo(gomega.ContainElement("kueue.x-k8s.io/managed"),
   173  					"Pod shouldn't have finalizer set")
   174  			})
   175  		})
   176  	})
   177  
   178  	ginkgo.When("with manageJobsWithoutQueueName enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
   179  		ginkgo.BeforeAll(func() {
   180  			fwk = &framework.Framework{
   181  				CRDPath:     crdPath,
   182  				WebhookPath: webhookPath,
   183  			}
   184  			cfg = fwk.Init()
   185  
   186  			discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
   187  			gomega.Expect(err).NotTo(gomega.HaveOccurred())
   188  			serverVersionFetcher = kubeversion.NewServerVersionFetcher(discoveryClient)
   189  			err = serverVersionFetcher.FetchServerVersion()
   190  			gomega.Expect(err).NotTo(gomega.HaveOccurred())
   191  
   192  			ctx, k8sClient = fwk.RunManager(cfg, managerSetup(
   193  				jobframework.WithManageJobsWithoutQueueName(true),
   194  				jobframework.WithKubeServerVersion(serverVersionFetcher),
   195  				jobframework.WithIntegrationOptions(corev1.SchemeGroupVersion.WithKind("Pod").String(), &configapi.PodIntegrationOptions{
   196  					PodSelector: &metav1.LabelSelector{},
   197  					NamespaceSelector: &metav1.LabelSelector{
   198  						MatchExpressions: []metav1.LabelSelectorRequirement{
   199  							{
   200  								Key:      "kubernetes.io/metadata.name",
   201  								Operator: metav1.LabelSelectorOpNotIn,
   202  								Values:   []string{"kube-system", "kueue-system"},
   203  							},
   204  						},
   205  					},
   206  				}),
   207  			))
   208  		})
   209  		ginkgo.BeforeEach(func() {
   210  			ns = &corev1.Namespace{
   211  				ObjectMeta: metav1.ObjectMeta{
   212  					GenerateName: "pod-",
   213  				},
   214  			}
   215  			gomega.Expect(k8sClient.Create(ctx, ns)).To(gomega.Succeed())
   216  		})
   217  
   218  		ginkgo.AfterEach(func() {
   219  			gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
   220  		})
   221  		ginkgo.AfterAll(func() {
   222  			fwk.Teardown()
   223  		})
   224  
   225  		ginkgo.When("The queue-name label is not set", func() {
   226  			var pod *corev1.Pod
   227  
   228  			ginkgo.BeforeEach(func() {
   229  				pod = testingpod.MakePod("pod-integration", ns.Name).Obj()
   230  			})
   231  
   232  			ginkgo.It("Should inject scheduling gate, 'managed' label and finalizer into created pod", func() {
   233  				gomega.Expect(k8sClient.Create(ctx, pod)).Should(gomega.Succeed())
   234  
   235  				lookupKey := types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}
   236  				createdPod := &corev1.Pod{}
   237  				gomega.Eventually(func() error {
   238  					return k8sClient.Get(ctx, lookupKey, createdPod)
   239  				}, util.Timeout, util.Interval).Should(gomega.Succeed())
   240  
   241  				gomega.Expect(createdPod.Spec.SchedulingGates).To(
   242  					gomega.ContainElement(corev1.PodSchedulingGate{Name: "kueue.x-k8s.io/admission"}),
   243  					"Pod should have scheduling gate",
   244  				)
   245  
   246  				gomega.Expect(createdPod.Labels).To(
   247  					gomega.HaveKeyWithValue("kueue.x-k8s.io/managed", "true"),
   248  					"Pod should have the label",
   249  				)
   250  
   251  				gomega.Expect(createdPod.Finalizers).To(gomega.ContainElement("kueue.x-k8s.io/managed"),
   252  					"Pod should have finalizer set")
   253  			})
   254  
   255  			ginkgo.It("Should skip a Pod created in the forbidden 'kube-system' namespace", func() {
   256  				pod.Namespace = "kube-system"
   257  				gomega.Expect(k8sClient.Create(ctx, pod)).Should(gomega.Succeed())
   258  
   259  				lookupKey := types.NamespacedName{Name: pod.Name, Namespace: "kube-system"}
   260  				createdPod := &corev1.Pod{}
   261  				gomega.Eventually(func() error {
   262  					return k8sClient.Get(ctx, lookupKey, createdPod)
   263  				}, util.Timeout, util.Interval).Should(gomega.Succeed())
   264  
   265  				gomega.Expect(createdPod.Spec.SchedulingGates).NotTo(
   266  					gomega.ContainElement(corev1.PodSchedulingGate{Name: "kueue.x-k8s.io/admission"}),
   267  					"Pod shouldn't have scheduling gate",
   268  				)
   269  
   270  				gomega.Expect(createdPod.Labels).NotTo(
   271  					gomega.HaveKeyWithValue("kueue.x-k8s.io/managed", "true"),
   272  					"Pod shouldn't have the label",
   273  				)
   274  
   275  				gomega.Expect(createdPod.Finalizers).NotTo(gomega.ContainElement("kueue.x-k8s.io/managed"),
   276  					"Pod shouldn't have finalizer set")
   277  			})
   278  		})
   279  	})
   280  })