// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package webhook import ( "fmt" "time" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" "github.com/aws/amazon-vpc-resource-controller-k8s/test/framework/manifest" "github.com/aws/amazon-vpc-resource-controller-k8s/test/framework/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) // TODO: Add integration test for Windows when ConfigMap feature is implemented. var _ = Describe("validating webhook test cases", func() { Context("[CANARY] when modifying existing pod", func() { It("should fail on updating pod sgp annotation", func() { newPod := pod.DeepCopy() newPod.Annotations[config.ResourceNamePodENI] = "updated-annotation" err := frameWork.PodManager.PatchPod(ctx, pod, newPod) Expect(err).To(HaveOccurred()) }) It("should fail on deleting the pod sgp annotation", func() { newPod := pod.DeepCopy() delete(newPod.Annotations, config.ResourceNamePodENI) err := frameWork.PodManager.PatchPod(ctx, pod, newPod) Expect(err).To(HaveOccurred()) }) It("should not fail when modifying other annotations", func() { newPod := pod.DeepCopy() newPod.Annotations["some-other-annotation"] = "new-annotation" err := frameWork.PodManager.PatchPod(ctx, pod, newPod) Expect(err).ToNot(HaveOccurred()) }) }) Context("[CANARY] when creating new pod", func() { It("should fail on creating pod with sgp annotation", func() { newPod, err := manifest.NewDefaultPodBuilder(). Annotations(map[string]string{config.ResourceNamePodENI: "new-annotation"}). Namespace(namespace). Name("new-pod"). Build() Expect(err).ToNot(HaveOccurred()) _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, newPod, utils.ResourceCreationTimeout) Expect(err).To(HaveOccurred()) }) It("should not fail on creating new pod without sgp annotation", func() { newPod, err := manifest.NewDefaultPodBuilder(). Annotations(map[string]string{"some-other-annotation": "new-annotation"}). Namespace(namespace). Name("new-pod"). Build() Expect(err).ToNot(HaveOccurred()) _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, newPod, utils.ResourceCreationTimeout) Expect(err).ToNot(HaveOccurred()) // Allow the cache to sync time.Sleep(utils.PollIntervalShort) err = frameWork.PodManager.DeleteAndWaitTillPodIsDeleted(ctx, newPod) Expect(err).ToNot(HaveOccurred()) }) }) Context("[CANARY] when modifying existing node", func() { var existingNodeNameToMutate string var k8sClientToMutateNode client.Client BeforeEach(func() { nodes, err := frameWork.NodeManager.GetNodesWithOS(config.OSLinux) Expect(err).ToNot(HaveOccurred()) Expect(nodes.Items).ToNot(BeEmpty()) existingNodeNameToMutate = nodes.Items[0].Name }) When("with aws-node credentials", func() { BeforeEach(func() { k8sSchema := runtime.NewScheme() clientgoscheme.AddToScheme(k8sSchema) restCfgWithAWSNode, err := frameWork.SAManager.BuildRestConfigWithServiceAccount(ctx, types.NamespacedName{Namespace: config.KubeSystemNamespace, Name: "aws-node"}) Expect(err).NotTo(HaveOccurred()) k8sClientToMutateNode, err = client.New(restCfgWithAWSNode, client.Options{Scheme: k8sSchema}) Expect(err).NotTo(HaveOccurred()) }) It("should succeed update node with vpc.amazonaws.com/eniConfig label", func() { var originalNodeLabels map[string]string By(fmt.Sprintf("update node %s with %s label", existingNodeNameToMutate, config.CustomNetworkingLabel), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } originalNodeLabels = existingNodeToMutate.Labels nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels[config.CustomNetworkingLabel] = "dummy-value" return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) By(fmt.Sprintf("restore node %s with original labels", existingNodeNameToMutate), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels = originalNodeLabels return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) }) It("should fail update node with dummy-label label", func() { By(fmt.Sprintf("update node %s with %s label", existingNodeNameToMutate, "dummy-label"), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels["dummy-label"] = "dummy-value" fmt.Println(existingNodeToMutate.Labels, nodeCopy.Labels) return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("admission webhook \"vnode.vpc.k8s.aws\" denied the request: aws-node can only update limited fields on the Node Object")) }) }) It("should fail update node with dummy-key taint", func() { By(fmt.Sprintf("update node %s with %s taint", existingNodeNameToMutate, "dummy-key"), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Spec.Taints = append(nodeCopy.Spec.Taints, corev1.Taint{ Key: "dummy-key", Value: "dummy-value", Effect: corev1.TaintEffectPreferNoSchedule, }) return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("admission webhook \"vnode.vpc.k8s.aws\" denied the request: aws-node can only update limited fields on the Node Object")) }) }) }) When("with framework credentials", func() { BeforeEach(func() { k8sClientToMutateNode = frameWork.K8sClient }) It("should succeed update node with vpc.amazonaws.com/eniConfig label", func() { var originalNodeLabels map[string]string By(fmt.Sprintf("update node %s with %s label", existingNodeNameToMutate, config.CustomNetworkingLabel), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } originalNodeLabels = existingNodeToMutate.Labels nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels[config.CustomNetworkingLabel] = "dummy-value" return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) By(fmt.Sprintf("restore node %s with original labels", existingNodeNameToMutate), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels = originalNodeLabels return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) }) It("should succeed update node with dummy-label label", func() { var originalNodeLabels map[string]string By(fmt.Sprintf("update node %s with %s label", existingNodeNameToMutate, "dummy-label"), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } originalNodeLabels = existingNodeToMutate.Labels nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels["dummy-label"] = "dummy-value" return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) By(fmt.Sprintf("restore node %s with original labels", existingNodeNameToMutate), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Labels = originalNodeLabels return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) }) It("should succeed update node with dummy-key taint", func() { var originalNodeTaints []corev1.Taint By(fmt.Sprintf("update node %s with %s taint", existingNodeNameToMutate, "dummy-key"), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } originalNodeTaints = existingNodeToMutate.Spec.Taints nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Spec.Taints = append(nodeCopy.Spec.Taints, corev1.Taint{ Key: "dummy-key", Value: "dummy-value", Effect: corev1.TaintEffectPreferNoSchedule, }) return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) By(fmt.Sprintf("restore node %s with original taints", existingNodeNameToMutate), func() { err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { existingNodeToMutate := &corev1.Node{} if err := k8sClientToMutateNode.Get(ctx, types.NamespacedName{Name: existingNodeNameToMutate}, existingNodeToMutate); err != nil { return err } nodeCopy := existingNodeToMutate.DeepCopy() nodeCopy.Spec.Taints = originalNodeTaints return k8sClientToMutateNode.Update(ctx, nodeCopy) }) Expect(err).NotTo(HaveOccurred()) }) }) }) }) })