Skip to content

Commit c4a5a02

Browse files
committed
Add support for pool labels
1 parent dcc172d commit c4a5a02

File tree

3 files changed

+277
-0
lines changed

3 files changed

+277
-0
lines changed

gridscale/resource_gridscale_k8s.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
k8sRocketStorageSupportRelease = "1.26"
2727
k8sMultiNodePoolSupportRelease = "1.30"
2828
k8sTaintKeyValueRegex = `^[a-zA-Z0-9-]+$`
29+
k8sLabelKeyValueRegex = `^[a-zA-Z0-9-]+$`
2930
)
3031

3132
// ResourceGridscaleK8sModeler struct represents a modeler of the gridscale k8s resource.
@@ -145,6 +146,27 @@ func (rgk8sm *ResourceGridscaleK8sModeler) buildInputSchema() map[string]*schema
145146
},
146147
},
147148
},
149+
"labels": {
150+
Type: schema.TypeList,
151+
Optional: true,
152+
Description: "List of labels to be applied to the nodes of this pool.",
153+
Elem: &schema.Resource{
154+
Schema: map[string]*schema.Schema{
155+
"key": {
156+
Type: schema.TypeString,
157+
Required: true,
158+
Description: "The key of the label.",
159+
ValidateFunc: validation.StringMatch(regexp.MustCompile(k8sLabelKeyValueRegex), "key must match Kubernetes label key format"),
160+
},
161+
"value": {
162+
Type: schema.TypeString,
163+
Required: true,
164+
Description: "The value of the label.",
165+
ValidateFunc: validation.StringMatch(regexp.MustCompile(k8sLabelKeyValueRegex), "value must match Kubernetes label value format"),
166+
},
167+
},
168+
},
169+
},
148170
}
149171
return map[string]*schema.Schema{
150172
"name": {
@@ -906,6 +928,28 @@ func resourceGridscaleK8sRead(d *schema.ResourceData, meta interface{}) error {
906928
nodePoolRead["taints"] = taintsRead
907929
}
908930

931+
// Handle labels
932+
if labels, isLabelsSet := nodePoolSet["labels"]; isLabelsSet {
933+
labelsList := labels.([]any)
934+
labelsRead := make([]map[string]any, 0)
935+
936+
for _, labelInterface := range labelsList {
937+
label := labelInterface.(map[string]any)
938+
labelRead := make(map[string]any)
939+
940+
if key, isKeySet := label["key"]; isKeySet {
941+
labelRead["key"] = key
942+
}
943+
if value, isValueSet := label["value"]; isValueSet {
944+
labelRead["value"] = value
945+
}
946+
947+
labelsRead = append(labelsRead, labelRead)
948+
}
949+
950+
nodePoolRead["labels"] = labelsRead
951+
}
952+
909953
nodePools = append(nodePools, nodePoolRead)
910954
}
911955
}
@@ -1031,6 +1075,28 @@ func resourceGridscaleK8sCreate(d *schema.ResourceData, meta interface{}) error
10311075
nodePool["taints"] = taintsRequest
10321076
}
10331077

1078+
// Handle labels
1079+
if labelsInterface, isLabelsSet := d.GetOk(fmt.Sprintf("node_pool.%d.labels", index)); isLabelsSet {
1080+
labelsList := labelsInterface.([]any)
1081+
labelsRequest := make([]map[string]any, 0)
1082+
1083+
for _, labelInterface := range labelsList {
1084+
label := labelInterface.(map[string]any)
1085+
labelRequest := make(map[string]any)
1086+
1087+
if key, isKeySet := label["key"]; isKeySet {
1088+
labelRequest["key"] = key
1089+
}
1090+
if value, isValueSet := label["value"]; isValueSet {
1091+
labelRequest["value"] = value
1092+
}
1093+
1094+
labelsRequest = append(labelsRequest, labelRequest)
1095+
}
1096+
1097+
nodePool["labels"] = labelsRequest
1098+
}
1099+
10341100
nodePools = append(nodePools, nodePool)
10351101
}
10361102
parameters["pools"] = nodePools
@@ -1217,6 +1283,28 @@ func resourceGridscaleK8sUpdate(d *schema.ResourceData, meta interface{}) error
12171283
nodePool["taints"] = taintsRequest
12181284
}
12191285

1286+
// Handle labels
1287+
if labelsInterface, isLabelsSet := d.GetOk(fmt.Sprintf("node_pool.%d.labels", index)); isLabelsSet {
1288+
labelsList := labelsInterface.([]any)
1289+
labelsRequest := make([]map[string]any, 0)
1290+
1291+
for _, labelInterface := range labelsList {
1292+
label := labelInterface.(map[string]any)
1293+
labelRequest := make(map[string]any)
1294+
1295+
if key, isKeySet := label["key"]; isKeySet {
1296+
labelRequest["key"] = key
1297+
}
1298+
if value, isValueSet := label["value"]; isValueSet {
1299+
labelRequest["value"] = value
1300+
}
1301+
1302+
labelsRequest = append(labelsRequest, labelRequest)
1303+
}
1304+
1305+
nodePool["labels"] = labelsRequest
1306+
}
1307+
12201308
nodePools = append(nodePools, nodePool)
12211309
}
12221310
parameters["pools"] = nodePools
@@ -1559,6 +1647,59 @@ func validateK8sParameters(d *schema.ResourceDiff, template gsclient.PaaSTemplat
15591647
}
15601648
}
15611649
}
1650+
1651+
// Validate labels
1652+
nodePoolParameterLabels, labels_ok := templateParameterNodePools.Schema.Schema["labels"]
1653+
if labels_ok {
1654+
if labelsInterface, isLabelsSet := d.GetOk(fmt.Sprintf("node_pool.%d.labels", index)); isLabelsSet {
1655+
labelsList := labelsInterface.([]any)
1656+
1657+
// Check if labels list is empty when it's allowed to be
1658+
if len(labelsList) == 0 && !nodePoolParameterLabels.Empty {
1659+
errorMessages = append(
1660+
errorMessages,
1661+
fmt.Sprintf("Invalid 'node_pool.%d.labels' value. Labels list cannot be empty.\n", index),
1662+
)
1663+
}
1664+
1665+
// Validate each label
1666+
for _, labelInterface := range labelsList {
1667+
label := labelInterface.(map[string]any)
1668+
1669+
// Validate key
1670+
if key, isKeySet := label["key"]; isKeySet {
1671+
keyStr := key.(string)
1672+
if !regexp.MustCompile(k8sLabelKeyValueRegex).MatchString(keyStr) {
1673+
errorMessages = append(
1674+
errorMessages,
1675+
fmt.Sprintf("Invalid 'node_pool.%d.labels.key' value. Key must match Kubernetes label key format.\n", index),
1676+
)
1677+
}
1678+
} else {
1679+
errorMessages = append(
1680+
errorMessages,
1681+
fmt.Sprintf("Invalid 'node_pool.%d.labels' value. Key is required.\n", index),
1682+
)
1683+
}
1684+
1685+
// Validate value
1686+
if value, isValueSet := label["value"]; isValueSet {
1687+
valueStr := value.(string)
1688+
if !regexp.MustCompile(k8sLabelKeyValueRegex).MatchString(valueStr) {
1689+
errorMessages = append(
1690+
errorMessages,
1691+
fmt.Sprintf("Invalid 'node_pool.%d.labels.value' value. Value must match Kubernetes label value format.\n", index),
1692+
)
1693+
}
1694+
} else {
1695+
errorMessages = append(
1696+
errorMessages,
1697+
fmt.Sprintf("Invalid 'node_pool.%d.labels' value. Value is required.\n", index),
1698+
)
1699+
}
1700+
}
1701+
}
1702+
}
15621703
}
15631704
}
15641705

gridscale/resource_gridscale_k8s_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,66 @@ func TestAccResourceGridscaleK8sBasic(t *testing.T) {
165165
"gridscale_k8s.foopaas", "surge_node", "true"),
166166
),
167167
},
168+
{
169+
Config: testAccCheckResourceGridscaleK8sConfigAddLabel(),
170+
Check: resource.ComposeTestCheckFunc(
171+
testAccCheckResourceGridscalePaaSExists("gridscale_k8s.foopaas", &object),
172+
resource.TestCheckResourceAttr(
173+
"gridscale_k8s.foopaas", "node_pool.0.name", "my-node-pool"),
174+
resource.TestCheckResourceAttr(
175+
"gridscale_k8s.foopaas", "node_pool.0.node_count", "1"),
176+
resource.TestCheckResourceAttr(
177+
"gridscale_k8s.foopaas", "node_pool.0.cores", "2"),
178+
resource.TestCheckResourceAttr(
179+
"gridscale_k8s.foopaas", "node_pool.0.memory", "4"),
180+
resource.TestCheckResourceAttr(
181+
"gridscale_k8s.foopaas", "node_pool.0.storage", "50"),
182+
resource.TestCheckResourceAttr(
183+
"gridscale_k8s.foopaas", "node_pool.0.storage_type", "storage_insane"),
184+
resource.TestCheckResourceAttr(
185+
"gridscale_k8s.foopaas", "node_pool.0.rocket_storage", "10"),
186+
resource.TestCheckResourceAttr(
187+
"gridscale_k8s.foopaas", "node_pool.0.labels.#", "2"),
188+
resource.TestCheckResourceAttr(
189+
"gridscale_k8s.foopaas", "node_pool.0.labels.0.key", "example-key"),
190+
resource.TestCheckResourceAttr(
191+
"gridscale_k8s.foopaas", "node_pool.0.labels.0.value", "example-value"),
192+
resource.TestCheckResourceAttr(
193+
"gridscale_k8s.foopaas", "node_pool.0.labels.1.key", "another-key"),
194+
resource.TestCheckResourceAttr(
195+
"gridscale_k8s.foopaas", "node_pool.0.labels.1.value", "another-value"),
196+
resource.TestCheckResourceAttr(
197+
"gridscale_k8s.foopaas", "k8s_hubble", "true"),
198+
resource.TestCheckResourceAttr(
199+
"gridscale_k8s.foopaas", "surge_node", "true"),
200+
),
201+
},
202+
{
203+
Config: testAccCheckResourceGridscaleK8sConfigRemoveLabel(),
204+
Check: resource.ComposeTestCheckFunc(
205+
testAccCheckResourceGridscalePaaSExists("gridscale_k8s.foopaas", &object),
206+
resource.TestCheckResourceAttr(
207+
"gridscale_k8s.foopaas", "node_pool.0.name", "my-node-pool"),
208+
resource.TestCheckResourceAttr(
209+
"gridscale_k8s.foopaas", "node_pool.0.node_count", "1"),
210+
resource.TestCheckResourceAttr(
211+
"gridscale_k8s.foopaas", "node_pool.0.cores", "2"),
212+
resource.TestCheckResourceAttr(
213+
"gridscale_k8s.foopaas", "node_pool.0.memory", "4"),
214+
resource.TestCheckResourceAttr(
215+
"gridscale_k8s.foopaas", "node_pool.0.storage", "50"),
216+
resource.TestCheckResourceAttr(
217+
"gridscale_k8s.foopaas", "node_pool.0.storage_type", "storage_insane"),
218+
resource.TestCheckResourceAttr(
219+
"gridscale_k8s.foopaas", "node_pool.0.rocket_storage", "10"),
220+
resource.TestCheckResourceAttr(
221+
"gridscale_k8s.foopaas", "node_pool.0.labels.#", "0"),
222+
resource.TestCheckResourceAttr(
223+
"gridscale_k8s.foopaas", "k8s_hubble", "true"),
224+
resource.TestCheckResourceAttr(
225+
"gridscale_k8s.foopaas", "surge_node", "true"),
226+
),
227+
},
168228
{
169229
Config: testAccCheckResourceGridscaleK8sConfigNodeCountIncrease(),
170230
Check: resource.ComposeTestCheckFunc(
@@ -405,6 +465,53 @@ func testAccCheckResourceGridscaleK8sConfigRemoveTaint() string {
405465
`
406466
}
407467

468+
func testAccCheckResourceGridscaleK8sConfigAddLabel() string {
469+
return `
470+
resource "gridscale_k8s" "foopaas" {
471+
name = "newname"
472+
release = "1.30"
473+
node_pool {
474+
name = "my-node-pool"
475+
node_count = 1
476+
cores = 2
477+
memory = 4
478+
storage = 50
479+
storage_type = "storage_insane"
480+
rocket_storage = 10
481+
labels {
482+
key = "example-key"
483+
value = "example-value"
484+
}
485+
labels {
486+
key = "another-key"
487+
value = "another-value"
488+
}
489+
}
490+
k8s_hubble = true
491+
}
492+
`
493+
}
494+
495+
func testAccCheckResourceGridscaleK8sConfigRemoveLabel() string {
496+
return `
497+
resource "gridscale_k8s" "foopaas" {
498+
name = "newname"
499+
release = "1.30"
500+
node_pool {
501+
name = "my-node-pool"
502+
node_count = 1
503+
cores = 2
504+
memory = 4
505+
storage = 50
506+
storage_type = "storage_insane"
507+
rocket_storage = 10
508+
labels = []
509+
}
510+
k8s_hubble = true
511+
}
512+
`
513+
}
514+
408515
func testAccCheckResourceGridscaleK8sConfigNodeCountDecrease() string {
409516
return `
410517
resource "gridscale_k8s" "foopaas" {

website/docs/r/k8s.html.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ The following arguments are supported:
6363
* `storage_type` - Storage type (one of storage, storage_high, storage_insane).
6464
* `rocket_storage` - Rocket storage per worker node (in GiB).
6565
* `taints` - List of taints to be applied to the nodes of this pool.
66+
* `labels` - List of labels to be applied to the nodes of this pool.
6667
* `surge_node` - Enable surge node to avoid resources shortage during the cluster upgrade (Default: true).
6768
* `cluster_cidr` - (Immutable) The cluster CIDR that will be used to generate the CIDR of nodes, services, and pods. The allowed CIDR prefix length is /16. If the cluster CIDR is not set, the cluster will use "10.244.0.0/16" as it default (even though the `cluster_cidr` in the k8s resource is empty).
6869
* `cluster_traffic_encryption` - Enables cluster encryption via wireguard if true. Only available for GSK version 1.29 and above. Default is false.
@@ -120,6 +121,33 @@ resource "gridscale_k8s" "k8s-test" {
120121
}
121122
```
122123

124+
#### Pool Labels
125+
126+
```terraform
127+
resource "gridscale_k8s" "k8s-test" {
128+
name = "test"
129+
release = "1.30" # instead, gsk_version can be set.
130+
131+
node_pool {
132+
name = "pool-0"
133+
node_count = 2
134+
cores = 2
135+
memory = 4
136+
storage = 30
137+
storage_type = "storage_insane"
138+
139+
labels {
140+
key = "example-key"
141+
value = "example-value"
142+
}
143+
labels {
144+
key = "another-key"
145+
value = "another-value"
146+
}
147+
}
148+
}
149+
```
150+
123151
## Timeouts
124152

125153
Timeouts configuration options (in seconds):
@@ -152,6 +180,7 @@ This resource exports the following attributes:
152180
* `storage_type` - See Argument Reference above.
153181
* `rocket_storage` - See Argument Reference above.
154182
* `taints` - See Argument Reference above.
183+
* `labels` - See Argument Reference above.
155184
* `surge_node` - See Argument Reference above.
156185
* `cluster_cidr` - See Argument Reference above.
157186
* `cluster_traffic_encryption` - See Argument Reference above.

0 commit comments

Comments
 (0)