From 18e82bf6cfe8fdfd8189c8cf578b2c90d9d36d60 Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Fri, 26 Sep 2025 18:39:56 +0200 Subject: [PATCH 1/2] fix(ske): make generate cluster payload work for eu02 relates to STACKITCLI-258 / #997 --- internal/pkg/services/ske/utils/utils.go | 28 +++++++---- internal/pkg/services/ske/utils/utils_test.go | 46 ++++++++++++++++++- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/internal/pkg/services/ske/utils/utils.go b/internal/pkg/services/ske/utils/utils.go index 605ca4158..12a18f08f 100644 --- a/internal/pkg/services/ske/utils/utils.go +++ b/internal/pkg/services/ske/utils/utils.go @@ -16,13 +16,9 @@ import ( ) const ( - defaultNodepoolAvailabilityZone = "eu01-3" defaultNodepoolCRI = ske.CRINAME_CONTAINERD - defaultNodepoolMachineType = "b1.2" defaultNodepoolMachineImageName = "flatcar" - defaultNodepoolMaxSurge = 1 defaultNodepoolMaxUnavailable = 0 - defaultNodepoolMaximum = 2 defaultNodepoolMinimum = 1 defaultNodepoolName = "pool-default" defaultNodepoolVolumeType = "storage_premium_perf2" @@ -110,22 +106,34 @@ func getDefaultPayloadKubernetes(resp *ske.ProviderOptions) (*ske.Kubernetes, er } func getDefaultPayloadNodepool(resp *ske.ProviderOptions) (*ske.Nodepool, error) { + if resp.AvailabilityZones == nil || len(*resp.AvailabilityZones) == 0 { + return nil, fmt.Errorf("no availability zones found") + } + availabilityZones := make([]string, len(*resp.AvailabilityZones)) + for i := range *resp.AvailabilityZones { + availabilityZones[i] = (*resp.AvailabilityZones)[i].GetName() + } + + if resp.MachineTypes == nil || len(*resp.MachineTypes) == 0 { + return nil, fmt.Errorf("no machine types found") + } + machineType := (*resp.MachineTypes)[0].GetName() + output := &ske.Nodepool{ - AvailabilityZones: &[]string{ - defaultNodepoolAvailabilityZone, - }, + AvailabilityZones: &availabilityZones, Cri: &ske.CRI{ Name: utils.Ptr(defaultNodepoolCRI), }, Machine: &ske.Machine{ - Type: utils.Ptr(defaultNodepoolMachineType), + Type: &machineType, Image: &ske.Image{ Name: utils.Ptr(defaultNodepoolMachineImageName), }, }, - MaxSurge: utils.Ptr(int64(defaultNodepoolMaxSurge)), + // there must be as many nodes as availability zones are given + MaxSurge: utils.Ptr(int64(len(availabilityZones))), MaxUnavailable: utils.Ptr(int64(defaultNodepoolMaxUnavailable)), - Maximum: utils.Ptr(int64(defaultNodepoolMaximum)), + Maximum: utils.Ptr(int64(len(availabilityZones))), Minimum: utils.Ptr(int64(defaultNodepoolMinimum)), Name: utils.Ptr(defaultNodepoolName), Volume: &ske.Volume{ diff --git a/internal/pkg/services/ske/utils/utils_test.go b/internal/pkg/services/ske/utils/utils_test.go index 917d590ae..d36f2b481 100644 --- a/internal/pkg/services/ske/utils/utils_test.go +++ b/internal/pkg/services/ske/utils/utils_test.go @@ -146,6 +146,17 @@ func TestClusterExists(t *testing.T) { func fixtureProviderOptions(mods ...func(*ske.ProviderOptions)) *ske.ProviderOptions { providerOptions := &ske.ProviderOptions{ + AvailabilityZones: &[]ske.AvailabilityZone{ + {Name: utils.Ptr("eu01-m")}, + {Name: utils.Ptr("eu01-1")}, + {Name: utils.Ptr("eu01-2")}, + {Name: utils.Ptr("eu01-3")}, + }, + MachineTypes: &[]ske.MachineType{ + { + Name: utils.Ptr("b1.2"), + }, + }, KubernetesVersions: &[]ske.KubernetesVersion{ { State: utils.Ptr("supported"), @@ -263,6 +274,9 @@ func fixtureGetDefaultPayload(mods ...func(*ske.CreateOrUpdateClusterPayload)) * Nodepools: &[]ske.Nodepool{ { AvailabilityZones: &[]string{ + "eu01-m", + "eu01-1", + "eu01-2", "eu01-3", }, Cri: &ske.CRI{ @@ -275,9 +289,9 @@ func fixtureGetDefaultPayload(mods ...func(*ske.CreateOrUpdateClusterPayload)) * Name: utils.Ptr("flatcar"), }, }, - MaxSurge: utils.Ptr(int64(1)), + MaxSurge: utils.Ptr(int64(4)), MaxUnavailable: utils.Ptr(int64(0)), - Maximum: utils.Ptr(int64(2)), + Maximum: utils.Ptr(int64(4)), Minimum: utils.Ptr(int64(1)), Name: utils.Ptr("pool-default"), Volume: &ske.Volume{ @@ -312,6 +326,34 @@ func TestGetDefaultPayload(t *testing.T) { listProviderOptionsFails: true, isValid: false, }, + { + description: "availability zones nil", + listProviderOptionsResp: fixtureProviderOptions(func(po *ske.ProviderOptions) { + po.AvailabilityZones = nil + }), + isValid: false, + }, + { + description: "no availability zones", + listProviderOptionsResp: fixtureProviderOptions(func(po *ske.ProviderOptions) { + po.AvailabilityZones = &[]ske.AvailabilityZone{} + }), + isValid: false, + }, + { + description: "machine types nil", + listProviderOptionsResp: fixtureProviderOptions(func(po *ske.ProviderOptions) { + po.MachineTypes = nil + }), + isValid: false, + }, + { + description: "no machine types", + listProviderOptionsResp: fixtureProviderOptions(func(po *ske.ProviderOptions) { + po.MachineTypes = &[]ske.MachineType{} + }), + isValid: false, + }, { description: "no Kubernetes versions 1", listProviderOptionsResp: fixtureProviderOptions(func(po *ske.ProviderOptions) { From 8d31ff9f5b8ebc057cd7dcbbcdca5e30cf0c455d Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Thu, 9 Oct 2025 10:59:27 +0200 Subject: [PATCH 2/2] filter out AZs where not all flavors are available --- internal/pkg/services/ske/utils/utils.go | 9 +++++++-- internal/pkg/services/ske/utils/utils_test.go | 5 ++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/pkg/services/ske/utils/utils.go b/internal/pkg/services/ske/utils/utils.go index 12a18f08f..904ff97a1 100644 --- a/internal/pkg/services/ske/utils/utils.go +++ b/internal/pkg/services/ske/utils/utils.go @@ -6,6 +6,7 @@ import ( "maps" "os" "path/filepath" + "regexp" "strconv" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -109,9 +110,13 @@ func getDefaultPayloadNodepool(resp *ske.ProviderOptions) (*ske.Nodepool, error) if resp.AvailabilityZones == nil || len(*resp.AvailabilityZones) == 0 { return nil, fmt.Errorf("no availability zones found") } - availabilityZones := make([]string, len(*resp.AvailabilityZones)) + var availabilityZones []string for i := range *resp.AvailabilityZones { - availabilityZones[i] = (*resp.AvailabilityZones)[i].GetName() + azName := (*resp.AvailabilityZones)[i].GetName() + // don't include availability zones like eu01-m, eu02-m, not all flavors are available there + if !regexp.MustCompile(`\w{2}\d{2}-m`).MatchString(azName) { + availabilityZones = append(availabilityZones, azName) + } } if resp.MachineTypes == nil || len(*resp.MachineTypes) == 0 { diff --git a/internal/pkg/services/ske/utils/utils_test.go b/internal/pkg/services/ske/utils/utils_test.go index d36f2b481..b150509ec 100644 --- a/internal/pkg/services/ske/utils/utils_test.go +++ b/internal/pkg/services/ske/utils/utils_test.go @@ -274,7 +274,6 @@ func fixtureGetDefaultPayload(mods ...func(*ske.CreateOrUpdateClusterPayload)) * Nodepools: &[]ske.Nodepool{ { AvailabilityZones: &[]string{ - "eu01-m", "eu01-1", "eu01-2", "eu01-3", @@ -289,9 +288,9 @@ func fixtureGetDefaultPayload(mods ...func(*ske.CreateOrUpdateClusterPayload)) * Name: utils.Ptr("flatcar"), }, }, - MaxSurge: utils.Ptr(int64(4)), + MaxSurge: utils.Ptr(int64(3)), MaxUnavailable: utils.Ptr(int64(0)), - Maximum: utils.Ptr(int64(4)), + Maximum: utils.Ptr(int64(3)), Minimum: utils.Ptr(int64(1)), Name: utils.Ptr("pool-default"), Volume: &ske.Volume{