Skip to content

Commit 3ac1d50

Browse files
authored
Feature: allow system components on nodepools (#591)
* feat: allow system components on nodepools * docs: generated docs for ske * lint: sort imports * revert changes
1 parent 2bf6a8d commit 3ac1d50

6 files changed

Lines changed: 155 additions & 59 deletions

File tree

docs/data-sources/ske_cluster.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ Read-Only:
114114

115115
Read-Only:
116116

117+
- `allow_system_components` (Boolean) Allow system components to run on this node pool.
117118
- `availability_zones` (List of String) Specify a list of availability zones.
118119
- `cri` (String) Specifies the container runtime.
119120
- `labels` (Map of String) Labels to add to each node.

docs/resources/ske_cluster.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Required:
7979

8080
Optional:
8181

82+
- `allow_system_components` (Boolean) Allow system components to run on this node pool.
8283
- `cri` (String) Specifies the container runtime. Defaults to `containerd`
8384
- `labels` (Map of String) Labels to add to each node.
8485
- `max_surge` (Number) Maximum number of additional VMs that are created during an update. If set (larger than 0), then it must be at least the amount of zones configured for the nodepool. The `max_surge` and `max_unavailable` fields cannot both be unset at the same time.

stackit/internal/services/ske/cluster/datasource.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
202202
ElementType: types.StringType,
203203
Computed: true,
204204
},
205+
"allow_system_components": schema.BoolAttribute{
206+
Description: "Allow system components to run on this node pool.",
207+
Computed: true,
208+
},
205209
},
206210
},
207211
},

