Skip to content

Commit de05d50

Browse files
Merge pull request #13266 from thomasferrandiz/backport-multus-1.32
[Release-1.32] Add multus e2e test
2 parents 266f824 + 2115bac commit de05d50

File tree

5 files changed

+301
-1
lines changed

5 files changed

+301
-1
lines changed

.github/workflows/e2e.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
strategy:
5252
fail-fast: false
5353
matrix:
54-
etest: [btrfs, embeddedmirror, externalip, privateregistry, rootless, s3, startup, wasm]
54+
etest: [btrfs, embeddedmirror, externalip, privateregistry, rootless, s3, startup, wasm, multus]
5555
max-parallel: 5
5656
steps:
5757
- name: "Checkout"
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
apiVersion: "k8s.cni.cncf.io/v1"
2+
kind: NetworkAttachmentDefinition
3+
metadata:
4+
name: macvlan-conf
5+
spec:
6+
config: '{
7+
"cniVersion": "0.3.1",
8+
"plugins": [
9+
{
10+
"type": "macvlan",
11+
"capabilities": { "ips": true },
12+
"master": "eth1",
13+
"mode": "bridge",
14+
"ipam": {
15+
"type": "static",
16+
"routes": [
17+
{
18+
"dst": "0.0.0.0/0",
19+
"gw": "10.1.1.1"
20+
}
21+
]
22+
}
23+
}, {
24+
"capabilities": { "mac": true },
25+
"type": "tuning"
26+
}
27+
]
28+
}'
29+
---
30+
31+
apiVersion: v1
32+
kind: Pod
33+
metadata:
34+
labels:
35+
app: pod-macvlan
36+
name: pod-macvlan
37+
annotations:
38+
k8s.v1.cni.cncf.io/networks: '[
39+
{ "name": "macvlan-conf",
40+
"ips": [ "10.1.1.101/24" ],
41+
"mac": "c2:b0:57:49:47:f1",
42+
"gateway": [ "10.1.1.1" ]
43+
}]'
44+
spec:
45+
containers:
46+
- image: rancher/mirrored-library-busybox:1.36.1
47+
command:
48+
- sleep
49+
- infinity
50+
imagePullPolicy: Always
51+
name: busybox
52+
securityContext:
53+
capabilities:
54+
add: ["NET_ADMIN","NET_RAW"]
55+
---
56+
57+
apiVersion: v1
58+
kind: Pod
59+
metadata:
60+
labels:
61+
app: pod2-macvlan
62+
name: pod2-macvlan
63+
annotations:
64+
k8s.v1.cni.cncf.io/networks: '[
65+
{ "name": "macvlan-conf",
66+
"ips": [ "10.1.1.102/24" ],
67+
"mac": "c2:b0:57:45:47:f1",
68+
"gateway": [ "10.1.1.1" ]
69+
}]'
70+
spec:
71+
containers:
72+
- image: rancher/mirrored-library-busybox:1.36.1
73+
command:
74+
- sleep
75+
- infinity
76+
imagePullPolicy: Always
77+
name: busybox
78+
securityContext:
79+
capabilities:
80+
add: ["NET_ADMIN","NET_RAW"]

