From 1240bdf7131f4c48e71aee4bb5fd1aa26f9a1d3c Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 7 Sep 2026 09:55:02 +0200 Subject: [PATCH 1/2] feat: use cloud profile storage type and size as defaults for machine classes Signed-off-by: Niclas Schad --- pkg/controller/worker/machines.go | 14 ++++++ pkg/controller/worker/machines_test.go | 68 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/pkg/controller/worker/machines.go b/pkg/controller/worker/machines.go index c81ba5fb..412bcee4 100644 --- a/pkg/controller/worker/machines.go +++ b/pkg/controller/worker/machines.go @@ -205,6 +205,20 @@ func (w *workerDelegate) generateMachineConfig(ctx context.Context) error { // specifying the volume type requires a custom volume size to be specified too. if pool.Volume != nil && pool.Volume.Type != nil { machineClassSpec["rootDiskType"] = *pool.Volume.Type + } else if machineTypeFromCloudProfile != nil && + machineTypeFromCloudProfile.Storage != nil && + machineTypeFromCloudProfile.Storage.Type != "" && + machineTypeFromCloudProfile.Storage.Type != "default" { + // Use the storage type from the cloud profile as the default if not explicitly set in the shoot spec. + machineClassSpec["rootDiskType"] = machineTypeFromCloudProfile.Storage.Type + if machineTypeFromCloudProfile.Storage.StorageSize != nil { + cloudProfileVolumeSize, err := worker.DiskSize(machineTypeFromCloudProfile.Storage.StorageSize.String()) + if err == nil && cloudProfileVolumeSize > 0 { + if _, alreadySet := machineClassSpec["rootDiskSize"]; !alreadySet { + machineClassSpec["rootDiskSize"] = cloudProfileVolumeSize + } + } + } } if machineImage.ID != "" { diff --git a/pkg/controller/worker/machines_test.go b/pkg/controller/worker/machines_test.go index c8101e98..1f49c8a2 100644 --- a/pkg/controller/worker/machines_test.go +++ b/pkg/controller/worker/machines_test.go @@ -6,6 +6,7 @@ package worker_test import ( "context" + "embed" "encoding/json" "fmt" "maps" @@ -32,6 +33,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -1139,6 +1141,72 @@ var _ = Describe("Machines", func() { Expect(result[0].AutoPreserveFailedMachineMax).To(Equal(int32(0))) Expect(result[1].AutoPreserveFailedMachineMax).To(Equal(int32(0))) }) + It("should use storage type and size from cloud profile machine type as default when no pool volume is specified", func() { + premiumStorageSize := resource.MustParse("64Gi") + clusterWithPremiumMachineType := &extensionscontroller.Cluster{ + CloudProfile: cluster.CloudProfile.DeepCopy(), + Shoot: cluster.Shoot, + Seed: cluster.Seed, + } + clusterWithPremiumMachineType.CloudProfile.Spec.MachineTypes = []gardencorev1beta1.MachineType{ + { + Name: machineType, + Capabilities: capabilitiesAmd, + Storage: &gardencorev1beta1.MachineTypeStorage{ + Class: "standard", + StorageSize: &premiumStorageSize, + Type: "premium", + }, + }, + { + Name: machineTypeArm, + Architecture: ptr.To(archARM), + Capabilities: capabilitiesArm, + }, + } + + machineClassPath := filepath.Join("internal", "machineclass") + if useStackitMCM { + machineClassPath = filepath.Join("internal", "machineclass-stackit") + } + + workerDelegate, _ = NewWorkerDelegate(c, scheme, chartApplier, "", w, clusterWithPremiumMachineType, customLabelDomain) + + var capturedMachineClasses []map[string]interface{} + chartApplier. + EXPECT(). + ApplyFromEmbeddedFS( + ctx, + charts.InternalChart, + machineClassPath, + namespace, + "machineclass", + gomock.AssignableToTypeOf(kubernetes.Values(nil)), + ). + DoAndReturn(func(_ context.Context, _ embed.FS, _, _, _ string, opts ...kubernetes.ApplyOption) error { + applyOpts := &kubernetes.ApplyOptions{} + for _, o := range opts { + o.MutateApplyOptions(applyOpts) + } + if values, ok := applyOpts.Values.(map[string]interface{}); ok { + if classes, ok := values["machineClasses"].([]map[string]interface{}); ok { + capturedMachineClasses = classes + } + } + return nil + }) + + err := workerDelegate.DeployMachineClasses(ctx) + Expect(err).NotTo(HaveOccurred()) + + Expect(capturedMachineClasses).NotTo(BeEmpty()) + for _, class := range capturedMachineClasses { + if class["machineType"] == machineType { + Expect(class).To(HaveKeyWithValue("rootDiskType", "premium"), "expected rootDiskType to be set from cloud profile storage type") + Expect(class).To(HaveKeyWithValue("rootDiskSize", 64), "expected rootDiskSize to be set from cloud profile storage size") + } + } + }) }, Entry("with capabilities and using imageIDs", true, false, false), Entry("with capabilities and using imageIDs with STACKIT mcm", true, false, true), From 00ebf961b0eebe1f2e1b358bcfafee5210e763c3 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 7 Sep 2026 13:23:12 +0200 Subject: [PATCH 2/2] Modernize Signed-off-by: Niclas Schad --- pkg/controller/worker/machines_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/controller/worker/machines_test.go b/pkg/controller/worker/machines_test.go index 1f49c8a2..12d293bd 100644 --- a/pkg/controller/worker/machines_test.go +++ b/pkg/controller/worker/machines_test.go @@ -33,7 +33,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -1160,7 +1159,7 @@ var _ = Describe("Machines", func() { }, { Name: machineTypeArm, - Architecture: ptr.To(archARM), + Architecture: new(archARM), Capabilities: capabilitiesArm, }, } @@ -1172,7 +1171,7 @@ var _ = Describe("Machines", func() { workerDelegate, _ = NewWorkerDelegate(c, scheme, chartApplier, "", w, clusterWithPremiumMachineType, customLabelDomain) - var capturedMachineClasses []map[string]interface{} + var capturedMachineClasses []map[string]any chartApplier. EXPECT(). ApplyFromEmbeddedFS( @@ -1188,8 +1187,8 @@ var _ = Describe("Machines", func() { for _, o := range opts { o.MutateApplyOptions(applyOpts) } - if values, ok := applyOpts.Values.(map[string]interface{}); ok { - if classes, ok := values["machineClasses"].([]map[string]interface{}); ok { + if values, ok := applyOpts.Values.(map[string]any); ok { + if classes, ok := values["machineClasses"].([]map[string]any); ok { capturedMachineClasses = classes } }