Skip to content

Commit e5daea6

Browse files
added more tests
1 parent 4f21398 commit e5daea6

3 files changed

Lines changed: 260 additions & 1 deletion

File tree

controllers/managedcloudprofile_controller_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
})

crd/cloudprofilesync.cobaltcore.dev_managedcloudprofiles.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ spec:
532532
maxAge:
533533
description: |-
534534
MaxAge defines the maximum age for images to keep. Images older than
535-
now MaxAge are eligible for deletion.
535+
now - MaxAge are eligible for deletion.
536536
type: string
537537
type: object
538538
imageName:
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
apiVersion: apiextensions.k8s.io/v1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: shoots.core.gardener.cloud
5+
spec:
6+
group: core.gardener.cloud
7+
versions:
8+
- name: v1beta1
9+
served: true
10+
storage: true
11+
schema:
12+
openAPIV3Schema:
13+
description: Shoot is the schema for the shoots API.
14+
type: object
15+
properties:
16+
apiVersion:
17+
description: |-
18+
APIVersion defines the versioned schema of this representation of an object.
19+
Servers should convert recognized schemas to the latest internal value, and
20+
may reject unrecognized values.
21+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
22+
type: string
23+
kind:
24+
description: |-
25+
Kind is a string value representing the REST resource this object represents.
26+
Servers may infer this from the endpoint the client submits requests to.
27+
Cannot be updated.
28+
In CamelCase.
29+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
30+
type: string
31+
metadata:
32+
type: object
33+
spec:
34+
description: Spec defines the desired state of the Shoot.
35+
type: object
36+
properties:
37+
cloudProfile:
38+
description: Reference to the CloudProfile used by the Shoot.
39+
type: object
40+
properties:
41+
name:
42+
description: Name of the CloudProfile.
43+
type: string
44+
provider:
45+
description: Provider-specific configuration.
46+
type: object
47+
properties:
48+
workers:
49+
description: Worker pools for the Shoot.
50+
type: array
51+
items:
52+
type: object
53+
properties:
54+
machine:
55+
description: Machine configuration for a worker pool.
56+
type: object
57+
properties:
58+
image:
59+
description: Image configuration for worker nodes.
60+
type: object
61+
properties:
62+
name:
63+
description: Machine image name.
64+
type: string
65+
version:
66+
description: Machine image version.
67+
type: string
68+
status:
69+
description: Status contains the current status of the Shoot.
70+
type: object
71+
scope: Namespaced
72+
names:
73+
plural: shoots
74+
singular: shoot
75+
kind: Shoot

0 commit comments

Comments
 (0)