Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Add toleration trait e2e tests #2123

Merged
merged 5 commits into from
Mar 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
507 changes: 370 additions & 137 deletions deploy/traits.yaml

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions docs/modules/traits/pages/toleration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
This trait sets Tolerations over Integration pods. Tolerations allow (but do not require) the pods to schedule onto nodes with matching taints.
See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ for more details.

The toleration should be expressed in a similar manner of taints *_Key[=Value]:Effect[:Seconds]_* where values in square brackets are optional. Examples:
The toleration should be expressed in a similar manner that of taints, i.e., `Key[=Value]:Effect[:Seconds]`, where values in square brackets are optional.

node-role.kubernetes.io/master:NoSchedule
node.kubernetes.io/network-unavailable:NoExecute:3000
disktype=ssd:PreferNoSchedule
For examples:

- `node-role.kubernetes.io/master:NoSchedule`
- `node.kubernetes.io/network-unavailable:NoExecute:3000`
- `disktype=ssd:PreferNoSchedule`

It's disabled by default.

Expand Down Expand Up @@ -36,7 +38,7 @@ The following configuration options are available:

| toleration.taints
| []string
| The taint to tolerate in the form Key[=Value]:Effect[:Seconds]
| The list of taints to tolerate, in the form `Key[=Value]:Effect[:Seconds]`

|===

Expand Down
2 changes: 1 addition & 1 deletion e2e/common/traits/pdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import (
camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
)

