Feature: allow system components on nodepools (#591)

* feat: allow system components on nodepools

* docs: generated docs for ske

* lint: sort imports

* revert changes
This commit is contained in:
Mauritz Uphoff 2024-11-13 11:44:55 +01:00 committed by GitHub
parent 2bf6a8dce7
commit 3ac1d50253
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 155 additions and 59 deletions

View file

@ -202,6 +202,10 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
ElementType: types.StringType,
Computed: true,
},
"allow_system_components": schema.BoolAttribute{
Description: "Allow system components to run on this node pool.",
Computed: true,
},
},
},
},

View file

@ -82,42 +82,44 @@ type Model struct {
// Struct corresponding to Model.NodePools[i]
type nodePool struct {
Name types.String `tfsdk:"name"`
MachineType types.String `tfsdk:"machine_type"`
OSName types.String `tfsdk:"os_name"`
OSVersionMin types.String `tfsdk:"os_version_min"`
OSVersion types.String `tfsdk:"os_version"`
OSVersionUsed types.String `tfsdk:"os_version_used"`
Minimum types.Int64 `tfsdk:"minimum"`
Maximum types.Int64 `tfsdk:"maximum"`
MaxSurge types.Int64 `tfsdk:"max_surge"`
MaxUnavailable types.Int64 `tfsdk:"max_unavailable"`
VolumeType types.String `tfsdk:"volume_type"`
VolumeSize types.Int64 `tfsdk:"volume_size"`
Labels types.Map `tfsdk:"labels"`
Taints types.List `tfsdk:"taints"`
CRI types.String `tfsdk:"cri"`
AvailabilityZones types.List `tfsdk:"availability_zones"`
Name types.String `tfsdk:"name"`
MachineType types.String `tfsdk:"machine_type"`
OSName types.String `tfsdk:"os_name"`
OSVersionMin types.String `tfsdk:"os_version_min"`
OSVersion types.String `tfsdk:"os_version"`
OSVersionUsed types.String `tfsdk:"os_version_used"`
Minimum types.Int64 `tfsdk:"minimum"`
Maximum types.Int64 `tfsdk:"maximum"`
MaxSurge types.Int64 `tfsdk:"max_surge"`
MaxUnavailable types.Int64 `tfsdk:"max_unavailable"`
VolumeType types.String `tfsdk:"volume_type"`
VolumeSize types.Int64 `tfsdk:"volume_size"`
Labels types.Map `tfsdk:"labels"`
Taints types.List `tfsdk:"taints"`
CRI types.String `tfsdk:"cri"`
AvailabilityZones types.List `tfsdk:"availability_zones"`
AllowSystemComponents types.Bool `tfsdk:"allow_system_components"`
}
// Types corresponding to nodePool
var nodePoolTypes = map[string]attr.Type{
"name": basetypes.StringType{},
"machine_type": basetypes.StringType{},
"os_name": basetypes.StringType{},
"os_version_min": basetypes.StringType{},
"os_version": basetypes.StringType{},
"os_version_used": basetypes.StringType{},
"minimum": basetypes.Int64Type{},
"maximum": basetypes.Int64Type{},
"max_surge": basetypes.Int64Type{},
"max_unavailable": basetypes.Int64Type{},
"volume_type": basetypes.StringType{},
"volume_size": basetypes.Int64Type{},
"labels": basetypes.MapType{ElemType: types.StringType},
"taints": basetypes.ListType{ElemType: types.ObjectType{AttrTypes: taintTypes}},
"cri": basetypes.StringType{},
"availability_zones": basetypes.ListType{ElemType: types.StringType},
"name": basetypes.StringType{},
"machine_type": basetypes.StringType{},
"os_name": basetypes.StringType{},
"os_version_min": basetypes.StringType{},
"os_version": basetypes.StringType{},
"os_version_used": basetypes.StringType{},
"minimum": basetypes.Int64Type{},
"maximum": basetypes.Int64Type{},
"max_surge": basetypes.Int64Type{},
"max_unavailable": basetypes.Int64Type{},
"volume_type": basetypes.StringType{},
"volume_size": basetypes.Int64Type{},
"labels": basetypes.MapType{ElemType: types.StringType},
"taints": basetypes.ListType{ElemType: types.ObjectType{AttrTypes: taintTypes}},
"cri": basetypes.StringType{},
"availability_zones": basetypes.ListType{ElemType: types.StringType},
"allow_system_components": basetypes.BoolType{},
}
// Struct corresponding to nodePool.Taints[i]
@ -391,6 +393,12 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
Required: true,
ElementType: types.StringType,
},
"allow_system_components": schema.BoolAttribute{
Description: "Allow system components to run on this node pool.",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
},
"minimum": schema.Int64Attribute{
Description: "Minimum number of nodes in the pool.",
Required: true,
@ -994,16 +1002,32 @@ func toNodepoolsPayload(ctx context.Context, m *Model, availableMachineVersions
Type: conversion.StringValueToPointer(nodePool.VolumeType),
Size: conversion.Int64ValueToPointer(nodePool.VolumeSize),
},
Taints: &ts,
Cri: &cn,
Labels: ls,
AvailabilityZones: &zs,
Taints: &ts,
Cri: &cn,
Labels: ls,
AvailabilityZones: &zs,
AllowSystemComponents: conversion.BoolValueToPointer(nodePool.AllowSystemComponents),
}
cnps = append(cnps, cnp)
}
if err := verifySystemComponentsInNodePools(cnps); err != nil {
return nil, nil, err
}
return cnps, deprecatedVersionsUsed, nil
}
// verifySystemComponentsInNodePools checks if at least one node pool has the allow_system_components attribute set to true.
func verifySystemComponentsInNodePools(nodePools []ske.Nodepool) error {
for _, nodePool := range nodePools {
if nodePool.AllowSystemComponents != nil && *nodePool.AllowSystemComponents {
return nil // A node pool allowing system components was found
}
}
return fmt.Errorf("at least one node_pool must allow system components")
}
// latestMatchingMachineVersion determines the latest machine image version for the create/update payload.
// It considers the available versions for the specified OS (OSName), the minimum version configured by the user,
// 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 {
nodePools := []attr.Value{}
for i, nodePoolResp := range *cl.Nodepools {
nodePool := map[string]attr.Value{
"name": types.StringPointerValue(nodePoolResp.Name),
"machine_type": types.StringPointerValue(nodePoolResp.Machine.Type),
"os_name": types.StringNull(),
"os_version_min": modelNodePoolOSVersionMin[*nodePoolResp.Name],
"os_version": modelNodePoolOSVersion[*nodePoolResp.Name],
"minimum": types.Int64PointerValue(nodePoolResp.Minimum),
"maximum": types.Int64PointerValue(nodePoolResp.Maximum),
"max_surge": types.Int64PointerValue(nodePoolResp.MaxSurge),
"max_unavailable": types.Int64PointerValue(nodePoolResp.MaxUnavailable),
"volume_type": types.StringNull(),
"volume_size": types.Int64PointerValue(nodePoolResp.Volume.Size),
"labels": types.MapNull(types.StringType),
"cri": types.StringNull(),
"availability_zones": types.ListNull(types.StringType),
"name": types.StringPointerValue(nodePoolResp.Name),
"machine_type": types.StringPointerValue(nodePoolResp.Machine.Type),
"os_name": types.StringNull(),
"os_version_min": modelNodePoolOSVersionMin[*nodePoolResp.Name],
"os_version": modelNodePoolOSVersion[*nodePoolResp.Name],
"minimum": types.Int64PointerValue(nodePoolResp.Minimum),
"maximum": types.Int64PointerValue(nodePoolResp.Maximum),
"max_surge": types.Int64PointerValue(nodePoolResp.MaxSurge),
"max_unavailable": types.Int64PointerValue(nodePoolResp.MaxUnavailable),
"volume_type": types.StringNull(),
"volume_size": types.Int64PointerValue(nodePoolResp.Volume.Size),
"labels": types.MapNull(types.StringType),
"cri": types.StringNull(),
"availability_zones": types.ListNull(types.StringType),
"allow_system_components": types.BoolPointerValue(nodePoolResp.AllowSystemComponents),
}
if nodePoolResp.Machine != nil && nodePoolResp.Machine.Image != nil {

View file

@ -8,8 +8,10 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
)
@ -106,7 +108,8 @@ func TestMapFields(t *testing.T) {
Name: utils.Ptr("name"),
Nodepools: &[]ske.Nodepool{
{
AvailabilityZones: &[]string{"z1", "z2"},
AllowSystemComponents: utils.Ptr(true),
AvailabilityZones: &[]string{"z1", "z2"},
Cri: &ske.CRI{
Name: utils.Ptr("cri"),
},
@ -195,6 +198,7 @@ func TestMapFields(t *testing.T) {
types.StringValue("z2"),
},
),
"allow_system_components": types.BoolValue(true),
},
),
},
@ -481,6 +485,7 @@ func TestMapFields(t *testing.T) {
types.StringValue("z2"),
},
),
"allow_system_components": types.BoolValue(true),
},
),
},
@ -599,6 +604,7 @@ func TestMapFields(t *testing.T) {
types.StringValue("z2"),
},
),
"allow_system_components": types.BoolNull(),
},
),
},
@ -2284,3 +2290,57 @@ func TestToNetworkPayload(t *testing.T) {
})
}
}
func TestVerifySystemComponentNodepools(t *testing.T) {
tests := []struct {
description string
nodePools []ske.Nodepool
isValid bool
}{
{
description: "all pools allow system components",
nodePools: []ske.Nodepool{
{
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(true)),
},
{
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(true)),
},
},
isValid: true,
},
{
description: "one pool allows system components",
nodePools: []ske.Nodepool{
{
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(true)),
},
{
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(false)),
},
},
isValid: true,
},
{
description: "no pool allows system components",
nodePools: []ske.Nodepool{
{
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(false)),
},
{
AllowSystemComponents: conversion.BoolValueToPointer(basetypes.NewBoolValue(false)),
},
},
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
err := verifySystemComponentsInNodePools(tt.nodePools)
if (err == nil) != tt.isValid {
t.Errorf("expected validity to be %v, but got error: %v", tt.isValid, err)
}
})
}
}

