diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 1c331bd8..1ac015d4 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -76,3 +76,10 @@ jobs: with: name: test-e2e-results path: .output/*.xml + + - name: Upload logs as artifacts + uses: actions/upload-artifact@v4 + if: failure() + with: + name: test-logs + path: ./test/e2e/.output/*.log diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index a02c338b..66f047a1 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -17,6 +17,11 @@ limitations under the License. package e2e import ( + "bytes" + "fmt" + "os" + "os/exec" + "strings" "testing" . "github.com/onsi/ginkgo/v2" @@ -29,6 +34,8 @@ import ( testenv "github.com/kubevirt/ipam-extensions/test/env" ) +const logsDir = ".output" // ./test/e2e/.output + var _ = BeforeSuite(func() { ctrl.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) testenv.Start() @@ -36,6 +43,72 @@ var _ = BeforeSuite(func() { // Run e2e tests using the Ginkgo runner. func TestE2E(t *testing.T) { + os.RemoveAll(logsDir) + if err := os.MkdirAll(logsDir, 0755); err != nil { + panic(fmt.Sprintf("Error creating directory: %v", err)) + } + RegisterFailHandler(Fail) RunSpecs(t, "kubevirt-ipam-controller e2e suite") } + +func logCommand(args []string, topic string, failureCount int) { + stdout, stderr, err := kubectl(args...) + if err != nil { + fmt.Printf("Error running command kubectl %v, err %v\n", args, err) + return + } + + fileName := fmt.Sprintf(logsDir+"/%d_%s.log", failureCount, topic) + file, err := os.Create(fileName) + if err != nil { + fmt.Printf("Error running command %v, err %v\n", args, err) + return + } + defer file.Close() + + output := fmt.Sprintf("kubectl %s\n", strings.Join(args, " ")) + if stdout != "" { + output += fmt.Sprintf("%s\n", stdout) + } + if stderr != "" { + output += fmt.Sprintf("%s\n", stderr) + } + fmt.Fprint(file, output) +} + +func logOvnPods(failureCount int) { + args := []string{"get", "pods", "-n", "ovn-kubernetes", "--no-headers", "-o=custom-columns=NAME:.metadata.name"} + ovnK8sPods, stderr, err := kubectl(args...) + if err != nil { + fmt.Printf("Error running command kubectl %v, stderr %s, err %v\n", args, stderr, err) + return + } + + podContainers := []struct { + PodPrefix string + Containers []string + }{ + {PodPrefix: "ovnkube-control-plane", Containers: []string{"ovnkube-cluster-manager"}}, + {PodPrefix: "ovnkube-node", Containers: []string{"ovnkube-controller"}}, + } + + for _, pod := range strings.Split(ovnK8sPods, "\n") { + for _, pc := range podContainers { + if strings.HasPrefix(pod, pc.PodPrefix) { + for _, container := range pc.Containers { + logCommand([]string{"logs", "-n", "ovn-kubernetes", pod, "-c", container}, pod+"_"+container, failureCount) + } + } + } + } +} + +func kubectl(command ...string) (string, string, error) { + var stdout, stderr bytes.Buffer + cmd := exec.Command(os.Getenv("KUBECTL"), command...) + cmd.Stderr = &stderr + cmd.Stdout = &stdout + err := cmd.Run() + return stdout.String(), stderr.String(), err +} diff --git a/test/e2e/persistentips_test.go b/test/e2e/persistentips_test.go index 6d22ac7c..8068b5b4 100644 --- a/test/e2e/persistentips_test.go +++ b/test/e2e/persistentips_test.go @@ -41,6 +41,20 @@ import ( ) var _ = Describe("Persistent IPs", func() { + var failureCount int = 0 + JustAfterEach(func() { + if CurrentSpecReport().Failed() { + failureCount++ + By(fmt.Sprintf("Test failed, collecting logs and artifacts, failure count %d", failureCount)) + + logCommand([]string{"get", "pods", "-A"}, "pods", failureCount) + logCommand([]string{"get", "vm", "-A", "-oyaml"}, "vms", failureCount) + logCommand([]string{"get", "vmi", "-A", "-oyaml"}, "vmis", failureCount) + logCommand([]string{"get", "ipamclaims", "-A", "-oyaml"}, "ipamclaims", failureCount) + logOvnPods(failureCount) + } + }) + When("network attachment definition created with allowPersistentIPs=true", func() { var ( td testenv.TestData @@ -74,6 +88,9 @@ var _ = Describe("Persistent IPs", func() { WithTimeout(5 * time.Minute). Should(BeTrue()) + // TODO remove + Expect(1).To(Equal(2)) + By("Wait for IPAMClaim to get created") Eventually(testenv.IPAMClaimsFromNamespace(vm.Namespace)). WithTimeout(time.Minute).