From b41ca91694eac9c601c7fba69a4828119ec308c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=83=9C=ED=99=98=20=EB=B0=95?= Date: Sat, 1 Aug 2026 20:44:01 +0900 Subject: [PATCH] =?UTF-8?q?test(e2e):=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C?= =?UTF-8?q?=EC=A0=90=20namespace=20=EB=8D=A4=ED=94=84=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(=ED=98=84=EC=9E=AC=20=EB=8D=A4=ED=94=84=EB=8A=94=20?= =?UTF-8?q?=EC=B2=AD=EC=86=8C=20=ED=9B=84=EB=9D=BC=20=EB=AC=B4=EC=9D=98?= =?UTF-8?q?=EB=AF=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 남은 e2e 실패 2건(backup 이 Completed 에 못 감 / failover 가 primary 를 못 바꿈)이 **러너 자원 부족인지 제품 결함인지 가를 수 없다** — 워크플로 말미의 `kubectl get pods -A` 덤프에 테스트 파드가 하나도 없기 때문이다. 각 spec 의 AfterAll 이 namespace 를 먼저 지우므로 덤프 시점엔 kube-system 과 operator 만 남는다 (실측 확인). 진단은 실패 *직후* 해야 한다. 공용 헬퍼 dumpNamespaceOnFailure 를 두고 failover/backup_restore 의 AfterEach 에서 호출한다 — pods/pvc/jobs/CR/events + operator 로그 tail 을 남긴다. e2e_test.go 에만 있던 패턴을 필요한 곳으로 넓힌 것이다. 이 PR 은 실패를 고치지 않는다. **고칠 수 있게 만든다** — 추측으로 타임아웃을 올리는 대신 실제 상태를 보고 판단하기 위한 선행 작업이다. verify: go vet -tags=e2e ./test/e2e/ → exit 0 / gofmt 출력 없음 Signed-off-by: 태환 박 Co-Authored-By: Claude Opus 5 --- test/e2e/backup_restore_test.go | 2 ++ test/e2e/e2e_suite_test.go | 38 +++++++++++++++++++++++++++++++++ test/e2e/failover_test.go | 2 ++ 3 files changed, 42 insertions(+) diff --git a/test/e2e/backup_restore_test.go b/test/e2e/backup_restore_test.go index 7c6b8e86..45fb5ccf 100644 --- a/test/e2e/backup_restore_test.go +++ b/test/e2e/backup_restore_test.go @@ -64,6 +64,8 @@ spec: Expect(err).NotTo(HaveOccurred(), "Valkey CR apply") }) + AfterEach(func() { dumpNamespaceOnFailure(brNamespace) }) + AfterAll(func() { _, _ = utils.Run(exec.Command("kubectl", "delete", "valkeybackup", brBackup, "-n", brNamespace, "--ignore-not-found")) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 91b59c18..3625c825 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -202,3 +202,41 @@ func teardownPrometheusOperatorCRDs() { By("uninstalling Prometheus Operator CRDs") utils.UninstallPrometheusOperatorCRDs() } + +// dumpNamespaceOnFailure — spec 이 실패했을 때 해당 namespace 의 상태를 남긴다. +// +// 각 spec 의 AfterAll 이 namespace 를 지우므로, 워크플로 말미의 `kubectl get pods -A` +// 덤프에는 **테스트 파드가 하나도 남지 않는다** — 실측으로 확인했다(실패 덤프에 +// kube-system 과 operator 만 있었다). 그래서 진단은 실패 *직후* 여기서 해야 한다. +// +// 남은 타임아웃 실패(backup 이 Completed 에 못 감 / failover 가 primary 를 못 바꿈)가 +// 러너 자원 부족인지 제품 결함인지 가르려면 이 정보가 필요하다. +func dumpNamespaceOnFailure(ns string) { + if !CurrentSpecReport().Failed() { + return + } + for _, probe := range []struct { + label string + args []string + }{ + {"pods", []string{"get", "pods", "-n", ns, "-o", "wide"}}, + {"pvc", []string{"get", "pvc", "-n", ns}}, + {"jobs", []string{"get", "jobs", "-n", ns}}, + {"valkey CRs", []string{"get", "valkey,valkeycluster,valkeybackup,valkeyrestore", "-n", ns, "-o", "wide"}}, + {"events", []string{"get", "events", "-n", ns, "--sort-by=.lastTimestamp"}}, + } { + out, err := utils.Run(exec.Command("kubectl", probe.args...)) + if err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "[dump] %s (%s): %v\n", probe.label, ns, err) + continue + } + _, _ = fmt.Fprintf(GinkgoWriter, "[dump] %s (%s):\n%s\n", probe.label, ns, out) + } + + // operator 로그 tail — reconcile 이 왜 멈췄는지의 1차 단서. + out, err := utils.Run(exec.Command("kubectl", "-n", "valkey-operator-system", "logs", + "deploy/valkey-operator-controller-manager", "--tail=120")) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "[dump] operator logs (tail 120):\n%s\n", out) + } +} diff --git a/test/e2e/failover_test.go b/test/e2e/failover_test.go index 5e138f0b..177d93f6 100644 --- a/test/e2e/failover_test.go +++ b/test/e2e/failover_test.go @@ -62,6 +62,8 @@ spec: Expect(err).NotTo(HaveOccurred(), "Valkey CR apply") }) + AfterEach(func() { dumpNamespaceOnFailure(failoverNamespace) }) + AfterAll(func() { _, _ = utils.Run(exec.Command("kubectl", "delete", "valkey", failoverCRName, "-n", failoverNamespace, "--ignore-not-found"))