View file

@ -21,17 +21,17 @@ var clusterResource = map[string]string{
"project_id": testutil.ProjectId,
"name": fmt.Sprintf("cl-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
"name_min": fmt.Sprintf("cl-min-%s", acctest.RandStringFromCharSet(3, acctest.CharSetAlphaNum)),
"kubernetes_version_min": "1.28",
"kubernetes_version_used": "1.28.11",
"kubernetes_version_min_new": "1.29",
"kubernetes_version_used_new": "1.29.6",
"kubernetes_version_min": "1.29",
"kubernetes_version_used": "1.29.10",
"kubernetes_version_min_new": "1.30",
"kubernetes_version_used_new": "1.30.6",
"nodepool_name": "np-acc-test",
"nodepool_name_min": "np-acc-min-test",
"nodepool_machine_type": "b1.2",
"nodepool_os_version_min": "3815.2",
"nodepool_os_version_used": "3815.2.3",
"nodepool_os_version_min_new": "3815.2.3",
"nodepool_os_version_used_new": "3815.2.3",
"nodepool_os_version_min": "3975.2",
"nodepool_os_version_used": "3975.2.0",
"nodepool_os_version_min_new": "3975.2.1",
"nodepool_os_version_used_new": "3975.2.1",
"nodepool_os_name": "flatcar",
"nodepool_minimum": "2",
"nodepool_maximum": "3",
@ -46,6 +46,7 @@ var clusterResource = map[string]string{
"nodepool_taints_effect": "PreferNoSchedule",
"nodepool_taints_key": "tkey",
"nodepool_taints_value": "tvalue",
"nodepool_allow_system_components": "true",
"extensions_acl_enabled": "true",
"extensions_acl_cidrs": "192.168.0.0/24",
"extensions_argus_enabled": "false",
@ -96,6 +97,7 @@ func getConfig(kubernetesVersion, nodePoolMachineOSVersion string, maintenanceEn
key = "%s"
value = "%s"
}]
allow_system_components = %s
}]
extensions = {
acl = {
@ -169,6 +171,7 @@ func getConfig(kubernetesVersion, nodePoolMachineOSVersion string, maintenanceEn
clusterResource["nodepool_taints_effect"],
clusterResource["nodepool_taints_key"],
clusterResource["nodepool_taints_value"],
clusterResource["nodepool_allow_system_components"],
clusterResource["extensions_acl_enabled"],
clusterResource["extensions_acl_cidrs"],
clusterResource["extensions_argus_enabled"],
@ -233,6 +236,7 @@ func TestAccSKE(t *testing.T) {
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.taints.0.effect", clusterResource["nodepool_taints_effect"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.taints.0.key", clusterResource["nodepool_taints_key"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.taints.0.value", clusterResource["nodepool_taints_value"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.allow_system_components", clusterResource["nodepool_allow_system_components"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.cri", clusterResource["nodepool_cri"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.enabled", clusterResource["extensions_acl_enabled"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),
@ -343,6 +347,7 @@ func TestAccSKE(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.taints.0.effect", clusterResource["nodepool_taints_effect"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.taints.0.key", clusterResource["nodepool_taints_key"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.taints.0.value", clusterResource["nodepool_taints_value"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.allow_system_components", clusterResource["nodepool_allow_system_components"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.cri", clusterResource["nodepool_cri"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.enabled", clusterResource["extensions_acl_enabled"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),