Skip to content

Commit 2fda561

Browse files
committed
feat: add e2e test to validate webhook conversion between versions
1 parent b77b135 commit 2fda561

File tree

1 file changed

+142
-1
lines changed
  • docs/book/src/multiversion-tutorial/testdata/project/test/e2e

1 file changed

+142
-1
lines changed

docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_test.go

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"os"
2626
"os/exec"
2727
"path/filepath"
28+
"strings"
2829
"time"
2930

3031
. "github.com/onsi/ginkgo/v2"
@@ -319,7 +320,147 @@ var _ = Describe("Manager", Ordered, func() {
319320

320321
// +kubebuilder:scaffold:e2e-webhooks-checks
321322

322-
// TODO: Customize the e2e test suite with scenarios specific to your project.
323+
It("should validate webhook conversion between v1 and v2 CronJob versions", func() {
324+
By("creating a CronJob in v1 format")
325+
326+
// Create a CronJob using v1 API with traditional cron schedule
327+
cronJobV1 := `
328+
apiVersion: batch.tutorial.kubebuilder.io/v1
329+
kind: CronJob
330+
metadata:
331+
name: test-cronjob-v1
332+
namespace: ` + namespace + `
333+
spec:
334+
schedule: "*/5 * * * *"
335+
jobTemplate:
336+
spec:
337+
template:
338+
spec:
339+
containers:
340+
- name: hello
341+
image: busybox:1.28
342+
imagePullPolicy: IfNotPresent
343+
command:
344+
- /bin/sh
345+
- -c
346+
- date; echo Hello from the Kubernetes cluster
347+
restartPolicy: OnFailure
348+
`
349+
350+
// Apply the v1 CronJob
351+
cmd := exec.Command("kubectl", "apply", "-f", "-")
352+
cmd.Stdin = strings.NewReader(cronJobV1)
353+
_, err := utils.Run(cmd)
354+
Expect(err).NotTo(HaveOccurred(), "Failed to create v1 CronJob")
355+
356+
// Verify the CronJob was created and is accessible
357+
By("verifying the v1 CronJob was created successfully")
358+
Eventually(func(g Gomega) {
359+
cmd := exec.Command("kubectl", "get", "cronjob.batch.tutorial.kubebuilder.io", "test-cronjob-v1", "-n", namespace, "-o", "json")
360+
output, err := utils.Run(cmd)
361+
g.Expect(err).NotTo(HaveOccurred())
362+
363+
var cronJobData map[string]interface{}
364+
err = json.Unmarshal([]byte(output), &cronJobData)
365+
g.Expect(err).NotTo(HaveOccurred())
366+
367+
// Verify this is indeed a resource with string schedule
368+
spec, ok := cronJobData["spec"].(map[string]interface{})
369+
g.Expect(ok).To(BeTrue(), "Failed to get spec from CronJob")
370+
371+
schedule, ok := spec["schedule"].(string)
372+
g.Expect(ok).To(BeTrue(), "Schedule should be a string in stored format")
373+
g.Expect(schedule).To(Equal("*/5 * * * *"), "Schedule should match the applied format")
374+
}).Should(Succeed())
375+
376+
By("creating a CronJob in v2 format to test structured schedule")
377+
378+
// Create a CronJob using v2 API with structured schedule
379+
cronJobV2 := `
380+
apiVersion: batch.tutorial.kubebuilder.io/v2
381+
kind: CronJob
382+
metadata:
383+
name: test-cronjob-v2
384+
namespace: ` + namespace + `
385+
spec:
386+
schedule:
387+
minute: "0"
388+
hour: "6"
389+
dayOfMonth: "1"
390+
jobTemplate:
391+
spec:
392+
template:
393+
spec:
394+
containers:
395+
- name: hello
396+
image: busybox:1.28
397+
imagePullPolicy: IfNotPresent
398+
command:
399+
- /bin/sh
400+
- -c
401+
- date; echo Hello from v2 CronJob
402+
restartPolicy: OnFailure
403+
`
404+
405+
// Apply the v2 CronJob
406+
cmd = exec.Command("kubectl", "apply", "-f", "-")
407+
cmd.Stdin = strings.NewReader(cronJobV2)
408+
_, err = utils.Run(cmd)
409+
Expect(err).NotTo(HaveOccurred(), "Failed to create v2 CronJob")
410+
411+
// Verify the v2 CronJob was created and stored correctly
412+
By("verifying the v2 CronJob was created successfully")
413+
Eventually(func(g Gomega) {
414+
cmd := exec.Command("kubectl", "get", "cronjob.batch.tutorial.kubebuilder.io", "test-cronjob-v2", "-n", namespace, "-o", "json")
415+
output, err := utils.Run(cmd)
416+
g.Expect(err).NotTo(HaveOccurred())
417+
418+
var cronJobData map[string]interface{}
419+
err = json.Unmarshal([]byte(output), &cronJobData)
420+
g.Expect(err).NotTo(HaveOccurred())
421+
422+
spec, ok := cronJobData["spec"].(map[string]interface{})
423+
g.Expect(ok).To(BeTrue(), "Failed to get spec from CronJob")
424+
425+
// The schedule should be stored as string in the hub version (v1) due to conversion
426+
schedule, ok := spec["schedule"].(string)
427+
g.Expect(ok).To(BeTrue(), "Schedule should be stored as string due to hub conversion")
428+
// This should be the converted cron string: minute=0, hour=6, dayOfMonth=1, month=*, dayOfWeek=*
429+
g.Expect(schedule).To(Equal("0 6 1 * *"), "Schedule should be converted to cron format")
430+
}).Should(Succeed())
431+
432+
By("testing conversion by applying patch to trigger webhook conversion")
433+
// Apply a patch to trigger conversion webhook calls
434+
patchData := `{"metadata":{"labels":{"test":"conversion"}}}`
435+
cmd = exec.Command("kubectl", "patch", "cronjob.batch.tutorial.kubebuilder.io", "test-cronjob-v1", "-n", namespace,
436+
"--type", "merge", "-p", patchData)
437+
_, err = utils.Run(cmd)
438+
Expect(err).NotTo(HaveOccurred(), "Failed to patch v1 CronJob")
439+
440+
cmd = exec.Command("kubectl", "patch", "cronjob.batch.tutorial.kubebuilder.io", "test-cronjob-v2", "-n", namespace,
441+
"--type", "merge", "-p", patchData)
442+
_, err = utils.Run(cmd)
443+
Expect(err).NotTo(HaveOccurred(), "Failed to patch v2 CronJob")
444+
445+
By("verifying conversion webhook was called by checking controller logs")
446+
// Check the controller logs for conversion messages
447+
Eventually(func(g Gomega) {
448+
cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
449+
logs, err := utils.Run(cmd)
450+
g.Expect(err).NotTo(HaveOccurred())
451+
452+
// Look for conversion log messages that we added in the conversion functions
453+
g.Expect(logs).To(ContainSubstring("ConvertTo: Converting CronJob from Spoke version v2 to Hub version v1"),
454+
"Should see v2->v1 conversion in logs")
455+
g.Expect(logs).To(ContainSubstring("ConvertFrom: Converting CronJob from Hub version v1 to Spoke version v2"),
456+
"Should see v1->v2 conversion in logs")
457+
}, 30*time.Second, 2*time.Second).Should(Succeed())
458+
459+
// Cleanup the test CronJobs
460+
By("cleaning up test CronJobs")
461+
cmd = exec.Command("kubectl", "delete", "cronjob.batch.tutorial.kubebuilder.io", "test-cronjob-v1", "test-cronjob-v2", "-n", namespace, "--ignore-not-found")
462+
_, _ = utils.Run(cmd) // Use _ for cleanup as it might fail if resources don't exist
463+
}) // TODO: Customize the e2e test suite with scenarios specific to your project.
323464
// Consider applying sample/CR(s) and check their status and/or verifying
324465
// the reconciliation by using the metrics, i.e.:
325466
// metricsOutput := getMetricsOutput()

0 commit comments

Comments
 (0)