stackit/internal/services/ske/cluster/resource.go

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -82,42 +82,44 @@ type Model struct {
8282

8383
// Struct corresponding to Model.NodePools[i]
8484
type nodePool struct {
85-
Name types.String `tfsdk:"name"`
86-
MachineType types.String `tfsdk:"machine_type"`
87-
OSName types.String `tfsdk:"os_name"`
88-
OSVersionMin types.String `tfsdk:"os_version_min"`
89-
OSVersion types.String `tfsdk:"os_version"`
90-
OSVersionUsed types.String `tfsdk:"os_version_used"`
91-
Minimum types.Int64 `tfsdk:"minimum"`
92-
Maximum types.Int64 `tfsdk:"maximum"`
93-
MaxSurge types.Int64 `tfsdk:"max_surge"`
94-
MaxUnavailable types.Int64 `tfsdk:"max_unavailable"`
95-
VolumeType types.String `tfsdk:"volume_type"`
96-
VolumeSize types.Int64 `tfsdk:"volume_size"`
97-
Labels types.Map `tfsdk:"labels"`
98-
Taints types.List `tfsdk:"taints"`
99-
CRI types.String `tfsdk:"cri"`
100-
AvailabilityZones types.List `tfsdk:"availability_zones"`
85+
Name types.String `tfsdk:"name"`
86+
MachineType types.String `tfsdk:"machine_type"`
87+
OSName types.String `tfsdk:"os_name"`
88+
OSVersionMin types.String `tfsdk:"os_version_min"`
89+
OSVersion types.String `tfsdk:"os_version"`
90+
OSVersionUsed types.String `tfsdk:"os_version_used"`
91+
Minimum types.Int64 `tfsdk:"minimum"`
92+
Maximum types.Int64 `tfsdk:"maximum"`
93+
MaxSurge types.Int64 `tfsdk:"max_surge"`
94+
MaxUnavailable types.Int64 `tfsdk:"max_unavailable"`
95+
VolumeType types.String `tfsdk:"volume_type"`
96+
VolumeSize types.Int64 `tfsdk:"volume_size"`
97+
Labels types.Map `tfsdk:"labels"`
98+
Taints types.List `tfsdk:"taints"`
99+
CRI types.String `tfsdk:"cri"`
100+
AvailabilityZones types.List `tfsdk:"availability_zones"`
101+
AllowSystemComponents types.Bool `tfsdk:"allow_system_components"`
101102
}
102103

103104
// Types corresponding to nodePool
104105
var nodePoolTypes = map[string]attr.Type{
105-
"name": basetypes.StringType{},
106-
"machine_type": basetypes.StringType{},
107-
"os_name": basetypes.StringType{},
108-
"os_version_min": basetypes.StringType{},
109-
"os_version": basetypes.StringType{},
110-
"os_version_used": basetypes.StringType{},
111-
"minimum": basetypes.Int64Type{},
112-
"maximum": basetypes.Int64Type{},
113-
"max_surge": basetypes.Int64Type{},
114-
"max_unavailable": basetypes.Int64Type{},
115-
"volume_type": basetypes.StringType{},
116-
"volume_size": basetypes.Int64Type{},
117-
"labels": basetypes.MapType{ElemType: types.StringType},
118-
"taints": basetypes.ListType{ElemType: types.ObjectType{AttrTypes: taintTypes}},
119-
"cri": basetypes.StringType{},
120-
"availability_zones": basetypes.ListType{ElemType: types.StringType},
106+
"name": basetypes.StringType{},
107+
"machine_type": basetypes.StringType{},
108+
"os_name": basetypes.StringType{},
109+
"os_version_min": basetypes.StringType{},
110+
"os_version": basetypes.StringType{},
111+
"os_version_used": basetypes.StringType{},
112+
"minimum": basetypes.Int64Type{},
113+
"maximum": basetypes.Int64Type{},
114+
"max_surge": basetypes.Int64Type{},
115+
"max_unavailable": basetypes.Int64Type{},
116+
"volume_type": basetypes.StringType{},
117+
"volume_size": basetypes.Int64Type{},
118+
"labels": basetypes.MapType{ElemType: types.StringType},
119+
"taints": basetypes.ListType{ElemType: types.ObjectType{AttrTypes: taintTypes}},
120+
"cri": basetypes.StringType{},
121+
"availability_zones": basetypes.ListType{ElemType: types.StringType},
122+
"allow_system_components": basetypes.BoolType{},
121123
}
122124

123125
// Struct corresponding to nodePool.Taints[i]
@@ -391,6 +393,12 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
391393
Required: true,
392394
ElementType: types.StringType,
393395
},
396+
"allow_system_components": schema.BoolAttribute{
397+
Description: "Allow system components to run on this node pool.",
398+
Optional: true,
399+
Computed: true,
400+
Default: booldefault.StaticBool(true),
401+
},
394402
"minimum": schema.Int64Attribute{
395403
Description: "Minimum number of nodes in the pool.",
396404
Required: true,
@@ -994,16 +1002,32 @@ func toNodepoolsPayload(ctx context.Context, m *Model, availableMachineVersions
9941002
Type: conversion.StringValueToPointer(nodePool.VolumeType),
9951003
Size: conversion.Int64ValueToPointer(nodePool.VolumeSize),
9961004
},
997-
Taints: &ts,
998-
Cri: &cn,
999-
Labels: ls,
1000-
AvailabilityZones: &zs,
1005+
Taints: &ts,
1006+
Cri: &cn,
1007+
Labels: ls,
1008+
AvailabilityZones: &zs,
1009+
AllowSystemComponents: conversion.BoolValueToPointer(nodePool.AllowSystemComponents),
10011010
}
10021011
cnps = append(cnps, cnp)
10031012
}
1013+
1014+
if err := verifySystemComponentsInNodePools(cnps); err != nil {
1015+
return nil, nil, err
1016+
}
1017+
10041018
return cnps, deprecatedVersionsUsed, nil
10051019
}
10061020

