@@ -25,6 +25,7 @@ import (
25
25
"os"
26
26
"os/exec"
27
27
"path/filepath"
28
+ "strings"
28
29
"time"
29
30
30
31
. "github.com/onsi/ginkgo/v2"
@@ -319,7 +320,147 @@ var _ = Describe("Manager", Ordered, func() {
319
320
320
321
// +kubebuilder:scaffold:e2e-webhooks-checks
321
322
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.
323
464
// Consider applying sample/CR(s) and check their status and/or verifying
324
465
// the reconciliation by using the metrics, i.e.:
325
466
// metricsOutput := getMetricsOutput()
0 commit comments