Commit 380a70ac authored by Derek Nola's avatar Derek Nola

Expand E2E test matrix to cover all possible tests that fit on GHA

Fix rootless test Fix svc firewall E2E test, broken by #11711 Signed-off-by: 's avatarDerek Nola <derek.nola@suse.com>
parent 9865ab1f
...@@ -45,8 +45,8 @@ jobs: ...@@ -45,8 +45,8 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
etest: [startup, s3, btrfs, externalip, privateregistry, embeddedmirror, wasm] etest: [btrfs, embeddedmirror, externalip, privateregistry, rootless, s3, startup, wasm]
max-parallel: 3 max-parallel: 5
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v4 uses: actions/checkout@v4
......
...@@ -60,10 +60,10 @@ Vagrant.configure("2") do |config| ...@@ -60,10 +60,10 @@ Vagrant.configure("2") do |config|
NODE_BOXES = NODE_BOXES.split(" ", -1) NODE_BOXES = NODE_BOXES.split(" ", -1)
end end
NODE_ROLES.each_with_index do |name, i| NODE_ROLES.each_with_index do |role, i|
role_num = role.split("-", -1).pop.to_i role_num = role.split("-", -1).pop.to_i
config.vm.define role do |node| config.vm.define role do |node|
provision(node.vm, name, role_num, i) provision(node.vm, role, role_num, i)
end end
end end
end end
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"os" "os"
"strings"
"testing" "testing"
"github.com/k3s-io/k3s/tests" "github.com/k3s-io/k3s/tests"
...@@ -153,7 +154,7 @@ var _ = AfterEach(func() { ...@@ -153,7 +154,7 @@ var _ = AfterEach(func() {
var _ = AfterSuite(func() { var _ = AfterSuite(func() {
if failed { if failed {
AddReportEntry("journald-logs", e2e.TailJournalLogs(1000, tc.Servers)) Expect(SaveRootlessJournalLogs(tc.Servers)).To(Succeed())
} else { } else {
Expect(e2e.GetCoverageReport(tc.Servers)).To(Succeed()) Expect(e2e.GetCoverageReport(tc.Servers)).To(Succeed())
} }
...@@ -162,3 +163,62 @@ var _ = AfterSuite(func() { ...@@ -162,3 +163,62 @@ var _ = AfterSuite(func() {
Expect(os.Remove(tc.KubeconfigFile)).To(Succeed()) Expect(os.Remove(tc.KubeconfigFile)).To(Succeed())
} }
}) })
// RunCmdOnRootlessNode executes a command from within the given node as user vagrant
func RunCmdOnRootlessNode(cmd string, nodename string) (string, error) {
injectEnv := ""
if _, ok := os.LookupEnv("E2E_GOCOVER"); ok && strings.HasPrefix(cmd, "k3s") {
injectEnv = "GOCOVERDIR=/tmp/k3scov "
}
runcmd := "vagrant ssh " + nodename + " -c \"" + injectEnv + cmd + "\""
out, err := e2e.RunCommand(runcmd)
// On GHA CI we see warnings about "[fog][WARNING] Unrecognized arguments: libvirt_ip_command"
// these are added to the command output and need to be removed
out = strings.ReplaceAll(out, "[fog][WARNING] Unrecognized arguments: libvirt_ip_command\n", "")
if err != nil {
return out, fmt.Errorf("failed to run command: %s on node %s: %s, %v", cmd, nodename, out, err)
}
return out, nil
}
func GenRootlessKubeconfigFile(serverName string) (string, error) {
kubeConfig, err := RunCmdOnRootlessNode("cat /home/vagrant/.kube/k3s.yaml", serverName)
if err != nil {
return "", err
}
vNode := e2e.VagrantNode(serverName)
nodeIP, err := vNode.FetchNodeExternalIP()
if err != nil {
return "", err
}
kubeConfig = strings.Replace(kubeConfig, "127.0.0.1", nodeIP, 1)
kubeConfigFile := fmt.Sprintf("kubeconfig-%s", serverName)
if err := os.WriteFile(kubeConfigFile, []byte(kubeConfig), 0644); err != nil {
return "", err
}
if err := os.Setenv("E2E_KUBECONFIG", kubeConfigFile); err != nil {
return "", err
}
return kubeConfigFile, nil
}
// SaveRootlessJournalLogs saves the journal logs of each node to a <NAME>-jlog.txt file.
// When used in GHA CI, the logs are uploaded as an artifact on failure.
func SaveRootlessJournalLogs(nodes []e2e.VagrantNode) error {
for _, node := range nodes {
lf, err := os.Create(node.String() + "-jlog.txt")
if err != nil {
return err
}
defer lf.Close()
cmd := "vagrant ssh --no-tty " + node.String() + " -c \"journalctl -u --user k3s-rootless --no-pager\""
logs, err := e2e.RunCommand(cmd)
if err != nil {
return err
}
if _, err := lf.Write([]byte(logs)); err != nil {
return fmt.Errorf("failed to write %s node logs: %v", node, err)
}
}
return nil
}
package rootless
import (
"fmt"
"os"
"strings"
"github.com/k3s-io/k3s/tests/e2e"
)
// RunCmdOnRootlessNode executes a command from within the given node as user vagrant
func RunCmdOnRootlessNode(cmd string, nodename string) (string, error) {
injectEnv := ""
if _, ok := os.LookupEnv("E2E_GOCOVER"); ok && strings.HasPrefix(cmd, "k3s") {
injectEnv = "GOCOVERDIR=/tmp/k3scov "
}
runcmd := "vagrant ssh " + nodename + " -c \"" + injectEnv + cmd + "\""
out, err := e2e.RunCommand(runcmd)
if err != nil {
return out, fmt.Errorf("failed to run command: %s on node %s: %s, %v", cmd, nodename, out, err)
}
return out, nil
}
func GenRootlessKubeconfigFile(serverName string) (string, error) {
kubeConfig, err := RunCmdOnRootlessNode("cat /home/vagrant/.kube/k3s.yaml", serverName)
if err != nil {
return "", err
}
vNode := e2e.VagrantNode(serverName)
nodeIP, err := vNode.FetchNodeExternalIP()
if err != nil {
return "", err
}
kubeConfig = strings.Replace(kubeConfig, "127.0.0.1", nodeIP, 1)
kubeConfigFile := fmt.Sprintf("kubeconfig-%s", serverName)
if err := os.WriteFile(kubeConfigFile, []byte(kubeConfig), 0644); err != nil {
return "", err
}
if err := os.Setenv("E2E_KUBECONFIG", kubeConfigFile); err != nil {
return "", err
}
return kubeConfigFile, nil
}
...@@ -26,9 +26,11 @@ fi ...@@ -26,9 +26,11 @@ fi
# Enable IPv4 forwarding # Enable IPv4 forwarding
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# Disable Ubuntu Restricted unprivileged user namespaces
echo "kernel.apparmor_restrict_unprivileged_unconfined=0" >> /etc/sysctl.conf
echo "kernel.apparmor_restrict_unprivileged_userns=0" >> /etc/sysctl.conf
sysctl --system sysctl --system
# Check if the string is already in GRUB_CMDLINE_LINUX # Check if the string is already in GRUB_CMDLINE_LINUX
if grep -qxF "GRUB_CMDLINE_LINUX=\"systemd.unified_cgroup_hierarchy=1 \"" /etc/default/grub; then if grep -qxF "GRUB_CMDLINE_LINUX=\"systemd.unified_cgroup_hierarchy=1 \"" /etc/default/grub; then
echo "String is already in GRUB_CMDLINE_LINUX. No changes made." echo "String is already in GRUB_CMDLINE_LINUX. No changes made."
......
...@@ -33,10 +33,7 @@ func Test_E2EPoliciesAndFirewall(t *testing.T) { ...@@ -33,10 +33,7 @@ func Test_E2EPoliciesAndFirewall(t *testing.T) {
RunSpecs(t, "Services Traffic Policies and Firewall config Suite", suiteConfig, reporterConfig) RunSpecs(t, "Services Traffic Policies and Firewall config Suite", suiteConfig, reporterConfig)
} }
var ( var tc *e2e.TestConfig
tc *e2e.TestConfig
nodes []e2e.Node
)
var _ = ReportAfterEach(e2e.GenReport) var _ = ReportAfterEach(e2e.GenReport)
...@@ -88,16 +85,17 @@ var _ = Describe("Verify Services Traffic policies and firewall config", Ordered ...@@ -88,16 +85,17 @@ var _ = Describe("Verify Services Traffic policies and firewall config", Ordered
for _, pod := range pods { for _, pod := range pods {
if strings.Contains(pod.Name, "test-loadbalancer-ext") { if strings.Contains(pod.Name, "test-loadbalancer-ext") {
serverNodeName = pod.Spec.NodeName serverNodeName = pod.Spec.NodeName
break
} }
} }
return serverNodeName, nil return serverNodeName, nil
}, "25s", "5s").ShouldNot(BeEmpty(), "server pod not found") }, "25s", "5s").ShouldNot(BeEmpty(), "server pod not found")
var serverNodeIP string var serverNodeIP string
for _, node := range nodes { nodeIPs, err := e2e.GetNodeIPs(tc.KubeconfigFile)
Expect(err).NotTo(HaveOccurred(), "failed to get node IPs")
for _, node := range nodeIPs {
if node.Name == serverNodeName { if node.Name == serverNodeName {
serverNodeIP = node.InternalIP serverNodeIP = node.IPv4
} }
} }
...@@ -136,19 +134,6 @@ var _ = Describe("Verify Services Traffic policies and firewall config", Ordered ...@@ -136,19 +134,6 @@ var _ = Describe("Verify Services Traffic policies and firewall config", Ordered
cmd := "curl -m 5 -s -f http://" + lbSvcExtExternalIPs[0] + ":82/ip" cmd := "curl -m 5 -s -f http://" + lbSvcExtExternalIPs[0] + ":82/ip"
return e2e.RunCommand(cmd) return e2e.RunCommand(cmd)
}, "25s", "5s").ShouldNot(ContainSubstring("10.42")) }, "25s", "5s").ShouldNot(ContainSubstring("10.42"))
// Verify connectivity to the other nodeIP does not work because of external traffic policy=local
for _, externalIP := range lbSvcExternalIPs {
if externalIP == lbSvcExtExternalIPs[0] {
// This IP we already test and it shuold work
continue
}
Eventually(func() error {
cmd := "curl -m 5 -s -f http://" + externalIP + ":82/ip"
_, err := e2e.RunCommand(cmd)
return err
}, "40s", "5s").Should(MatchError(ContainSubstring("exit status")))
}
}) })
// Verifies that the internal traffic policy=local is deployed // Verifies that the internal traffic policy=local is deployed
...@@ -260,29 +245,31 @@ var _ = Describe("Verify Services Traffic policies and firewall config", Ordered ...@@ -260,29 +245,31 @@ var _ = Describe("Verify Services Traffic policies and firewall config", Ordered
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: nginx-loadbalancer-svc-ext-firewall name: nginx-loadbalancer-svc-ext-firewall
spec: spec:
type: LoadBalancer type: LoadBalancer
loadBalancerSourceRanges: loadBalancerSourceRanges:
- {{.NodeIP}}/32 - {{.NodeIP}}/32
ports: ports:
- port: 82 - port: 82
targetPort: 80 targetPort: 80
protocol: TCP protocol: TCP
name: http name: http
selector: selector:
k8s-app: nginx-app-loadbalancer-ext k8s-app: nginx-app-loadbalancer-ext
` `
// Remove the service nginx-loadbalancer-svc-ext // Remove the service nginx-loadbalancer-svc-ext
_, err := e2e.RunCommand("kubectl delete svc nginx-loadbalancer-svc-ext") _, err := e2e.RunCommand("kubectl --kubeconfig=" + tc.KubeconfigFile + " delete svc nginx-loadbalancer-svc-ext")
Expect(err).NotTo(HaveOccurred(), "failed to remove service nginx-loadbalancer-svc-ext") Expect(err).NotTo(HaveOccurred(), "failed to remove service nginx-loadbalancer-svc-ext")
// Parse and execute the template with the node IP // Parse and execute the template with the node IP
tmpl, err := template.New("service").Parse(serviceManifest) tmpl, err := template.New("service").Parse(serviceManifest)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
nodeIPs, err := e2e.GetNodeIPs(tc.KubeconfigFile)
Expect(err).NotTo(HaveOccurred())
var filledManifest strings.Builder var filledManifest strings.Builder
err = tmpl.Execute(&filledManifest, struct{ NodeIP string }{NodeIP: nodes[0].InternalIP}) err = tmpl.Execute(&filledManifest, struct{ NodeIP string }{NodeIP: nodeIPs[0].IPv4})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// Write the filled manifest to a temporary file // Write the filled manifest to a temporary file
...@@ -307,29 +294,31 @@ selector: ...@@ -307,29 +294,31 @@ selector:
// Verify that only the allowed node can curl. That node should be able to curl both externalIPs (i.e. node.InternalIP) // Verify that only the allowed node can curl. That node should be able to curl both externalIPs (i.e. node.InternalIP)
It("Verify firewall is working", func() { It("Verify firewall is working", func() {
for _, node := range nodes { nodeIPs, err := e2e.GetNodeIPs(tc.KubeconfigFile)
var sNode, aNode e2e.VagrantNode Expect(err).NotTo(HaveOccurred())
for _, n := range tc.Servers {
if n.String() == nodes[0].Name { var firstNode e2e.VagrantNode
sNode = n var secondNode e2e.VagrantNode
} for _, node := range tc.AllNodes() {
} if node.String() == nodeIPs[0].Name {
for _, n := range tc.Agents { firstNode = node
if n.String() == nodes[1].Name { } else {
aNode = n secondNode = node
} }
} }
fmt.Println("First node: ", firstNode.String())
fmt.Println("Second node: ", secondNode.String())
for _, ip := range nodeIPs {
// Verify connectivity from nodes[0] works because we passed its IP to the loadBalancerSourceRanges // Verify connectivity from nodes[0] works because we passed its IP to the loadBalancerSourceRanges
Eventually(func() (string, error) { Eventually(func() (string, error) {
cmd := "curl -m 5 -s -f http://" + node.InternalIP + ":82" cmd := "curl -m 5 -s -f http:// " + ip.IPv4 + ":82"
return sNode.RunCmdOnNode(cmd) return firstNode.RunCmdOnNode(cmd)
}, "40s", "5s").Should(ContainSubstring("Welcome to nginx")) }, "40s", "5s").Should(ContainSubstring("Welcome to nginx"))
// Verify connectivity from nodes[1] fails because we did not pass its IP to the loadBalancerSourceRanges // Verify connectivity from nodes[1] fails because we did not pass its IP to the loadBalancerSourceRanges
Eventually(func(g Gomega) error { Eventually(func(g Gomega) error {
cmd := "curl -m 5 -s -f http:// " + node.InternalIP + ":82" cmd := "curl -m 5 -s -f http:// " + ip.IPv4 + ":82"
_, err := aNode.RunCmdOnNode(cmd) _, err := secondNode.RunCmdOnNode(cmd)
return err return err
}, "40s", "5s").Should(MatchError(ContainSubstring("exit status"))) }, "40s", "5s").Should(MatchError(ContainSubstring("exit status")))
} }
...@@ -344,7 +333,7 @@ var _ = AfterEach(func() { ...@@ -344,7 +333,7 @@ var _ = AfterEach(func() {
var _ = AfterSuite(func() { var _ = AfterSuite(func() {
if failed { if failed {
AddReportEntry("journald-logs", e2e.TailJournalLogs(1000, tc.AllNodes())) Expect(e2e.SaveJournalLogs(tc.AllNodes())).To(Succeed())
} else { } else {
Expect(e2e.GetCoverageReport(tc.AllNodes())).To(Succeed()) Expect(e2e.GetCoverageReport(tc.AllNodes())).To(Succeed())
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment