Skip to content

Commit ac0840f

Browse files
authored
Allow specifying network ID in SKE cluster (#368)
1 parent e2e5f19 commit ac0840f

6 files changed

Lines changed: 204 additions & 7 deletions

File tree

docs/data-sources/ske_cluster.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ This should be used with care since it also disables a couple of other features
4040
- `kubernetes_version_min` (String) The minimum Kubernetes version, this field is always nil. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current kubernetes version being used for your cluster, use the `kubernetes_version_used` field.
4141
- `kubernetes_version_used` (String) Full Kubernetes version used. For example, if `1.22` was selected, this value may result to `1.22.15`
4242
- `maintenance` (Attributes) A single maintenance block as defined below (see [below for nested schema](#nestedatt--maintenance))
43+
- `network` (Attributes) Network block as defined below. (see [below for nested schema](#nestedatt--network))
4344
- `node_pools` (Attributes List) One or more `node_pool` block as defined below. (see [below for nested schema](#nestedatt--node_pools))
4445

4546
<a id="nestedatt--extensions"></a>
@@ -90,6 +91,14 @@ Read-Only:
9091
- `start` (String) Date time for maintenance window start.
9192

9293

94+
<a id="nestedatt--network"></a>
95+
### Nested Schema for `network`
96+
97+
Read-Only:
98+
99+
- `id` (String) ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed.
100+
101+
93102
<a id="nestedatt--node_pools"></a>
94103
### Nested Schema for `node_pools`
95104

docs/resources/ske_cluster.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Deprecated as of Kubernetes 1.25 and later
5555
- `kubernetes_version` (String, Deprecated) Kubernetes version. Must only contain major and minor version (e.g. 1.22). This field is deprecated, use `kubernetes_version_min instead`
5656
- `kubernetes_version_min` (String) The minimum Kubernetes version. This field will be used to set the minimum kubernetes version on creation/update of the cluster. If unset, the latest supported Kubernetes version will be used. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current kubernetes version being used for your cluster, use the read-only `kubernetes_version_used` field.
5757
- `maintenance` (Attributes) A single maintenance block as defined below. (see [below for nested schema](#nestedatt--maintenance))
58+
- `network` (Attributes) Network block as defined below. (see [below for nested schema](#nestedatt--network))
5859

5960
### Read-Only
6061

@@ -162,3 +163,11 @@ Optional:
162163

163164
- `enable_kubernetes_version_updates` (Boolean) Flag to enable/disable auto-updates of the Kubernetes version. Defaults to `true`. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html).
164165
- `enable_machine_image_version_updates` (Boolean) Flag to enable/disable auto-updates of the OS image version. Defaults to `true`. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html).
166+
167+
168+
<a id="nestedatt--network"></a>
169+
### Nested Schema for `network`
170+
171+
Optional:
172+
173+
- `id` (String) ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed.

docs/resources/ske_project.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title: "stackit_ske_project Resource - stackit"
44
subcategory: ""
55
description: |-
6-
SKE project resource schema. Must have a region specified in the provider configuration. This resource allows you to enable the SKE service and you can only have one per project. Before deleting this resource, all SKE clusters associated to the project must be deleted. Otherwise, error would occur due to the existing clusters. In such case, it is highly recommended to remove the SKE project from the state, directly using the "terraform state rm". Warning: SKE project resource is no longer in use and will be removed with the next minor release. SKE service enablement is done automatically when a new cluster is created.
6+
SKE project resource schema. Must have a region specified in the provider configuration. This resource allows you to enable the SKE service and you can only have one per project. Before deleting this resource, all SKE clusters associated to the project must be deleted. Otherwise, error would occur due to the existing clusters. In such case, it is highly recommended to remove the SKE project from the state, directly using the "terraform state rm". Warning: SKE project resource is no longer in use and will be removed after October 10th 2024. SKE service enablement is done automatically when a new cluster is created.
77
---
88

99
# stackit_ske_project (Resource)
1010

11-
SKE project resource schema. Must have a `region` specified in the provider configuration. This resource allows you to enable the SKE service and you can only have one per project. Before deleting this resource, all SKE clusters associated to the project must be deleted. Otherwise, error would occur due to the existing clusters. In such case, it is highly recommended to remove the SKE project from the state, directly using the "`terraform state rm`". Warning: SKE project resource is no longer in use and will be removed with the next minor release. SKE service enablement is done automatically when a new cluster is created.
11+
SKE project resource schema. Must have a `region` specified in the provider configuration. This resource allows you to enable the SKE service and you can only have one per project. Before deleting this resource, all SKE clusters associated to the project must be deleted. Otherwise, error would occur due to the existing clusters. In such case, it is highly recommended to remove the SKE project from the state, directly using the "`terraform state rm`". Warning: SKE project resource is no longer in use and will be removed after October 10th 2024. SKE service enablement is done automatically when a new cluster is created.
1212

1313
## Example Usage
1414

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,20 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
228228
},
229229
},
230230

231+
"network": schema.SingleNestedAttribute{
232+
Description: "Network block as defined below.",
233+
Computed: true,
234+
Attributes: map[string]schema.Attribute{
235+
"id": schema.StringAttribute{
236+
Description: "ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed.",
237+
Computed: true,
238+
Validators: []validator.String{
239+
validate.UUID(),
240+
},
241+
},
242+
},
243+
},
244+
231245
"hibernations": schema.ListNestedAttribute{
232246
Description: "One or more hibernation block as defined below.",
233247
Computed: true,

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

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type Model struct {
7373
AllowPrivilegedContainers types.Bool `tfsdk:"allow_privileged_containers"`
7474
NodePools types.List `tfsdk:"node_pools"`
7575
Maintenance types.Object `tfsdk:"maintenance"`
76+
Network types.Object `tfsdk:"network"`
7677
Hibernations types.List `tfsdk:"hibernations"`
7778
Extensions types.Object `tfsdk:"extensions"`
7879
KubeConfig types.String `tfsdk:"kube_config"`
@@ -148,6 +149,16 @@ var maintenanceTypes = map[string]attr.Type{
148149
"end": basetypes.StringType{},
149150
}
150151

152+
// Struct corresponding to Model.Network
153+
type network struct {
154+
ID types.String `tfsdk:"id"`
155+
}
156+
157+
// Types corresponding to network
158+
var networkTypes = map[string]attr.Type{
159+
"id": basetypes.StringType{},
160+
}
161+
151162
// Struct corresponding to Model.Hibernations[i]
152163
type hibernation struct {
153164
Start types.String `tfsdk:"start"`
@@ -491,6 +502,22 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
491502
},
492503
},
493504
},
505+
"network": schema.SingleNestedAttribute{
506+
Description: "Network block as defined below.",
507+
Optional: true,
508+
Attributes: map[string]schema.Attribute{
509+
"id": schema.StringAttribute{
510+
Description: "ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed.",
511+
Optional: true,
512+
Validators: []validator.String{
513+
validate.UUID(),
514+
},
515+
PlanModifiers: []planmodifier.String{
516+
stringplanmodifier.RequiresReplace(),
517+
},
518+
},
519+
},
520+
},
494521
"hibernations": schema.ListNestedAttribute{
495522
Description: "One or more hibernation block as defined below.",
496523
Optional: true,
@@ -731,6 +758,11 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
731758
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating maintenance API payload: %v", err))
732759
return
733760
}
761+
network, err := toNetworkPayload(ctx, model)
762+
if err != nil {
763+
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating network API payload: %v", err))
764+
return
765+
}
734766
hibernations, err := toHibernationsPayload(ctx, model)
735767
if err != nil {
736768
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating hibernations API payload: %v", err))
@@ -747,6 +779,7 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
747779
Hibernation: hibernations,
748780
Kubernetes: kubernetes,
749781
Maintenance: maintenance,
782+
Network: network,
750783
Nodepools: &nodePools,
751784
}
752785
_, err = r.client.CreateOrUpdateCluster(ctx, projectId, name).CreateOrUpdateClusterPayload(payload).Execute()
@@ -1174,6 +1207,22 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro
11741207
}, nil
11751208
}
11761209

1210+
func toNetworkPayload(ctx context.Context, m *Model) (*ske.V1Network, error) {
1211+
if m.Network.IsNull() || m.Network.IsUnknown() {
1212+
return nil, nil
1213+
}
1214+
1215+
network := network{}
1216+
diags := m.Network.As(ctx, &network, basetypes.ObjectAsOptions{})
1217+
if diags.HasError() {
1218+
return nil, fmt.Errorf("converting network object: %v", diags.Errors())
1219+
}
1220+
1221+
return &ske.V1Network{
1222+
Id: conversion.StringValueToPointer(network.ID),
1223+
}, nil
1224+
}
1225+
11771226
func mapFields(ctx context.Context, cl *ske.Cluster, m *Model) error {
11781227
if cl == nil {
11791228
return fmt.Errorf("response input is nil")
@@ -1206,19 +1255,23 @@ func mapFields(ctx context.Context, cl *ske.Cluster, m *Model) error {
12061255

12071256
err := mapNodePools(ctx, cl, m)
12081257
if err != nil {
1209-
return fmt.Errorf("mapping node_pools: %w", err)
1258+
return fmt.Errorf("map node_pools: %w", err)
12101259
}
12111260
err = mapMaintenance(ctx, cl, m)
12121261
if err != nil {
1213-
return fmt.Errorf("mapping maintenance: %w", err)
1262+
return fmt.Errorf("map maintenance: %w", err)
1263+
}
1264+
err = mapNetwork(cl, m)
1265+
if err != nil {
1266+
return fmt.Errorf("map network: %w", err)
12141267
}
12151268
err = mapHibernations(cl, m)
12161269
if err != nil {
1217-
return fmt.Errorf("mapping hibernations: %w", err)
1270+
return fmt.Errorf("map hibernations: %w", err)
12181271
}
12191272
err = mapExtensions(ctx, cl, m)
12201273
if err != nil {
1221-
return fmt.Errorf("mapping extensions: %w", err)
1274+
return fmt.Errorf("map extensions: %w", err)
12221275
}
12231276
return nil
12241277
}
@@ -1421,12 +1474,33 @@ func mapMaintenance(ctx context.Context, cl *ske.Cluster, m *Model) error {
14211474
}
14221475
maintenanceObject, diags := types.ObjectValue(maintenanceTypes, maintenanceValues)
14231476
if diags.HasError() {
1424-
return fmt.Errorf("creating flavor: %w", core.DiagsToError(diags))
1477+
return fmt.Errorf("create maintenance object: %w", core.DiagsToError(diags))
14251478
}
14261479
m.Maintenance = maintenanceObject
14271480
return nil
14281481
}
14291482

1483+
func mapNetwork(cl *ske.Cluster, m *Model) error {
1484+
if cl.Network == nil {
1485+
m.Network = types.ObjectNull(networkTypes)
1486+
return nil
1487+
}
1488+
1489+
id := types.StringNull()
1490+
if cl.Network.Id != nil {
1491+
id = types.StringValue(*cl.Network.Id)
1492+
}
1493+
networkValues := map[string]attr.Value{
1494+
"id": id,
1495+
}
1496+
networkObject, diags := types.ObjectValue(networkTypes, networkValues)
1497+
if diags.HasError() {
1498+
return fmt.Errorf("create network object: %w", core.DiagsToError(diags))
1499+
}
1500+
m.Network = networkObject
1501+
return nil
1502+
}
1503+
14301504
func getMaintenanceTimes(ctx context.Context, cl *ske.Cluster, m *Model) (startTime, endTime string, err error) {
14311505
startTimeAPI, err := time.Parse(time.RFC3339, *cl.Maintenance.TimeWindow.Start)
14321506
if err != nil {

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestMapFields(t *testing.T) {
4949
AllowPrivilegedContainers: types.BoolNull(),
5050
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
5151
Maintenance: types.ObjectNull(maintenanceTypes),
52+
Network: types.ObjectNull(networkTypes),
5253
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
5354
Extensions: types.ObjectNull(extensionsTypes),
5455
KubeConfig: types.StringNull(),
@@ -92,6 +93,9 @@ func TestMapFields(t *testing.T) {
9293
End: utils.Ptr("0010-11-12T13:14:15Z"),
9394
},
9495
},
96+
Network: &ske.V1Network{
97+
Id: utils.Ptr("nid"),
98+
},
9599
Name: utils.Ptr("name"),
96100
Nodepools: &[]ske.Nodepool{
97101
{
@@ -194,6 +198,9 @@ func TestMapFields(t *testing.T) {
194198
"start": types.StringValue("03:04:05+06:00"),
195199
"end": types.StringValue("13:14:15Z"),
196200
}),
201+
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
202+
"id": types.StringValue("nid"),
203+
}),
197204
Hibernations: types.ListValueMust(
198205
types.ObjectType{AttrTypes: hibernationTypes},
199206
[]attr.Value{
@@ -223,6 +230,30 @@ func TestMapFields(t *testing.T) {
223230
},
224231
true,
225232
},
233+
{
234+
"nil_network_id",
235+
types.ObjectNull(extensionsTypes),
236+
&ske.Cluster{
237+
Name: utils.Ptr("name"),
238+
Network: &ske.V1Network{},
239+
},
240+
Model{
241+
Id: types.StringValue("pid,name"),
242+
ProjectId: types.StringValue("pid"),
243+
Name: types.StringValue("name"),
244+
KubernetesVersion: types.StringNull(),
245+
AllowPrivilegedContainers: types.BoolNull(),
246+
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
247+
Maintenance: types.ObjectNull(maintenanceTypes),
248+
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
249+
"id": types.StringNull(),
250+
}),
251+
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
252+
Extensions: types.ObjectNull(extensionsTypes),
253+
KubeConfig: types.StringNull(),
254+
},
255+
true,
256+
},
226257
{
227258
"extensions_mixed_values",
228259
types.ObjectNull(extensionsTypes),
@@ -1936,3 +1967,63 @@ func TestGetLatestSupportedMachineVersion(t *testing.T) {
19361967
})
19371968
}
19381969
}
1970+
1971+
func TestToNetworkPayload(t *testing.T) {
1972+
tests := []struct {
1973+
description string
1974+
model *Model
1975+
expected *ske.V1Network
1976+
isValid bool
1977+
}{
1978+
{
1979+
"base",
1980+
&Model{
1981+
ProjectId: types.StringValue("pid"),
1982+
Name: types.StringValue("name"),
1983+
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
1984+
"id": types.StringValue("nid"),
1985+
}),
1986+
},
1987+
&ske.V1Network{
1988+
Id: utils.Ptr("nid"),
1989+
},
1990+
true,
1991+
},
1992+
{
1993+
"no_id",
1994+
&Model{
1995+
ProjectId: types.StringValue("pid"),
1996+
Name: types.StringValue("name"),
1997+
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
1998+
"id": types.StringNull(),
1999+
}),
2000+
},
2001+
&ske.V1Network{},
2002+
true,
2003+
},
2004+
{
2005+
"no_network",
2006+
&Model{
2007+
ProjectId: types.StringValue("pid"),
2008+
Name: types.StringValue("name"),
2009+
Network: types.ObjectNull(networkTypes),
2010+
},
2011+
nil,
2012+
true,
2013+
},
2014+
}
2015+
for _, tt := range tests {
2016+
t.Run(tt.description, func(t *testing.T) {
2017+
payload, err := toNetworkPayload(context.Background(), tt.model)
2018+
if !tt.isValid && err == nil {
2019+
t.Fatalf("Should have failed")
2020+
}
2021+
if tt.isValid {
2022+
diff := cmp.Diff(payload, tt.expected)
2023+
if diff != "" {
2024+
t.Fatalf("Data does not match: %s", diff)
2025+
}
2026+
}
2027+
})
2028+
}
2029+
}

0 commit comments

Comments
 (0)