tests/e2e/multus/Vagrantfile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
ENV['VAGRANT_NO_PARALLEL'] = 'no'
2+
NODE_ROLES = (ENV['E2E_NODE_ROLES'] ||
3+
["server-0", "agent-0" ])
4+
NODE_BOXES = (ENV['E2E_NODE_BOXES'] ||
5+
['bento/ubuntu-24.04', 'bento/ubuntu-24.04'])
6+
GITHUB_BRANCH = (ENV['E2E_GITHUB_BRANCH'] || "master")
7+
RELEASE_VERSION = (ENV['E2E_RELEASE_VERSION'] || "")
8+
GOCOVER = (ENV['E2E_GOCOVER'] || "")
9+
NODE_CPUS = (ENV['E2E_NODE_CPUS'] || 2).to_i
10+
NODE_MEMORY = (ENV['E2E_NODE_MEMORY'] || 3072).to_i
11+
NETWORK4_PREFIX = "10.10.10"
12+
MULTUS_PREFIX = "192.168.1"
13+
install_type = ""
14+
15+
def provision(vm, role, role_num, node_num)
16+
vm.box = NODE_BOXES[node_num]
17+
vm.hostname = role
18+
node_ip4 = "#{NETWORK4_PREFIX}.#{100+node_num}"
19+
vm.network "private_network", :ip => node_ip4, :netmask => "255.255.255.0"
20+
21+
scripts_location = Dir.exist?("./scripts") ? "./scripts" : "../scripts"
22+
vagrant_defaults = File.exist?("./vagrantdefaults.rb") ? "./vagrantdefaults.rb" : "../vagrantdefaults.rb"
23+
load vagrant_defaults
24+
25+
defaultOSConfigure(vm)
26+
addCoverageDir(vm, role, GOCOVER)
27+
install_type = getInstallType(vm, RELEASE_VERSION, GITHUB_BRANCH)
28+
29+
if role.include?("server") && role_num == 0
30+
vm.provision :k3s, run: 'once' do |k3s|
31+
k3s.config_mode = '0644' # side-step https://github.com/k3s-io/k3s/issues/4321
32+
k3s.args = "server "
33+
k3s.config = <<~YAML
34+
node-ip: #{node_ip4}
35+
token: vagrant
36+
flannel-iface: eth1
37+
YAML
38+
k3s.env = ["K3S_KUBECONFIG_MODE=0644", install_type]
39+
end
40+
#install multus manifest
41+
vm.provision "file", source: "multus-config.yaml", destination: "multus-config.yaml"
42+
vm.provision "shell", inline: "sudo mkdir -p /var/lib/rancher/k3s/server/manifests/ && sudo cp multus-config.yaml /var/lib/rancher/k3s/server/manifests/"
43+
end
44+
if role.include?("agent")
45+
vm.provision :k3s, run: 'once' do |k3s|
46+
k3s.config_mode = '0644' # side-step https://github.com/k3s-io/k3s/issues/4321
47+
k3s.args = "agent "
48+
k3s.config = <<~YAML
49+
server: https://#{NETWORK4_PREFIX}.100:6443
50+
token: vagrant
51+
node-ip: #{node_ip4}
52+
flannel-iface: eth1
53+
YAML
54+
k3s.env = ["K3S_KUBECONFIG_MODE=0644", install_type]
55+
end
56+
end
57+
end
58+
59+
Vagrant.configure("2") do |config|
60+
config.vagrant.plugins = ["vagrant-k3s", "vagrant-reload", "vagrant-libvirt"]
61+
config.vm.provider "libvirt" do |v|
62+
v.cpus = NODE_CPUS
63+
v.memory = NODE_MEMORY
64+
# We replicate the default prefix, but add a timestamp to enable parallel runs and cleanup of old VMs
65+
v.default_prefix = File.basename(Dir.getwd) + "_" + Time.now.to_i.to_s + "_"
66+
end
67+
68+
if NODE_ROLES.kind_of?(String)
69+
NODE_ROLES = NODE_ROLES.split(" ", -1)
70+
end
71+
if NODE_BOXES.kind_of?(String)
72+
NODE_BOXES = NODE_BOXES.split(" ", -1)
73+
end
74+
75+
NODE_ROLES.each_with_index do |role, i|
76+
role_num = role.split("-", -1).pop.to_i
77+
config.vm.define role do |node|
78+
provision(node.vm, role, role_num, i)
79+
end
80+
end
81+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: helm.cattle.io/v1
2+
kind: HelmChart
3+
metadata:
4+
name: multus
5+
namespace: kube-system
6+
spec:
7+
repo: https://rke2-charts.rancher.io
8+
chart: rke2-multus
9+
targetNamespace: kube-system
10+
valuesContent: |-
11+
config:
12+
fullnameOverride: multus
13+
cni_conf:
14+
confDir: /var/lib/rancher/k3s/agent/etc/cni/net.d
15+
binDir: /var/lib/rancher/k3s/data/cni/
16+
kubeconfig: /var/lib/rancher/k3s/agent/etc/cni/net.d/multus.d/multus.kubeconfig
17+
# Comment the following line when using rke2-multus < v4.2.202
18+
multusAutoconfigDir: /var/lib/rancher/k3s/agent/etc/cni/net.d

