@@ -370,6 +370,120 @@ var _ = Describe("MarimoNotebook Controller", func() {
370370 })
371371 })
372372
373+ Context ("When updating a MarimoNotebook" , func () {
374+ var notebook * marimov1alpha1.MarimoNotebook
375+ var namespacedName types.NamespacedName
376+
377+ BeforeEach (func () {
378+ notebook = & marimov1alpha1.MarimoNotebook {
379+ ObjectMeta : metav1.ObjectMeta {
380+ Name : "test-update-" + randString (),
381+ Namespace : "default" ,
382+ },
383+ Spec : marimov1alpha1.MarimoNotebookSpec {
384+ Source : "https://github.com/marimo-team/marimo.git" ,
385+ Storage : & marimov1alpha1.StorageSpec {Size : "1Gi" },
386+ },
387+ }
388+ namespacedName = types.NamespacedName {
389+ Name : notebook .Name ,
390+ Namespace : notebook .Namespace ,
391+ }
392+ })
393+
394+ AfterEach (func () {
395+ _ = k8sClient .Delete (ctx , notebook )
396+ })
397+
398+ It ("should update Service ports when a sidecar with a port is added" , func () {
399+ pausePort := int32 (9090 )
400+
401+ By ("creating the MarimoNotebook with storage" )
402+ Expect (k8sClient .Create (ctx , notebook )).To (Succeed ())
403+
404+ By ("waiting for Service to be created with only the main port" )
405+ svc := & corev1.Service {}
406+ Eventually (func () error {
407+ return k8sClient .Get (ctx , namespacedName , svc )
408+ }, timeout , interval ).Should (Succeed ())
409+ Expect (svc .Spec .Ports ).To (HaveLen (1 ))
410+
411+ By ("updating the MarimoNotebook to add a pause sidecar with a container port" )
412+ nb := & marimov1alpha1.MarimoNotebook {}
413+ Expect (k8sClient .Get (ctx , namespacedName , nb )).To (Succeed ())
414+ nb .Spec .Sidecars = []marimov1alpha1.SidecarSpec {
415+ {
416+ Name : "pause" ,
417+ Image : "registry.k8s.io/pause:3.9" ,
418+ ExposePort : & pausePort ,
419+ },
420+ }
421+ Expect (k8sClient .Update (ctx , nb )).To (Succeed ())
422+
423+ By ("checking Service is updated to include the sidecar port" )
424+ Eventually (func () bool {
425+ if err := k8sClient .Get (ctx , namespacedName , svc ); err != nil {
426+ return false
427+ }
428+ for _ , port := range svc .Spec .Ports {
429+ if port .Name == "pause" && port .Port == pausePort {
430+ return true
431+ }
432+ }
433+ return false
434+ }, timeout , interval ).Should (BeTrue (), "Service should expose the pause sidecar port" )
435+
436+ Expect (svc .Spec .Ports ).To (HaveLen (2 ))
437+ })
438+
439+ It ("should recreate Pod when env vars are changed" , func () {
440+ By ("creating the MarimoNotebook" )
441+ Expect (k8sClient .Create (ctx , notebook )).To (Succeed ())
442+
443+ By ("waiting for initial Pod to be created" )
444+ pod := & corev1.Pod {}
445+ Eventually (func () error {
446+ return k8sClient .Get (ctx , namespacedName , pod )
447+ }, timeout , interval ).Should (Succeed ())
448+ originalUID := pod .UID
449+
450+ By ("updating the notebook to add an env var" )
451+ nb := & marimov1alpha1.MarimoNotebook {}
452+ Expect (k8sClient .Get (ctx , namespacedName , nb )).To (Succeed ())
453+ nb .Spec .Env = []corev1.EnvVar {
454+ {Name : "MY_VAR" , Value : "hello" },
455+ }
456+ Expect (k8sClient .Update (ctx , nb )).To (Succeed ())
457+
458+ By ("verifying Pod is recreated with the new env var" )
459+ Eventually (func () bool {
460+ if err := k8sClient .Get (ctx , namespacedName , pod ); err != nil {
461+ return false
462+ }
463+ if pod .UID == originalUID {
464+ return false // still the old pod
465+ }
466+ for _ , env := range pod .Spec .Containers [0 ].Env {
467+ if env .Name == "MY_VAR" && env .Value == "hello" {
468+ return true
469+ }
470+ }
471+ return false
472+ }, timeout , interval ).Should (BeTrue (), "Pod should be recreated with MY_VAR env var" )
473+ })
474+
475+ It ("should reject changes to the storage attribute" , func () {
476+ By ("creating the MarimoNotebook with storage" )
477+ Expect (k8sClient .Create (ctx , notebook )).To (Succeed ())
478+
479+ By ("attempting to update the storage attribute" )
480+ nb := & marimov1alpha1.MarimoNotebook {}
481+ Expect (k8sClient .Get (ctx , namespacedName , nb )).To (Succeed ())
482+ nb .Spec .Storage = & marimov1alpha1.StorageSpec {Size : "2Gi" }
483+ Expect (k8sClient .Update (ctx , nb )).To (MatchError (ContainSubstring ("storage is immutable once set" )))
484+ })
485+ })
486+
373487 Context ("When deleting a MarimoNotebook" , func () {
374488 It ("should clean up owned resources via garbage collection" , func () {
375489 notebook := & marimov1alpha1.MarimoNotebook {
0 commit comments