| // +build integration |
| |
| // To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration" |
| |
| /* |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License 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 traits |
| |
| import ( |
| "fmt" |
| "testing" |
| |
| . "github.com/onsi/gomega" |
| |
| v1 "k8s.io/api/core/v1" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| |
| . "github.com/apache/camel-k/e2e/support" |
| camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1" |
| ) |
| |
| func TestAffinityTrait(t *testing.T) { |
| WithNewTestNamespace(t, func(ns string) { |
| Expect(Kamel("install", "-n", ns).Execute()).To(Succeed()) |
| |
| var hostname string |
| if node, err := selectSchedulableNode(); err == nil { |
| hostname = node.Labels["kubernetes.io/hostname"] |
| } else { |
| // if 'get nodes' is not allowed, just skip tests for node affinity |
| hostname = "" |
| } |
| |
| if hostname != "" { |
| t.Run("Run Java with node affinity", func(t *testing.T) { |
| Expect(Kamel("run", "-n", ns, "files/Java.java", |
| "--name", "java1", |
| "-t", "affinity.enabled=true", |
| "-t", fmt.Sprintf("affinity.node-affinity-labels=kubernetes.io/hostname in(%s)", hostname)).Execute()).To(Succeed()) |
| Eventually(IntegrationPodPhase(ns, "java1"), TestTimeoutLong).Should(Equal(v1.PodRunning)) |
| Eventually(IntegrationCondition(ns, "java1", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue)) |
| Eventually(IntegrationLogs(ns, "java1"), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) |
| |
| pod := IntegrationPod(ns, "java1")() |
| Expect(pod.Spec.Affinity).NotTo(BeNil()) |
| Expect(pod.Spec.Affinity.NodeAffinity).To(Equal(&v1.NodeAffinity{ |
| RequiredDuringSchedulingIgnoredDuringExecution: nodeSelector("kubernetes.io/hostname", v1.NodeSelectorOpIn, hostname), |
| })) |
| Expect(pod.Spec.NodeName).To(Equal(hostname)) |
| |
| Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) |
| }) |
| } |
| |
| t.Run("Run Java with pod affinity", func(t *testing.T) { |
| Expect(Kamel("run", "-n", ns, "files/Java.java", |
| "--name", "java2", |
| "-t", "affinity.enabled=true", |
| "-t", "affinity.pod-affinity-labels=camel.apache.org/integration").Execute()).To(Succeed()) |
| Eventually(IntegrationPodPhase(ns, "java2"), TestTimeoutLong).Should(Equal(v1.PodRunning)) |
| Eventually(IntegrationCondition(ns, "java2", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue)) |
| Eventually(IntegrationLogs(ns, "java2"), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) |
| |
| pod := IntegrationPod(ns, "java2")() |
| Expect(pod.Spec.Affinity).NotTo(BeNil()) |
| Expect(pod.Spec.Affinity.PodAffinity).To(Equal(&v1.PodAffinity{ |
| RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ |
| podAffinityTerm("camel.apache.org/integration", metav1.LabelSelectorOpExists, "kubernetes.io/hostname"), |
| }, |
| })) |
| |
| Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) |
| }) |
| |
| t.Run("Run Java with pod anti affinity", func(t *testing.T) { |
| Expect(Kamel("run", "-n", ns, "files/Java.java", |
| "--name", "java3", |
| "-t", "affinity.enabled=true", |
| "-t", "affinity.pod-anti-affinity-labels=camel.apache.org/integration").Execute()).To(Succeed()) |
| Eventually(IntegrationPodPhase(ns, "java3"), TestTimeoutLong).Should(Equal(v1.PodRunning)) |
| Eventually(IntegrationCondition(ns, "java3", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue)) |
| Eventually(IntegrationLogs(ns, "java3"), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) |
| |
| pod := IntegrationPod(ns, "java3")() |
| Expect(pod.Spec.Affinity).NotTo(BeNil()) |
| Expect(pod.Spec.Affinity.PodAntiAffinity).To(Equal(&v1.PodAntiAffinity{ |
| RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ |
| podAffinityTerm("camel.apache.org/integration", metav1.LabelSelectorOpExists, "kubernetes.io/hostname"), |
| }, |
| })) |
| |
| Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) |
| }) |
| }) |
| } |
| |
| func selectSchedulableNode() (*v1.Node, error) { |
| nodes, err := TestClient().CoreV1().Nodes().List(TestContext, metav1.ListOptions{}) |
| if err != nil { |
| return nil, err |
| } |
| for _, node := range nodes.Items { |
| if node.Spec.Taints == nil { |
| return &node, nil |
| } |
| } |
| return nil, fmt.Errorf("no node available") |
| } |
| |
| func nodeSelector(key string, operator v1.NodeSelectorOperator, value string) *v1.NodeSelector { |
| return &v1.NodeSelector{ |
| NodeSelectorTerms: []v1.NodeSelectorTerm{ |
| { |
| MatchExpressions: []v1.NodeSelectorRequirement{ |
| { |
| Key: key, |
| Operator: operator, |
| Values: []string{value}, |
| }, |
| }, |
| }, |
| }, |
| } |
| } |
| |
| func podAffinityTerm(key string, operator metav1.LabelSelectorOperator, topologyKey string) v1.PodAffinityTerm { |
| return v1.PodAffinityTerm{ |
| LabelSelector: &metav1.LabelSelector{ |
| MatchExpressions: []metav1.LabelSelectorRequirement{ |
| { |
| Key: key, |
| Operator: operator, |
| }, |
| }, |
| }, |
| TopologyKey: topologyKey, |
| } |
| } |