@@ -393,6 +393,97 @@ var _ = Describe("The ManagedCloudProfile reconciler", func() {
393393 Expect (k8sClient .Delete (ctx , & cloudProfile )).To (Succeed ())
394394 })
395395
396+ It ("preserves machine image versions referenced by Shoot workers" , func (ctx SpecContext ) {
397+ var cloudProfile gardenerv1beta1.CloudProfile
398+ cloudProfile .Name = "test-gc-shoot-preserve"
399+ cloudProfile .Spec .Regions = []gardenerv1beta1.Region {{Name : "foo" }}
400+ cloudProfile .Spec .MachineTypes = []gardenerv1beta1.MachineType {{Name : "baz" }}
401+ cloudProfile .Spec .MachineImages = []gardenerv1beta1.MachineImage {
402+ {
403+ Name : "shoot-preserve-image" ,
404+ Versions : []gardenerv1beta1.MachineImageVersion {
405+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.0" }, Architectures : []string {"amd64" }},
406+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.1+abc" }, Architectures : []string {"amd64" }},
407+ },
408+ },
409+ }
410+ Expect (k8sClient .Create (ctx , & cloudProfile )).To (Succeed ())
411+
412+ var shoot gardenerv1beta1.Shoot
413+ shoot .Name = "test-shoot"
414+ shoot .Namespace = metav1 .NamespaceDefault
415+ shoot .Spec .CloudProfile = & gardenerv1beta1.CloudProfileReference {Name : cloudProfile .Name }
416+ shoot .Spec .Provider .Workers = []gardenerv1beta1.Worker {
417+ {
418+ Name : "worker1" ,
419+ Machine : gardenerv1beta1.Machine {
420+ Image : & gardenerv1beta1.ShootMachineImage {
421+ Name : "shoot-preserve-image" ,
422+ Version : ptr .To ("1.0.0" ),
423+ },
424+ },
425+ },
426+ }
427+ Expect (k8sClient .Create (ctx , & shoot )).To (Succeed ())
428+
429+ var mcp v1alpha1.ManagedCloudProfile
430+ mcp .Name = "test-gc-shoot-preserve"
431+ mcp .Spec .CloudProfile = v1alpha1.CloudProfileSpec {
432+ Regions : []gardenerv1beta1.Region {{Name : "foo" }},
433+ MachineImages : []gardenerv1beta1.MachineImage {
434+ {
435+ Name : "shoot-preserve-image" ,
436+ Versions : []gardenerv1beta1.MachineImageVersion {
437+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.0" }, Architectures : []string {"amd64" }},
438+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.1+abc" }, Architectures : []string {"amd64" }},
439+ },
440+ },
441+ },
442+ MachineTypes : []gardenerv1beta1.MachineType {{Name : "baz" }},
443+ }
444+ mcp .Spec .MachineImageUpdates = []v1alpha1.MachineImageUpdate {
445+ {
446+ ImageName : "shoot-preserve-image" ,
447+ Source : v1alpha1.MachineImageUpdateSource {
448+ OCI : & v1alpha1.MachineImageUpdateSourceOCI {
449+ Registry : registryAddr ,
450+ Repository : "repo" ,
451+ Insecure : true ,
452+ },
453+ },
454+ GarbageCollection : & v1alpha1.GarbageCollectionConfig {
455+ Enabled : true ,
456+ MaxAge : metav1.Duration {Duration : 0 },
457+ },
458+ },
459+ }
460+ Expect (k8sClient .Create (ctx , & mcp )).To (Succeed ())
461+
462+ Eventually (func (g Gomega ) v1alpha1.ReconcileStatus {
463+ g .Expect (k8sClient .Get (ctx , client .ObjectKeyFromObject (& mcp ), & mcp )).To (Succeed ())
464+ return mcp .Status .Status
465+ }).Should (Equal (v1alpha1 .SucceededReconcileStatus ))
466+
467+ Eventually (func (g Gomega ) []string {
468+ g .Expect (k8sClient .Get (ctx , client .ObjectKeyFromObject (& cloudProfile ), & cloudProfile )).To (Succeed ())
469+ if len (cloudProfile .Spec .MachineImages ) == 0 {
470+ return []string {}
471+ }
472+ versions := []string {}
473+ for _ , v := range cloudProfile .Spec .MachineImages [0 ].Versions {
474+ versions = append (versions , v .Version )
475+ }
476+ return versions
477+ }).Should (And (
478+ ContainElement ("1.0.0" ),
479+ Not (ContainElement ("1.0.1+abc" )),
480+ ))
481+
482+ Expect (k8sClient .Delete (ctx , & mcp )).To (Succeed ())
483+ Expect (k8sClient .Delete (ctx , & cloudProfile )).To (Succeed ())
484+ Expect (k8sClient .Delete (ctx , & shoot )).To (Succeed ())
485+ })
486+
396487 It ("handles missing credential for GC OCI source" , func (ctx SpecContext ) {
397488 var mcp v1alpha1.ManagedCloudProfile
398489 mcp .Name = "test-gc-cred-error"
@@ -609,4 +700,97 @@ var _ = Describe("The ManagedCloudProfile reconciler", func() {
609700 Expect (k8sClient .Delete (ctx , & cloudProfile )).To (Succeed ())
610701 })
611702
703+ It ("updates ProviderConfig when garbage collecting machine image versions" , func (ctx SpecContext ) {
704+ var cloudProfile gardenerv1beta1.CloudProfile
705+ cloudProfile .Name = "test-gc-provider-config"
706+ cloudProfile .Spec .Regions = []gardenerv1beta1.Region {{Name : "foo" }}
707+ cloudProfile .Spec .MachineTypes = []gardenerv1beta1.MachineType {{Name : "baz" }}
708+ cloudProfile .Spec .MachineImages = []gardenerv1beta1.MachineImage {
709+ {
710+ Name : "provider-config-image" ,
711+ Versions : []gardenerv1beta1.MachineImageVersion {
712+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.0" }, Architectures : []string {"amd64" }},
713+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.1+abc" }, Architectures : []string {"amd64" }},
714+ },
715+ },
716+ }
717+
718+ var cfg providercfg.CloudProfileConfig
719+ cfg .MachineImages = []providercfg.MachineImages {
720+ {
721+ Name : "provider-config-image" ,
722+ Versions : []providercfg.MachineImageVersion {
723+ {Image : "repo/provider-config-image:1.0.0" },
724+ {Image : "repo/provider-config-image:1.0.1+abc" },
725+ },
726+ },
727+ }
728+ raw , err := json .Marshal (cfg )
729+ Expect (err ).To (Succeed ())
730+ cloudProfile .Spec .ProviderConfig = & runtime.RawExtension {Raw : raw }
731+ Expect (k8sClient .Create (ctx , & cloudProfile )).To (Succeed ())
732+
733+ var mcp v1alpha1.ManagedCloudProfile
734+ mcp .Name = "test-gc-provider-config"
735+ mcp .Spec .CloudProfile = v1alpha1.CloudProfileSpec {
736+ Regions : []gardenerv1beta1.Region {{Name : "foo" }},
737+ MachineImages : []gardenerv1beta1.MachineImage {
738+ {
739+ Name : "provider-config-image" ,
740+ Versions : []gardenerv1beta1.MachineImageVersion {
741+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.0" }, Architectures : []string {"amd64" }},
742+ {ExpirableVersion : gardenerv1beta1.ExpirableVersion {Version : "1.0.1+abc" }, Architectures : []string {"amd64" }},
743+ },
744+ },
745+ },
746+ MachineTypes : []gardenerv1beta1.MachineType {{Name : "baz" }},
747+ }
748+ mcp .Spec .MachineImageUpdates = []v1alpha1.MachineImageUpdate {
749+ {
750+ ImageName : "provider-config-image" ,
751+ Source : v1alpha1.MachineImageUpdateSource {
752+ OCI : & v1alpha1.MachineImageUpdateSourceOCI {
753+ Registry : registryAddr ,
754+ Repository : "repo" ,
755+ Insecure : true ,
756+ },
757+ },
758+ GarbageCollection : & v1alpha1.GarbageCollectionConfig {
759+ Enabled : true ,
760+ MaxAge : metav1.Duration {Duration : 0 },
761+ },
762+ },
763+ }
764+ Expect (k8sClient .Create (ctx , & mcp )).To (Succeed ())
765+
766+ Eventually (func (g Gomega ) v1alpha1.ReconcileStatus {
767+ g .Expect (k8sClient .Get (ctx , client .ObjectKeyFromObject (& mcp ), & mcp )).To (Succeed ())
768+ return mcp .Status .Status
769+ }).Should (Equal (v1alpha1 .SucceededReconcileStatus ))
770+
771+ Eventually (func (g Gomega ) []string {
772+ g .Expect (k8sClient .Get (ctx , client .ObjectKeyFromObject (& cloudProfile ), & cloudProfile )).To (Succeed ())
773+ if cloudProfile .Spec .ProviderConfig == nil {
774+ return []string {}
775+ }
776+ var updatedCfg providercfg.CloudProfileConfig
777+ if err := json .Unmarshal (cloudProfile .Spec .ProviderConfig .Raw , & updatedCfg ); err != nil {
778+ return []string {}
779+ }
780+ for _ , img := range updatedCfg .MachineImages {
781+ if img .Name == "provider-config-image" {
782+ images := make ([]string , len (img .Versions ))
783+ for i , v := range img .Versions {
784+ images [i ] = v .Image
785+ }
786+ return images
787+ }
788+ }
789+ return []string {}
790+ }).Should (BeEmpty ())
791+
792+ Expect (k8sClient .Delete (ctx , & mcp )).To (Succeed ())
793+ Expect (k8sClient .Delete (ctx , & cloudProfile )).To (Succeed ())
794+ })
795+
612796})
0 commit comments