1021+
// verifySystemComponentsInNodePools checks if at least one node pool has the allow_system_components attribute set to true.
1022+
func verifySystemComponentsInNodePools(nodePools []ske.Nodepool) error {
1023+
for _, nodePool := range nodePools {
1024+
if nodePool.AllowSystemComponents != nil && *nodePool.AllowSystemComponents {
1025+
return nil // A node pool allowing system components was found
1026+
}
1027+
}
1028+
return fmt.Errorf("at least one node_pool must allow system components")
1029+
}
1030+
10071031
// latestMatchingMachineVersion determines the latest machine image version for the create/update payload.
10081032
// It considers the available versions for the specified OS (OSName), the minimum version configured by the user,
10091033
// and the current version in the cluster. The function's behavior is as follows:
@@ -1374,20 +1398,21 @@ func mapNodePools(ctx context.Context, cl *ske.Cluster, m *Model) error {
13741398
nodePools := []attr.Value{}
13751399
for i, nodePoolResp := range *cl.Nodepools {
13761400
nodePool := map[string]attr.Value{
1377-
"name": types.StringPointerValue(nodePoolResp.Name),
1378-
"machine_type": types.StringPointerValue(nodePoolResp.Machine.Type),
1379-
"os_name": types.StringNull(),
1380-
"os_version_min": modelNodePoolOSVersionMin[*nodePoolResp.Name],
1381-
"os_version": modelNodePoolOSVersion[*nodePoolResp.Name],
1382-
"minimum": types.Int64PointerValue(nodePoolResp.Minimum),
1383-
"maximum": types.Int64PointerValue(nodePoolResp.Maximum),
1384-
"max_surge": types.Int64PointerValue(nodePoolResp.MaxSurge),
1385-
"max_unavailable": types.Int64PointerValue(nodePoolResp.MaxUnavailable),
1386-
"volume_type": types.StringNull(),
1387-
"volume_size": types.Int64PointerValue(nodePoolResp.Volume.Size),
1388-
"labels": types.MapNull(types.StringType),
1389-
"cri": types.StringNull(),
1390-
"availability_zones": types.ListNull(types.StringType),
1401+
"name": types.StringPointerValue(nodePoolResp.Name),
1402+
"machine_type": types.StringPointerValue(nodePoolResp.Machine.Type),
1403+
"os_name": types.StringNull(),
1404+
"os_version_min": modelNodePoolOSVersionMin[*nodePoolResp.Name],
1405+
"os_version": modelNodePoolOSVersion[*nodePoolResp.Name],
1406+
"minimum": types.Int64PointerValue(nodePoolResp.Minimum),
1407+
"maximum": types.Int64PointerValue(nodePoolResp.Maximum),
1408+
"max_surge": types.Int64PointerValue(nodePoolResp.MaxSurge),
1409+
"max_unavailable": types.Int64PointerValue(nodePoolResp.MaxUnavailable),
1410+
"volume_type": types.StringNull(),
1411+
"volume_size": types.Int64PointerValue(nodePoolResp.Volume.Size),
1412+
"labels": types.MapNull(types.StringType),
1413+
"cri": types.StringNull(),
1414+
"availability_zones": types.ListNull(types.StringType),
1415+
"allow_system_components": types.BoolPointerValue(nodePoolResp.AllowSystemComponents),
13911416
}
13921417

13931418
if nodePoolResp.Machine != nil && nodePoolResp.Machine.Image != nil {

stackit/internal/services/ske/cluster/resource_test.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"github.com/google/go-cmp/cmp"
99
"github.com/hashicorp/terraform-plugin-framework/attr"
1010
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1112
"github.com/stackitcloud/stackit-sdk-go/core/utils"
1213
"github.com/stackitcloud/stackit-sdk-go/services/ske"
14+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
1315
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
1416
)
1517

@@ -106,7 +108,8 @@ func TestMapFields(t *testing.T) {
106108
Name: utils.Ptr("name"),
107109
Nodepools: &[]ske.Nodepool{
108110
{
109-
AvailabilityZones: &[]string{"z1", "z2"},
111+
AllowSystemComponents: utils.Ptr(true),
112+
AvailabilityZones: &[]string{"z1", "z2"},
110113
Cri: &ske.CRI{
111114
Name: utils.Ptr("cri"),
112115
},
@@ -195,6 +198,7 @@ func TestMapFields(t *testing.T) {
195198
types.StringValue("z2"),
196199
},
197200
),
201+
"allow_system_components": types.BoolValue(true),
198202
},
199203
),
200204
},
@@ -481,6 +485,7 @@ func TestMapFields(t *testing.T) {
481485
types.StringValue("z2"),
482486
},
483487
),
488+
"allow_system_components": types.BoolValue(true),
484489
},
485490
),
486491
},
@@ -599,6 +604,7 @@ func TestMapFields(t *testing.T) {
599604
types.StringValue("z2"),
600605
},
601606
),
607+
"allow_system_components": types.BoolNull(),
602608
},
603609
),
604610
},
@@ -2284,3 +2290,57 @@ func TestToNetworkPayload(t *testing.T) {
22842290
})
22852291
}
22862292
}
2293+
2294+
func TestVerifySystemComponentNodepools(t *testing.T) {
2295+
tests := []struct {
2296+
description string
2297+
nodePools []ske.Nodepool
2298+
isValid bool
2299+
}{
2300+
{
2301+
description: "all pools allow system components",
2302+
nodePools: []ske.Nodepool{
2303+
{
2304+
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(true)),
2305+
},
2306+
{
2307+
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(true)),
2308+
},
2309+
},
2310+
isValid: true,
2311+
},
2312+
{
2313+
description: "one pool allows system components",
2314+
nodePools: []ske.Nodepool{
2315+
{
2316+
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(true)),
2317+
},
2318+
{
2319+
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(false)),
2320+
},
2321+
},
2322+
isValid: true,
2323+
},
2324+
{
2325+
description: "no pool allows system components",
2326+
nodePools: []ske.Nodepool{
2327+
{
2328+
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(false)),
2329+
},
2330+
{
2331+
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(false)),
2332+
},
2333+
},
2334+
isValid: false,
2335+
},
2336+
}
2337+
2338+
for _, tt := range tests {
2339+
t.Run(tt.description, func(t *testing.T) {
2340+
err := verifySystemComponentsInNodePools(tt.nodePools)
2341+
if (err == nil) != tt.isValid {
2342+
t.Errorf("expected validity to be %v, but got error: %v", tt.isValid, err)
2343+
}
2344+
})
2345+
}
2346+
}

