Skip to content
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
15 changes: 11 additions & 4 deletions test/e2e/backup_restore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ const (
brValkey = "vk-br-test"
brBackup = "vk-br-backup-1"
brRestore = "vk-br-restore-1"

// 승급 대상 버전은 상수 하나로 둔다 — patch 본문과 이미지·CR 단언이 각자
// 리터럴을 들고 있으면 한 곳만 고쳤을 때 나머지가 조용히 어긋난다
// (형제 e2e_test.go 에서 실제로 그렇게 어긋나 status 단언이 타임아웃했다).
// major 승급은 webhook 이 금지하므로 patch 승급만 쓴다.
brUpgradeVersion = "8.1.7"
brUpgradeImage = "docker.io/valkey/valkey:" + brUpgradeVersion
)

var _ = Describe("ValkeyBackup + ValkeyRestore (Standalone PVC)", Ordered, func() {
Expand Down Expand Up @@ -196,7 +203,7 @@ spec:
// version patch 시 STS image propagate + Pod rotation + RDB 호환성 회귀 가드.
Context("Restored 인스턴스의 8.1.6 → 8.1.7 version patch chain (V2)", func() {
It("spec.version.version 8.1.6 → 8.1.7 patch (restored 후)", func() {
patch := `{"spec":{"version":{"version":"8.1.7","image":"docker.io/valkey/valkey"}}}`
patch := fmt.Sprintf(`{"spec":{"version":{"version":%q,"image":"docker.io/valkey/valkey"}}}`, brUpgradeVersion)
_, err := utils.Run(exec.Command("kubectl", "patch", "valkey",
brValkey, "-n", brNamespace,
"--type=merge", "-p", patch))
Expand All @@ -210,7 +217,7 @@ spec:
"-o", "jsonpath={.spec.template.spec.containers[0].image}"))
return out
}, 60*time.Second, 5*time.Second).Should(
Equal("docker.io/valkey/valkey:8.1.7"),
Equal(brUpgradeImage),
"restored 인스턴스의 STS image 가 8.1.7 로 propagate (가설 A)")
})

Expand All @@ -221,7 +228,7 @@ spec:
"-o", "jsonpath={.spec.containers[0].image}"))
return out
}, 3*time.Minute, 10*time.Second).Should(
Equal("docker.io/valkey/valkey:8.1.7"),
Equal(brUpgradeImage),
"restored 인스턴스의 Pod 가 8.1.7 로 재생성 (가설 C)")
})

Expand All @@ -232,7 +239,7 @@ spec:
"-o", "jsonpath={.spec.version.version}"))
return out
}, 30*time.Second, 5*time.Second).Should(
Equal("8.1.7"),
Equal(brUpgradeVersion),
"webhook defaulter 가 8.1.7 → 8.1.6 reverting 안함 (가설 B)")
})

Expand Down
39 changes: 32 additions & 7 deletions test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,41 @@ var _ = BeforeSuite(func() {
"deploy/valkey-operator-controller-manager", "--timeout=180s"))
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "controller-manager rollout")

By("waiting for webhook endpoint to be serving")
// endpoint 에 주소가 잡혔다는 것만으로는 부족하다 — readiness probe 는 healthz(8081)
// 를 볼 뿐 webhook TLS 포트(9443)를 보지 않으므로, Pod 가 Ready 이고 endpoint 에
// 주소가 있어도 webhook 서버가 아직 안 듣고 있을 수 있다. 실측: endpoint 대기 통과
// **0.5초 뒤** 첫 CR apply 가 `connection refused` 로 죽었다.
//
// 그래서 **실제로 webhook 을 호출해** 응답하는지 본다. server-side dry-run 은
// admission 체인을 그대로 태우면서 아무것도 저장하지 않으므로 정확한 신호다.
// 검증 거부(4xx)는 "webhook 이 응답했다"는 뜻이라 성공으로 친다 — 연결 실패만 재시도.
By("waiting for the webhook to actually answer (server-side dry-run)")
probe := `apiVersion: cache.keiailab.io/v1alpha1
kind: Valkey
metadata:
name: webhook-readiness-probe
namespace: default
spec:
mode: Standalone
replicas: 1
`
EventuallyWithOffset(1, func() string {
out, err := utils.Run(exec.Command("kubectl", "-n", "valkey-operator-system", "get",
"endpoints", "valkey-operator-webhook-service",
"-o", "jsonpath={.subsets[0].addresses[0].ip}"))
if err != nil {
cmd := exec.Command("kubectl", "apply", "--dry-run=server", "-f", "-")
cmd.Stdin = strings.NewReader(probe)
out, err := utils.Run(cmd)
if err == nil {
return ""
}
return strings.TrimSpace(out)
}, 2*time.Minute, 3*time.Second).ShouldNot(BeEmpty(), "webhook endpoint never became ready")
msg := out + err.Error()
// webhook 에 닿지 못한 경우만 재시도 대상.
if strings.Contains(msg, "failed calling webhook") ||
strings.Contains(msg, "connection refused") ||
strings.Contains(msg, "context deadline exceeded") {
return msg
}
// 그 외(검증 거부 등)는 webhook 이 살아서 응답한 것 — 준비 완료로 본다.
return ""
}, 3*time.Minute, 3*time.Second).Should(BeEmpty(), "webhook never became reachable")
})

var _ = AfterSuite(func() {
Expand Down
15 changes: 10 additions & 5 deletions test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,13 @@ var _ = Describe("Manager", Ordered, func() {
const (
upgradeNamespace = "test-valkey-upgrade-20260507"
upgradeName = "vk-upgrade-test"
oldImage = "docker.io/valkey/valkey:8.1.6"
newImage = "docker.io/valkey/valkey:8.1.7"
// 승급 대상 버전을 상수 하나로 둔다 — patch 본문·기대 이미지·status 단언이
// 각자 리터럴을 들고 있으면 한 곳만 고쳤을 때 나머지가 조용히 어긋난다
// (실제로 그렇게 어긋나 status 단언이 옛 9.0.4 를 기다리며 타임아웃했다).
oldVersion = "8.1.6"
newVersion = "8.1.7"
oldImage = "docker.io/valkey/valkey:" + oldVersion
newImage = "docker.io/valkey/valkey:" + newVersion
)

By("creating a dedicated namespace for upgrade test data")
Expand Down Expand Up @@ -409,11 +414,11 @@ spec:
// 정당하게 거부한다 — "manual major version upgrade is prohibited;
// AutoUpdate automates patch/minor only". spec 의 목적(version patch 가 STS
// template image 로 전파되고 Pod 가 롤링되는지)은 patch 승급으로도 동일 달성.
By("patching spec.version.version to 8.1.7")
By("patching spec.version.version to " + newVersion)
cmd = exec.Command("kubectl", "patch", "valkey", upgradeName,
"-n", upgradeNamespace,
"--type=merge",
"-p", `{"spec":{"version":{"version":"8.1.7"}}}`)
"-p", fmt.Sprintf(`{"spec":{"version":{"version":%q}}}`, newVersion))
_, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())

Expand Down Expand Up @@ -452,7 +457,7 @@ spec:
upgradeName, "-n", upgradeNamespace,
"-o", "jsonpath={.status.version}"))
return out
}, 2*time.Minute, 5*time.Second).Should(Equal("9.0.4"))
}, 2*time.Minute, 5*time.Second).Should(Equal(newVersion))

By("cleaning up upgrade test data before the operator is undeployed")
_, _ = utils.Run(exec.Command("kubectl", "delete", "valkey",
Expand Down
Loading