@@ -25,6 +25,7 @@ import (
2525 "os"
2626 "os/exec"
2727 "path/filepath"
28+ "strings"
2829 "time"
2930
3031 . "github.com/onsi/ginkgo/v2"
@@ -178,9 +179,14 @@ var _ = Describe("Manager", Ordered, func() {
178179 cmd := exec .Command ("kubectl" , "create" , "clusterrolebinding" , metricsRoleBindingName ,
179180 "--clusterrole=project-metrics-reader" ,
180181 fmt .Sprintf ("--serviceaccount=%s:%s" , namespace , serviceAccountName ),
181- )
182- _ , err := utils .Run (cmd )
183- Expect (err ).NotTo (HaveOccurred (), "Failed to create ClusterRoleBinding" )
182+ "--dry-run=client" , "-o" , "yaml" )
183+ output , err := utils .Run (cmd )
184+ Expect (err ).NotTo (HaveOccurred (), "Failed to generate ClusterRoleBinding YAML" )
185+
186+ cmd = exec .Command ("kubectl" , "apply" , "-f" , "-" )
187+ cmd .Stdin = strings .NewReader (output )
188+ _ , err = utils .Run (cmd )
189+ Expect (err ).NotTo (HaveOccurred (), "Failed to apply ClusterRoleBinding" )
184190
185191 By ("validating that the metrics service is available" )
186192 cmd = exec .Command ("kubectl" , "get" , "service" , metricsServiceName , "-n" , namespace )
@@ -319,7 +325,86 @@ var _ = Describe("Manager", Ordered, func() {
319325
320326 // +kubebuilder:scaffold:e2e-webhooks-checks
321327
322- // TODO: Customize the e2e test suite with scenarios specific to your project.
328+ It ("should validate webhook conversion between v1 and v2 CronJob versions" , func () {
329+ By ("applying the v1 CronJob sample" )
330+ cmd := exec .Command ("kubectl" , "apply" , "-f" , "config/samples/batch_v1_cronjob.yaml" , "-n" , namespace )
331+ _ , err := utils .Run (cmd )
332+ Expect (err ).NotTo (HaveOccurred (), "Failed to apply v1 CronJob sample" )
333+
334+ By ("checking what is actually stored for v1 CronJob" )
335+ Eventually (func (g Gomega ) {
336+ cmd := exec .Command ("kubectl" , "get" , "cronjob.batch.tutorial.kubebuilder.io" , "cronjob-sample" , "-n" , namespace , "-o" , "json" )
337+ output , err := utils .Run (cmd )
338+ g .Expect (err ).NotTo (HaveOccurred ())
339+
340+ // Log the full output for debugging
341+ fmt .Printf ("V1 CronJob JSON: %s\n " , output )
342+
343+ var cronJobData map [string ]interface {}
344+ err = json .Unmarshal ([]byte (output ), & cronJobData )
345+ g .Expect (err ).NotTo (HaveOccurred ())
346+
347+ spec , ok := cronJobData ["spec" ].(map [string ]interface {})
348+ g .Expect (ok ).To (BeTrue (), "Failed to get spec from CronJob" )
349+
350+ scheduleValue := spec ["schedule" ]
351+ g .Expect (scheduleValue ).NotTo (BeNil (), "Schedule field should exist" )
352+
353+ fmt .Printf ("Schedule value: %+v (type: %T)\n " , scheduleValue , scheduleValue )
354+ }).Should (Succeed ())
355+
356+ By ("deleting the v1 CronJob sample" )
357+ cmd = exec .Command ("kubectl" , "delete" , "-f" , "config/samples/batch_v1_cronjob.yaml" , "-n" , namespace , "--ignore-not-found" )
358+ _ , _ = utils .Run (cmd )
359+
360+ By ("applying the v2 CronJob sample" )
361+ cmd = exec .Command ("kubectl" , "apply" , "-f" , "config/samples/batch_v2_cronjob.yaml" , "-n" , namespace )
362+ _ , err = utils .Run (cmd )
363+ Expect (err ).NotTo (HaveOccurred (), "Failed to apply v2 CronJob sample" )
364+
365+ By ("checking what is actually stored for v2 CronJob" )
366+ Eventually (func (g Gomega ) {
367+ cmd := exec .Command ("kubectl" , "get" , "cronjob.batch.tutorial.kubebuilder.io" , "cronjob-sample" , "-n" , namespace , "-o" , "json" )
368+ output , err := utils .Run (cmd )
369+ g .Expect (err ).NotTo (HaveOccurred ())
370+
371+ // Log the full output for debugging
372+ fmt .Printf ("V2 CronJob JSON: %s\n " , output )
373+
374+ var cronJobData map [string ]interface {}
375+ err = json .Unmarshal ([]byte (output ), & cronJobData )
376+ g .Expect (err ).NotTo (HaveOccurred ())
377+
378+ spec , ok := cronJobData ["spec" ].(map [string ]interface {})
379+ g .Expect (ok ).To (BeTrue (), "Failed to get spec from CronJob" )
380+
381+ scheduleValue := spec ["schedule" ]
382+ g .Expect (scheduleValue ).NotTo (BeNil (), "Schedule field should exist" )
383+
384+ fmt .Printf ("Schedule value: %+v (type: %T)\n " , scheduleValue , scheduleValue )
385+ }).Should (Succeed ())
386+
387+ By ("verifying conversion webhook was called by checking controller logs" )
388+ Eventually (func (g Gomega ) {
389+ cmd := exec .Command ("kubectl" , "logs" , controllerPodName , "-n" , namespace )
390+ logs , err := utils .Run (cmd )
391+ g .Expect (err ).NotTo (HaveOccurred ())
392+
393+ fmt .Printf ("Controller logs:\n %s\n " , logs )
394+
395+ // Look for any conversion log messages
396+ hasConversion := strings .Contains (logs , "ConvertTo" ) || strings .Contains (logs , "ConvertFrom" )
397+ if hasConversion {
398+ fmt .Printf ("Found conversion logs!\n " )
399+ } else {
400+ fmt .Printf ("No conversion logs found\n " )
401+ }
402+ }, 30 * time .Second , 2 * time .Second ).Should (Succeed ())
403+
404+ By ("cleaning up the v2 CronJob sample" )
405+ cmd = exec .Command ("kubectl" , "delete" , "-f" , "config/samples/batch_v2_cronjob.yaml" , "-n" , namespace , "--ignore-not-found" )
406+ _ , _ = utils .Run (cmd )
407+ })
323408 // Consider applying sample/CR(s) and check their status and/or verifying
324409 // the reconciliation by using the metrics, i.e.:
325410 // metricsOutput := getMetricsOutput()
0 commit comments