stackit/internal/services/ske/ske_acc_test.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ var clusterResource = map[string]string{
2121
"project_id": testutil.ProjectId,
2222
"name": fmt.Sprintf("cl-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
2323
"name_min": fmt.Sprintf("cl-min-%s", acctest.RandStringFromCharSet(3, acctest.CharSetAlphaNum)),
24-
"kubernetes_version_min": "1.28",
25-
"kubernetes_version_used": "1.28.11",
26-
"kubernetes_version_min_new": "1.29",
27-
"kubernetes_version_used_new": "1.29.6",
24+
"kubernetes_version_min": "1.29",
25+
"kubernetes_version_used": "1.29.10",
26+
"kubernetes_version_min_new": "1.30",
27+
"kubernetes_version_used_new": "1.30.6",
2828
"nodepool_name": "np-acc-test",
2929
"nodepool_name_min": "np-acc-min-test",
3030
"nodepool_machine_type": "b1.2",
31-
"nodepool_os_version_min": "3815.2",
32-
"nodepool_os_version_used": "3815.2.3",
33-
"nodepool_os_version_min_new": "3815.2.3",
34-
"nodepool_os_version_used_new": "3815.2.3",
31+
"nodepool_os_version_min": "3975.2",
32+
"nodepool_os_version_used": "3975.2.0",
33+
"nodepool_os_version_min_new": "3975.2.1",
34+
"nodepool_os_version_used_new": "3975.2.1",
3535
"nodepool_os_name": "flatcar",
3636
"nodepool_minimum": "2",
3737
"nodepool_maximum": "3",
@@ -46,6 +46,7 @@ var clusterResource = map[string]string{
4646
"nodepool_taints_effect": "PreferNoSchedule",
4747
"nodepool_taints_key": "tkey",
4848
"nodepool_taints_value": "tvalue",
49+
"nodepool_allow_system_components": "true",
4950
"extensions_acl_enabled": "true",
5051
"extensions_acl_cidrs": "192.168.0.0/24",
5152
"extensions_argus_enabled": "false",
@@ -96,6 +97,7 @@ func getConfig(kubernetesVersion, nodePoolMachineOSVersion string, maintenanceEn
9697
key = "%s"
9798
value = "%s"
9899
}]
100+
allow_system_components = %s
99101
}]
100102
extensions = {
101103
acl = {
@@ -169,6 +171,7 @@ func getConfig(kubernetesVersion, nodePoolMachineOSVersion string, maintenanceEn
169171
clusterResource["nodepool_taints_effect"],
170172
clusterResource["nodepool_taints_key"],
171173
clusterResource["nodepool_taints_value"],
174+
clusterResource["nodepool_allow_system_components"],
172175
clusterResource["extensions_acl_enabled"],
173176
clusterResource["extensions_acl_cidrs"],
174177
clusterResource["extensions_argus_enabled"],
@@ -233,6 +236,7 @@ func TestAccSKE(t *testing.T) {
233236
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.taints.0.effect", clusterResource["nodepool_taints_effect"]),
234237
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.taints.0.key", clusterResource["nodepool_taints_key"]),
235238
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.taints.0.value", clusterResource["nodepool_taints_value"]),
239+
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.allow_system_components", clusterResource["nodepool_allow_system_components"]),
236240
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.cri", clusterResource["nodepool_cri"]),
237241
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.enabled", clusterResource["extensions_acl_enabled"]),
238242
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),
@@ -343,6 +347,7 @@ func TestAccSKE(t *testing.T) {
343347
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.taints.0.effect", clusterResource["nodepool_taints_effect"]),
344348
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.taints.0.key", clusterResource["nodepool_taints_key"]),
345349
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.taints.0.value", clusterResource["nodepool_taints_value"]),
350+
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.allow_system_components", clusterResource["nodepool_allow_system_components"]),
346351
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.cri", clusterResource["nodepool_cri"]),
347352
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.enabled", clusterResource["extensions_acl_enabled"]),
348353
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),

0 commit comments

Comments
 (0)