From 64cd4e52fb4de98279f1a3f1882a1f55be4ffd5b Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Fri, 7 Aug 2026 18:25:51 +0200 Subject: [PATCH 1/7] fix(ske): nil pointer when gateway api is not set relates to #1671 --- stackit/internal/services/ske/cluster/resource.go | 2 +- .../internal/services/ske/cluster/resource_test.go | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index 360d4ecdf..28a66fe1f 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -2235,7 +2235,7 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { dnsExtension := types.ObjectNull(dnsTypes) if cl.Extensions.Dns != nil { enabled := types.BoolValue(cl.Extensions.Dns.Enabled) - gatewayApi := types.BoolValue(*cl.Extensions.Dns.GatewayApi) + gatewayApi := types.BoolPointerValue(cl.Extensions.Dns.GatewayApi) zonesList, diags := types.ListValueFrom(ctx, types.StringType, cl.Extensions.Dns.Zones) if diags.HasError() { diff --git a/stackit/internal/services/ske/cluster/resource_test.go b/stackit/internal/services/ske/cluster/resource_test.go index 3bb452b19..8382fbec7 100644 --- a/stackit/internal/services/ske/cluster/resource_test.go +++ b/stackit/internal/services/ske/cluster/resource_test.go @@ -342,9 +342,8 @@ func TestMapFields(t *testing.T) { Enabled: true, }, Dns: &ske.DNS{ - Zones: nil, - Enabled: true, - GatewayApi: new(true), + Zones: nil, + Enabled: true, }, ApplicationLoadBalancer: &ske.ApplicationLoadBalancer{ Enabled: true, @@ -382,7 +381,7 @@ func TestMapFields(t *testing.T) { "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "enabled": types.BoolValue(true), "zones": types.ListNull(types.StringType), - "gateway_api": types.BoolValue(true), + "gateway_api": types.BoolNull(), }), "application_load_balancer": types.ObjectValueMust(applicationLoadBalancerTypes, map[string]attr.Value{ "enabled": types.BoolValue(true), @@ -409,7 +408,7 @@ func TestMapFields(t *testing.T) { "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "enabled": types.BoolValue(false), "zones": types.ListNull(types.StringType), - "gateway_api": types.BoolValue(false), + "gateway_api": types.BoolNull(), }), "application_load_balancer": types.ObjectValueMust(applicationLoadBalancerTypes, map[string]attr.Value{ "enabled": types.BoolValue(false), @@ -450,7 +449,7 @@ func TestMapFields(t *testing.T) { "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "enabled": types.BoolValue(false), "zones": types.ListNull(types.StringType), - "gateway_api": types.BoolValue(false), + "gateway_api": types.BoolNull(), }), "application_load_balancer": types.ObjectValueMust(applicationLoadBalancerTypes, map[string]attr.Value{ "enabled": types.BoolValue(false), From aa98f56fa3e91ab34d5cb73ef4d3c3816b4c7c3f Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 12 Aug 2026 14:18:58 +0200 Subject: [PATCH 2/7] fix ske --- stackit/internal/services/ske/cluster/resource.go | 14 ++++++++------ .../internal/services/ske/cluster/resource_test.go | 5 ++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index 28a66fe1f..18b9a3510 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -767,6 +767,7 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re "extensions": schema.SingleNestedAttribute{ Description: "A single extensions block as defined below.", Optional: true, + Computed: true, PlanModifiers: []planmodifier.Object{ objectplanmodifier.UseStateForUnknown(), }, @@ -843,13 +844,13 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re "gateway_api": schema.BoolAttribute{ Description: "Enables Gateway API support for ExternalDNS. The CRDs must be installed by the user. Once installed, ExternalDNS will be configured at the next cluster reconcile.", Optional: true, - Computed: true, }, }, }, "application_load_balancer": schema.SingleNestedAttribute{ Description: "Application Load Balancer extension.", Optional: true, + Computed: true, Attributes: map[string]schema.Attribute{ "enabled": schema.BoolAttribute{ Description: "Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected.", @@ -2103,7 +2104,7 @@ func checkDisabledExtensions(ctx context.Context, ex *extensions) (aclDisabled, } applicationLoadBalancer := applicationLoadBalancer{} - if ex.ApplicationLoadBalancer.IsNull() { + if utils.IsUndefined(ex.ApplicationLoadBalancer) { applicationLoadBalancer.Enabled = types.BoolValue(false) } else { diags = ex.ApplicationLoadBalancer.As(ctx, &applicationLoadBalancer, basetypes.ObjectAsOptions{}) @@ -2123,11 +2124,13 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { var diags diag.Diagnostics ex := extensions{} - if !m.Extensions.IsNull() { + if !utils.IsUndefined(m.Extensions) { diags := m.Extensions.As(ctx, &ex, basetypes.ObjectAsOptions{}) if diags.HasError() { return fmt.Errorf("converting extensions object: %v", diags.Errors()) } + } else { + m.Extensions = types.ObjectNull(extensionsTypes) } // If the user provides the extensions block with the enabled flags as false @@ -2143,9 +2146,8 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { if err != nil { return fmt.Errorf("checking if extensions are disabled: %w", err) } - disabledExtensions := aclDisabled && observabilityDisabled && dnsDisabled && applicationLoadBalancerDisabled - if skeUtils.IsEmptyExtension(cl.Extensions) && (disabledExtensions || m.Extensions.IsNull()) { + if skeUtils.IsEmptyExtension(cl.Extensions) && utils.IsUndefined(m.Extensions) { if m.Extensions.Attributes() == nil { m.Extensions = types.ObjectNull(extensionsTypes) } @@ -2228,7 +2230,7 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { if diags.HasError() { return fmt.Errorf("creating applicationLoadBalancer: %w", core.DiagsToError(diags)) } - } else if applicationLoadBalancerDisabled && !ex.ApplicationLoadBalancer.IsNull() { + } else if applicationLoadBalancerDisabled && !utils.IsUndefined(ex.ApplicationLoadBalancer) { applicationLoadBalancerExtension = ex.ApplicationLoadBalancer } diff --git a/stackit/internal/services/ske/cluster/resource_test.go b/stackit/internal/services/ske/cluster/resource_test.go index 8382fbec7..d2ce1bf13 100644 --- a/stackit/internal/services/ske/cluster/resource_test.go +++ b/stackit/internal/services/ske/cluster/resource_test.go @@ -496,6 +496,9 @@ func TestMapFields(t *testing.T) { Enabled: true, GatewayApi: new(true), }, + ApplicationLoadBalancer: &ske.ApplicationLoadBalancer{ + Enabled: true, + }, }, Name: new("name"), Access: &ske.Access{ @@ -534,7 +537,7 @@ func TestMapFields(t *testing.T) { "gateway_api": types.BoolValue(true), }), "application_load_balancer": types.ObjectValueMust(applicationLoadBalancerTypes, map[string]attr.Value{ - "enabled": types.BoolValue(false), + "enabled": types.BoolValue(true), }), }), KubernetesVersionUsed: types.StringValue(""), From c000f19d06e9b1eef02d5f14a234725df094b50c Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Thu, 13 Aug 2026 09:54:54 +0200 Subject: [PATCH 3/7] fix toPayload function --- stackit/internal/services/ske/cluster/resource.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index 18b9a3510..207369c4d 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -1550,7 +1550,7 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error) return nil, fmt.Errorf("converting extensions.dns object: %v", diags.Errors()) } dnsEnabled := dns.Enabled.ValueBool() - gatewayApi := dns.GatewayApi.ValueBool() + gatewayApi := conversion.BoolValueToPointer(dns.GatewayApi) zones := []string{} diags = dns.Zones.ElementsAs(ctx, &zones, true) @@ -1560,7 +1560,7 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error) skeDNS = &ske.DNS{ Enabled: dnsEnabled, Zones: zones, - GatewayApi: &gatewayApi, + GatewayApi: gatewayApi, } } From 75b9714935cf4c00bda1274406dc9fdbb517db12 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Thu, 13 Aug 2026 17:19:31 +0200 Subject: [PATCH 4/7] remove plan modifier --- stackit/internal/services/ske/cluster/resource.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index 207369c4d..751ae9795 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -768,9 +768,6 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re Description: "A single extensions block as defined below.", Optional: true, Computed: true, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.UseStateForUnknown(), - }, Attributes: map[string]schema.Attribute{ "argus": schema.SingleNestedAttribute{ Description: "A single argus block as defined below. This field is deprecated and will be removed 06 January 2026.", @@ -2129,8 +2126,6 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { if diags.HasError() { return fmt.Errorf("converting extensions object: %v", diags.Errors()) } - } else { - m.Extensions = types.ObjectNull(extensionsTypes) } // If the user provides the extensions block with the enabled flags as false @@ -2147,13 +2142,6 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { return fmt.Errorf("checking if extensions are disabled: %w", err) } - if skeUtils.IsEmptyExtension(cl.Extensions) && utils.IsUndefined(m.Extensions) { - if m.Extensions.Attributes() == nil { - m.Extensions = types.ObjectNull(extensionsTypes) - } - return nil - } - aclExtension := types.ObjectNull(aclTypes) if cl.Extensions.Acl != nil { enabled := types.BoolValue(cl.Extensions.Acl.Enabled) From e7447a6e34e13ac13de0e03827cda13ea2e3d035 Mon Sep 17 00:00:00 2001 From: Marcel Jacek Date: Fri, 14 Aug 2026 12:45:20 +0200 Subject: [PATCH 5/7] fix: add default values for extensions --- .../internal/services/ske/cluster/resource.go | 17 +++++++++++++++- .../services/ske/cluster/resource_test.go | 20 ++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index 751ae9795..d8ea6f781 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" @@ -443,6 +444,7 @@ var descriptions = map[string]string{ // Schema defines the schema for the resource. func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + extensionApplicationLoadbalancerDefault := types.ObjectValueMust(applicationLoadBalancerTypes, map[string]attr.Value{"enabled": types.BoolValue(false)}) resp.Schema = schema.Schema{ Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["node_pools_plan_note"]), // Callout block: https://developer.hashicorp.com/terraform/registry/providers/docs#callouts @@ -768,6 +770,13 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re Description: "A single extensions block as defined below.", Optional: true, Computed: true, + Default: objectdefault.StaticValue(types.ObjectValueMust(extensionsTypes, map[string]attr.Value{ + "argus": types.ObjectNull(argusTypes), + "observability": types.ObjectNull(observabilityTypes), + "application_load_balancer": extensionApplicationLoadbalancerDefault, + "acl": types.ObjectNull(aclTypes), + "dns": types.ObjectNull(dnsTypes), + })), Attributes: map[string]schema.Attribute{ "argus": schema.SingleNestedAttribute{ Description: "A single argus block as defined below. This field is deprecated and will be removed 06 January 2026.", @@ -848,10 +857,16 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re Description: "Application Load Balancer extension.", Optional: true, Computed: true, + Validators: []validator.Object{ + objectvalidator.AlsoRequires(path.MatchRelative().AtName("enabled")), + }, + Default: objectdefault.StaticValue(extensionApplicationLoadbalancerDefault), Attributes: map[string]schema.Attribute{ "enabled": schema.BoolAttribute{ Description: "Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected.", - Required: true, + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), }, }, }, diff --git a/stackit/internal/services/ske/cluster/resource_test.go b/stackit/internal/services/ske/cluster/resource_test.go index d2ce1bf13..d344ca3e8 100644 --- a/stackit/internal/services/ske/cluster/resource_test.go +++ b/stackit/internal/services/ske/cluster/resource_test.go @@ -562,13 +562,19 @@ func TestMapFields(t *testing.T) { }, testRegion, Model{ - Id: types.StringValue("pid,region,name"), - ProjectId: types.StringValue("pid"), - Name: types.StringValue("name"), - NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), - Maintenance: types.ObjectNull(maintenanceTypes), - Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), - Extensions: types.ObjectNull(extensionsTypes), + Id: types.StringValue("pid,region,name"), + ProjectId: types.StringValue("pid"), + Name: types.StringValue("name"), + NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), + Maintenance: types.ObjectNull(maintenanceTypes), + Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), + Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{ + "argus": types.ObjectNull(argusTypes), + "observability": types.ObjectNull(observabilityTypes), + "application_load_balancer": types.ObjectNull(applicationLoadBalancerTypes), + "acl": types.ObjectNull(aclTypes), + "dns": types.ObjectNull(dnsTypes), + }), EgressAddressRanges: types.ListNull(types.StringType), PodAddressRanges: types.ListNull(types.StringType), ServiceAccountIssuer: types.StringNull(), From 6021be0caf4a5372d7145129d7a2129b45cf4b71 Mon Sep 17 00:00:00 2001 From: Marcel Jacek Date: Fri, 14 Aug 2026 13:40:29 +0200 Subject: [PATCH 6/7] update docs --- docs/resources/ske_cluster.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/resources/ske_cluster.md b/docs/resources/ske_cluster.md index e77aa329c..dd6c5b9f4 100644 --- a/docs/resources/ske_cluster.md +++ b/docs/resources/ske_cluster.md @@ -166,7 +166,7 @@ Required: ### Nested Schema for `extensions.application_load_balancer` -Required: +Optional: - `enabled` (Boolean) Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected. From c5903c6186fafa3478dda4293e2e70c9006576a4 Mon Sep 17 00:00:00 2001 From: Marcel Jacek Date: Fri, 14 Aug 2026 16:23:49 +0200 Subject: [PATCH 7/7] docs: add hint that after private preview alb will be enabled by default --- docs/resources/ske_cluster.md | 2 +- stackit/internal/services/ske/cluster/resource.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/resources/ske_cluster.md b/docs/resources/ske_cluster.md index dd6c5b9f4..90e675000 100644 --- a/docs/resources/ske_cluster.md +++ b/docs/resources/ske_cluster.md @@ -168,7 +168,7 @@ Required: Optional: -- `enabled` (Boolean) Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected. +- `enabled` (Boolean) Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected. Default value will change to true once the private preview phase is over. diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index d8ea6f781..2fd10bee1 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -863,7 +863,7 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re Default: objectdefault.StaticValue(extensionApplicationLoadbalancerDefault), Attributes: map[string]schema.Attribute{ "enabled": schema.BoolAttribute{ - Description: "Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected.", + Description: "Enables the application load balancer extension. Note: This feature is in private preview. Enabling application load balancer extension is only possible for enabled accounts. Otherwise the request will be rejected. Default value will change to true once the private preview phase is over.", Optional: true, Computed: true, Default: booldefault.StaticBool(false),