func TestPodDisruptionBudget(t *testing.T) {
func TestPodDisruptionBudgetTrait(t *testing.T) {
WithNewTestNamespace(t, func(ns string) {
name := "java"
Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
Expand Down
130 changes: 90 additions & 40 deletions e2e/common/traits/toleration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ import (
"testing"

. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"

v1 "k8s.io/api/core/v1"
"k8s.io/utils/pointer"

. "github.com/apache/camel-k/e2e/support"
camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
Expand All @@ -35,46 +37,94 @@ import (
func TestTolerationTrait(t *testing.T) {
WithNewTestNamespace(t, func(ns string) {
Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
var wait int64 = 300

InvokeUserTestCode(t, ns, func(ns string) {
t.Run("Run Java with node toleration operation exists", func(t *testing.T) {
Expect(Kamel("run", "-n", ns, "files/Java.java",
"--name", "java1",
"-t", "toleration.enabled=true",
"-t", "toleration.taints=camel.apache.org/master:NoExecute:300").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.Tolerations).NotTo(BeNil())

Expect(pod.Spec.Tolerations).To(ContainElement(v1.Toleration{
"camel.apache.org/master", v1.TolerationOpExists, "", v1.TaintEffectNoExecute, &wait,
}))

Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
})

t.Run("Run Java with node toleration operation equals", func(t *testing.T) {
Expect(Kamel("run", "-n", ns, "files/Java.java",
"--name", "java2",
"-t", "toleration.enabled=true",
"-t", "toleration.taints=camel.apache.org/master=test:NoExecute:300").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.Tolerations).NotTo(BeNil())

Expect(pod.Spec.Tolerations).To(ContainElement(v1.Toleration{
"camel.apache.org/master", v1.TolerationOpEqual, "test", v1.TaintEffectNoExecute, &wait,
}))

Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
})

t.Run("Run Java with node toleration operation exists", func(t *testing.T) {
name := "java1"
Expect(Kamel("run", "-n", ns, "files/Java.java",
"--name", name,
"-t", "toleration.enabled=true",
"-t", "toleration.taints=camel.apache.org/master:NoExecute:300",
).Execute()).To(Succeed())
Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(v1.PodRunning))
Eventually(IntegrationCondition(ns, name, camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))

pod := IntegrationPod(ns, name)()
Expect(pod.Spec.Tolerations).NotTo(BeNil())

Expect(pod.Spec.Tolerations).To(ContainElement(v1.Toleration{
Key: "camel.apache.org/master",
Operator: v1.TolerationOpExists,
Effect: v1.TaintEffectNoExecute,
TolerationSeconds: pointer.Int64Ptr(300),
}))
})

t.Run("Run Java with node toleration operation equals", func(t *testing.T) {
name := "java2"
Expect(Kamel("run", "-n", ns, "files/Java.java",
"--name", name,
"-t", "toleration.enabled=true",
"-t", "toleration.taints=camel.apache.org/master=test:NoExecute:300",
).Execute()).To(Succeed())
Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(v1.PodRunning))
Eventually(IntegrationCondition(ns, name, camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))

pod := IntegrationPod(ns, name)()
Expect(pod.Spec.Tolerations).NotTo(BeNil())

Expect(pod.Spec.Tolerations).To(ContainElement(v1.Toleration{
Key: "camel.apache.org/master",
Operator: v1.TolerationOpEqual,
Value: "test", Effect: v1.TaintEffectNoExecute,
TolerationSeconds: pointer.Int64Ptr(300),
}))
})

t.Run("Run Java with master node toleration", func(t *testing.T) {
if len(Nodes()()) == 1 {
t.Skip("Skip master node toleration test on single-node cluster")
}

name := "java3"
Expect(Kamel("run", "-n", ns, "files/Java.java",
"--name", name,
// Use the affinity trait to force the scheduling of the Integration pod onto a master node
"-t", "affinity.enabled=true",
"-t", "affinity.node-affinity-labels=node-role.kubernetes.io/master",
// And tolerate the corresponding taint
"-t", "toleration.enabled=true",
"-t", "toleration.taints=node-role.kubernetes.io/master:NoSchedule",
).Execute()).To(Succeed())

Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(v1.PodRunning))
Eventually(IntegrationCondition(ns, name, camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))

pod := IntegrationPod(ns, name)()
Expect(pod).NotTo(BeNil())

// Check the Integration pod contains the toleration
Expect(pod.Spec.Tolerations).To(ContainElement(v1.Toleration{
Key: "node-role.kubernetes.io/master",
Operator: v1.TolerationOpExists,
Effect: v1.TaintEffectNoSchedule,
}))

// Check the Integration pod is running on a master node
Expect(Node(pod.Spec.NodeName)).NotTo(BeNil())
Expect(Node(pod.Spec.NodeName)).To(PointTo(MatchFields(IgnoreExtras, Fields{
"Spec": MatchFields(IgnoreExtras, Fields{
"Taints": ContainElement(v1.Taint{
Key: "node-role.kubernetes.io/master",
Effect: v1.TaintEffectNoSchedule,
}),
}),
})))
})

// Clean up
Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
})
}
35 changes: 35 additions & 0 deletions e2e/support/test_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,41 @@ func Lease(ns string, name string) func() *coordination.Lease {
}
}

func Nodes() func() []corev1.Node {
return func() []corev1.Node {
nodes := &corev1.NodeList{
TypeMeta: metav1.TypeMeta{
Kind: "NodeList",
APIVersion: corev1.SchemeGroupVersion.String(),
},
}
err := TestClient().List(TestContext, nodes)
if err != nil {
panic(err)
}
return nodes.Items
}
}

func Node(name string) func() *corev1.Node {
return func() *corev1.Node {
node := &corev1.Node{
TypeMeta: metav1.TypeMeta{
Kind: "Node",
APIVersion: corev1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
err := TestClient().Get(TestContext, ctrl.ObjectKeyFromObject(node), node)
if err != nil {
panic(err)
}
return node
}
}

func Service(ns string, name string) func() *corev1.Service {
return func() *corev1.Service {
svc := corev1.Service{}
Expand Down
12 changes: 7 additions & 5 deletions pkg/trait/toleration.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,20 @@ import (
// This trait sets Tolerations over Integration pods. Tolerations allow (but do not require) the pods to schedule onto nodes with matching taints.
// See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ for more details.
//
// The toleration should be expressed in a similar manner of taints *_Key[=Value]:Effect[:Seconds]_* where values in square brackets are optional. Examples:
// The toleration should be expressed in a similar manner that of taints, i.e., `Key[=Value]:Effect[:Seconds]`, where values in square brackets are optional.
//
// node-role.kubernetes.io/master:NoSchedule
// node.kubernetes.io/network-unavailable:NoExecute:3000
// disktype=ssd:PreferNoSchedule
// For examples:
//
// - `node-role.kubernetes.io/master:NoSchedule`
// - `node.kubernetes.io/network-unavailable:NoExecute:3000`
// - `disktype=ssd:PreferNoSchedule`
//
// It's disabled by default.
//
// +camel-k:trait=toleration
type tolerationTrait struct {
BaseTrait `property:",squash"`
// The taint to tolerate in the form Key[=Value]:Effect[:Seconds]
// The list of taints to tolerate, in the form `Key[=Value]:Effect[:Seconds]`
Taints []string `property:"taints" json:"taints,omitempty"`
}

Expand Down