tests/e2e/multus/multus_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// This test verifies that two nodes, which can't connect using the local network, are
2+
// able to still connect using the node-external-ip. In real life, node-external-ip
3+
// would be a public IP. In the test, we create two networks, one sets the node
4+
// internal-ip and the other sets the node-external-ip. Traffic is blocked on the former
5+
6+
package multus
7+
8+
import (
9+
"flag"
10+
"fmt"
11+
"os"
12+
"testing"
13+
"time"
14+
15+
"github.com/k3s-io/k3s/tests"
16+
"github.com/k3s-io/k3s/tests/e2e"
17+
. "github.com/onsi/ginkgo/v2"
18+
. "github.com/onsi/gomega"
19+
)
20+
21+
// Valid nodeOS: bento/ubuntu-24.04, opensuse/Leap-15.6.x86_64
22+
var nodeOS = flag.String("nodeOS", "bento/ubuntu-24.04", "VM operating system")
23+
var serverCount = flag.Int("serverCount", 1, "number of server nodes")
24+
var agentCount = flag.Int("agentCount", 1, "number of agent nodes")
25+
var ci = flag.Bool("ci", false, "running on CI")
26+
var local = flag.Bool("local", false, "deploy a locally built K3s binary")
27+
28+
func Test_E2EMultus(t *testing.T) {
29+
flag.Parse()
30+
RegisterFailHandler(Fail)
31+
suiteConfig, reporterConfig := GinkgoConfiguration()
32+
RunSpecs(t, "Multus config Suite", suiteConfig, reporterConfig)
33+
34+
}
35+
36+
var tc *e2e.TestConfig
37+
38+
var _ = ReportAfterEach(e2e.GenReport)
39+
40+
var _ = Describe("Verify Multus config", Ordered, func() {
41+
Context("Cluster comes up with Multus enabled", func() {
42+
It("Starts up with no issues", func() {
43+
var err error
44+
if *local {
45+
tc, err = e2e.CreateLocalCluster(*nodeOS, *serverCount, *agentCount)
46+
} else {
47+
tc, err = e2e.CreateCluster(*nodeOS, *serverCount, *agentCount)
48+
}
49+
Expect(err).NotTo(HaveOccurred(), e2e.GetVagrantLog(err))
50+
By("CLUSTER CONFIG")
51+
By("OS: " + *nodeOS)
52+
By(tc.Status())
53+
})
54+
55+
It("Checks Node Status", func() {
56+
Eventually(func() error {
57+
return tests.NodesReady(tc.KubeconfigFile, e2e.VagrantSlice(tc.AllNodes()))
58+
}, "620s", "5s").Should(Succeed())
59+
e2e.DumpNodes(tc.KubeconfigFile)
60+
})
61+
62+
It("Checks pod status", func() {
63+
By("Fetching pod status")
64+
Eventually(func() error {
65+
return tests.AllPodsUp(tc.KubeconfigFile, "kube-system")
66+
}, "620s", "10s").Should(Succeed())
67+
})
68+
})
69+
Context("Deploy workloads to check cluster connectivity of the nodes", func() {
70+
It("Verifies that each node has vagrant IP", func() {
71+
nodeIPs, err := e2e.GetNodeIPs(tc.KubeconfigFile)
72+
fmt.Printf("nodeIPs: %v", nodeIPs)
73+
Expect(err).NotTo(HaveOccurred())
74+
for _, node := range nodeIPs {
75+
Expect(node.IPv4).Should(ContainSubstring("10.10."))
76+
}
77+
})
78+
It("Verifies that each pod has vagrant IP or clusterCIDR IP", func() {
79+
podIPs, err := e2e.GetPodIPs(tc.KubeconfigFile)
80+
Expect(err).NotTo(HaveOccurred())
81+
for _, pod := range podIPs {
82+
Expect(pod.IPv4).Should(Or(ContainSubstring("10.10."), ContainSubstring("10.42.")), pod.Name)
83+
}
84+
})
85+
It("Verifies multus daemonset comes up", func() {
86+
Eventually(func() (string, error) {
87+
cmd := "kubectl get ds multus -n kube-system -o jsonpath='{.status.numberReady}' --kubeconfig=" + tc.KubeconfigFile
88+
return e2e.RunCommand(cmd)
89+
}, "120s", "5s").Should(ContainSubstring("2"))
90+
})
91+
It("Deploys Multus NetworkAttachmentDefinition and test pods", func() {
92+
_, err := tc.DeployWorkload("multus_test.yaml")
93+
Expect(err).NotTo(HaveOccurred())
94+
time.Sleep(5 * time.Second)
95+
})
96+
It("Verifies internode connectivity over multus network", func() {
97+
cmd := "kubectl exec pod-macvlan --kubeconfig=" + tc.KubeconfigFile + " -- ping -c 1 -w 2 10.1.1.102"
98+
Eventually(func() (string, error) {
99+
return e2e.RunCommand(cmd)
100+
}, "20s", "3s").Should(ContainSubstring("0% packet loss"), "failed cmd: "+cmd)
101+
})
102+
})
103+
})
104+
105+
var failed bool
106+
var _ = AfterEach(func() {
107+
failed = failed || CurrentSpecReport().Failed()
108+
})
109+
110+
var _ = AfterSuite(func() {
111+
if failed {
112+
Expect(e2e.SaveJournalLogs(tc.AllNodes())).To(Succeed())
113+
Expect(e2e.TailPodLogs(50, tc.AllNodes())).To(Succeed())
114+
} else {
115+
Expect(e2e.GetCoverageReport(tc.AllNodes())).To(Succeed())
116+
}
117+
if !failed || *ci {
118+
Expect(e2e.DestroyCluster()).To(Succeed())
119+
Expect(os.Remove(tc.KubeconfigFile)).To(Succeed())
120+
}
121+
})

0 commit comments

Comments
 (0)