Skip to content

Commit ab232d6

Browse files
ChristianHammChristian Hammrubenhoenle
authored
feat(loadbalancer): support service plan attribute (#858)
relates to STACKITLB-250 Co-authored-by: Christian Hamm <Christian.Hamm@mail.schwarz> Co-authored-by: Ruben Hönle <git@hoenle.xyz>
1 parent 3255f1e commit ab232d6

File tree

8 files changed

+174
-0
lines changed

8 files changed

+174
-0
lines changed

docs/data-sources/loadbalancer.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ data "stackit_loadbalancer" "example" {
3838
- `listeners` (Attributes List) List of all listeners which will accept traffic. Limited to 20. (see [below for nested schema](#nestedatt--listeners))
3939
- `networks` (Attributes List) List of networks that listeners and targets reside in. (see [below for nested schema](#nestedatt--networks))
4040
- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options))
41+
- `plan_id` (String) The service plan ID. If not defined, the default service plan is `p10`. Possible values are: `p10`, `p50`, `p250`, `p750`.
4142
- `private_address` (String) Transient private Load Balancer IP address. It can change any time.
4243
- `target_pools` (Attributes List) List of all target pools which will be used in the Load Balancer. Limited to 20. (see [below for nested schema](#nestedatt--target_pools))
4344

docs/resources/loadbalancer.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ resource "stackit_server" "boot-from-image" {
7070
resource "stackit_loadbalancer" "example" {
7171
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
7272
name = "example-load-balancer"
73+
plan_id = "p10"
7374
target_pools = [
7475
{
7576
name = "example-target-pool"
@@ -125,6 +126,7 @@ resource "stackit_loadbalancer" "example" {
125126

126127
- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed.
127128
- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options))
129+
- `plan_id` (String) The service plan ID. If not defined, the default service plan is `p10`. Possible values are: `p10`, `p50`, `p250`, `p750`.
128130
- `region` (String) The resource region. If not defined, the provider region is used.
129131

130132
### Read-Only

examples/resources/stackit_loadbalancer/resource.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ resource "stackit_server" "boot-from-image" {
5151
resource "stackit_loadbalancer" "example" {
5252
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
5353
name = "example-load-balancer"
54+
plan_id = "p10"
5455
target_pools = [
5556
{
5657
name = "example-target-pool"

stackit/internal/services/loadbalancer/loadbalancer/datasource.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ func (r *loadBalancerDataSource) Configure(ctx context.Context, req datasource.C
6262

6363
// Schema defines the schema for the data source.
6464
func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
65+
servicePlanOptions := []string{"p10", "p50", "p250", "p750"}
66+
6567
descriptions := map[string]string{
6668
"main": "Load Balancer data source schema. Must have a `region` specified in the provider configuration.",
6769
"id": "Terraform's internal resource ID. It is structured as \"`project_id`\",\"region\",\"`name`\".",
@@ -72,6 +74,7 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
7274
"protocol": "Protocol is the highest network protocol we understand to load balance.",
7375
"target_pool": "Reference target pool by target pool name.",
7476
"name": "Load balancer name.",
77+
"plan_id": "The service plan ID. If not defined, the default service plan is `p10`. " + utils.FormatPossibleValues(servicePlanOptions...),
7578
"networks": "List of networks that listeners and targets reside in.",
7679
"network_id": "Openstack network ID.",
7780
"role": "The role defines how the load balancer is using the network.",
@@ -122,6 +125,10 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
122125
Description: descriptions["external_address"],
123126
Computed: true,
124127
},
128+
"plan_id": schema.StringAttribute{
129+
Description: descriptions["plan_id"],
130+
Computed: true,
131+
},
125132
"listeners": schema.ListNestedAttribute{
126133
Description: descriptions["listeners"],
127134
Computed: true,

stackit/internal/services/loadbalancer/loadbalancer/resource.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type Model struct {
5353
ExternalAddress types.String `tfsdk:"external_address"`
5454
Listeners types.List `tfsdk:"listeners"`
5555
Name types.String `tfsdk:"name"`
56+
PlanId types.String `tfsdk:"plan_id"`
5657
Networks types.List `tfsdk:"networks"`
5758
Options types.Object `tfsdk:"options"`
5859
PrivateAddress types.String `tfsdk:"private_address"`
@@ -295,6 +296,7 @@ func (r *loadBalancerResource) Configure(ctx context.Context, req resource.Confi
295296
func (r *loadBalancerResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
296297
protocolOptions := []string{"PROTOCOL_UNSPECIFIED", "PROTOCOL_TCP", "PROTOCOL_UDP", "PROTOCOL_TCP_PROXY", "PROTOCOL_TLS_PASSTHROUGH"}
297298
roleOptions := []string{"ROLE_UNSPECIFIED", "ROLE_LISTENERS_AND_TARGETS", "ROLE_LISTENERS", "ROLE_TARGETS"}
299+
servicePlanOptions := []string{"p10", "p50", "p250", "p750"}
298300

299301
descriptions := map[string]string{
300302
"main": "Load Balancer resource schema.",
@@ -306,6 +308,7 @@ func (r *loadBalancerResource) Schema(_ context.Context, _ resource.SchemaReques
306308
"protocol": "Protocol is the highest network protocol we understand to load balance. " + utils.SupportedValuesDocumentation(protocolOptions),
307309
"target_pool": "Reference target pool by target pool name.",
308310
"name": "Load balancer name.",
311+
"plan_id": "The service plan ID. If not defined, the default service plan is `p10`. " + utils.FormatPossibleValues(servicePlanOptions...),
309312
"networks": "List of networks that listeners and targets reside in.",
310313
"network_id": "Openstack network ID.",
311314
"role": "The role defines how the load balancer is using the network. " + utils.SupportedValuesDocumentation(roleOptions),
@@ -370,6 +373,14 @@ The example below creates the supporting infrastructure using the STACKIT Terraf
370373
stringplanmodifier.RequiresReplace(),
371374
},
372375
},
376+
"plan_id": schema.StringAttribute{
377+
Description: descriptions["plan_id"],
378+
Optional: true,
379+
Computed: true,
380+
PlanModifiers: []planmodifier.String{
381+
stringplanmodifier.RequiresReplace(),
382+
},
383+
},
373384
"listeners": schema.ListNestedAttribute{
374385
Description: descriptions["listeners"],
375386
Required: true,
@@ -899,6 +910,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*loadbalancer.CreateLoa
899910
ExternalAddress: conversion.StringValueToPointer(model.ExternalAddress),
900911
Listeners: listenersPayload,
901912
Name: conversion.StringValueToPointer(model.Name),
913+
PlanId: conversion.StringValueToPointer(model.PlanId),
902914
Networks: networksPayload,
903915
Options: optionsPayload,
904916
TargetPools: targetPoolsPayload,
@@ -1206,6 +1218,7 @@ func mapFields(ctx context.Context, lb *loadbalancer.LoadBalancer, m *Model, reg
12061218
m.Name = types.StringValue(name)
12071219
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), name)
12081220

1221+
m.PlanId = types.StringPointerValue(lb.PlanId)
12091222
m.ExternalAddress = types.StringPointerValue(lb.ExternalAddress)
12101223
m.PrivateAddress = types.StringPointerValue(lb.PrivateAddress)
12111224

stackit/internal/services/loadbalancer/loadbalancer/resource_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,149 @@ func TestToCreatePayload(t *testing.T) {
184184
},
185185
true,
186186
},
187+
{
188+
"service_plan_ok",
189+
&Model{
190+
PlanId: types.StringValue("p10"),
191+
ExternalAddress: types.StringValue("external_address"),
192+
Listeners: types.ListValueMust(types.ObjectType{AttrTypes: listenerTypes}, []attr.Value{
193+
types.ObjectValueMust(listenerTypes, map[string]attr.Value{
194+
"display_name": types.StringValue("display_name"),
195+
"port": types.Int64Value(80),
196+
"protocol": types.StringValue(string(loadbalancer.LISTENERPROTOCOL_TCP)),
197+
"server_name_indicators": types.ListValueMust(types.ObjectType{AttrTypes: serverNameIndicatorTypes}, []attr.Value{
198+
types.ObjectValueMust(
199+
serverNameIndicatorTypes,
200+
map[string]attr.Value{
201+
"name": types.StringValue("domain.com"),
202+
},
203+
),
204+
},
205+
),
206+
"target_pool": types.StringValue("target_pool"),
207+
}),
208+
}),
209+
Name: types.StringValue("name"),
210+
Networks: types.ListValueMust(types.ObjectType{AttrTypes: networkTypes}, []attr.Value{
211+
types.ObjectValueMust(networkTypes, map[string]attr.Value{
212+
"network_id": types.StringValue("network_id"),
213+
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
214+
}),
215+
types.ObjectValueMust(networkTypes, map[string]attr.Value{
216+
"network_id": types.StringValue("network_id_2"),
217+
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
218+
}),
219+
}),
220+
Options: types.ObjectValueMust(
221+
optionsTypes,
222+
map[string]attr.Value{
223+
"acl": types.SetValueMust(
224+
types.StringType,
225+
[]attr.Value{types.StringValue("cidr")}),
226+
"private_network_only": types.BoolValue(true),
227+
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
228+
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
229+
"credentials_ref": types.StringValue("logs-credentials_ref"),
230+
"push_url": types.StringValue("logs-push_url"),
231+
}),
232+
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
233+
"credentials_ref": types.StringValue("metrics-credentials_ref"),
234+
"push_url": types.StringValue("metrics-push_url"),
235+
}),
236+
}),
237+
},
238+
),
239+
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
240+
types.ObjectValueMust(targetPoolTypes, map[string]attr.Value{
241+
"active_health_check": types.ObjectValueMust(activeHealthCheckTypes, map[string]attr.Value{
242+
"healthy_threshold": types.Int64Value(1),
243+
"interval": types.StringValue("2s"),
244+
"interval_jitter": types.StringValue("3s"),
245+
"timeout": types.StringValue("4s"),
246+
"unhealthy_threshold": types.Int64Value(5),
247+
}),
248+
"name": types.StringValue("name"),
249+
"target_port": types.Int64Value(80),
250+
"targets": types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
251+
types.ObjectValueMust(targetTypes, map[string]attr.Value{
252+
"display_name": types.StringValue("display_name"),
253+
"ip": types.StringValue("ip"),
254+
}),
255+
}),
256+
"session_persistence": types.ObjectValueMust(sessionPersistenceTypes, map[string]attr.Value{
257+
"use_source_ip_address": types.BoolValue(true),
258+
}),
259+
}),
260+
}),
261+
},
262+
&loadbalancer.CreateLoadBalancerPayload{
263+
PlanId: utils.Ptr("p10"),
264+
ExternalAddress: utils.Ptr("external_address"),
265+
Listeners: &[]loadbalancer.Listener{
266+
{
267+
DisplayName: utils.Ptr("display_name"),
268+
Port: utils.Ptr(int64(80)),
269+
Protocol: loadbalancer.LISTENERPROTOCOL_TCP.Ptr(),
270+
ServerNameIndicators: &[]loadbalancer.ServerNameIndicator{
271+
{
272+
Name: utils.Ptr("domain.com"),
273+
},
274+
},
275+
TargetPool: utils.Ptr("target_pool"),
276+
},
277+
},
278+
Name: utils.Ptr("name"),
279+
Networks: &[]loadbalancer.Network{
280+
{
281+
NetworkId: utils.Ptr("network_id"),
282+
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
283+
},
284+
{
285+
NetworkId: utils.Ptr("network_id_2"),
286+
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
287+
},
288+
},
289+
Options: &loadbalancer.LoadBalancerOptions{
290+
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
291+
AllowedSourceRanges: &[]string{"cidr"},
292+
},
293+
PrivateNetworkOnly: utils.Ptr(true),
294+
Observability: &loadbalancer.LoadbalancerOptionObservability{
295+
Logs: &loadbalancer.LoadbalancerOptionLogs{
296+
CredentialsRef: utils.Ptr("logs-credentials_ref"),
297+
PushUrl: utils.Ptr("logs-push_url"),
298+
},
299+
Metrics: &loadbalancer.LoadbalancerOptionMetrics{
300+
CredentialsRef: utils.Ptr("metrics-credentials_ref"),
301+
PushUrl: utils.Ptr("metrics-push_url"),
302+
},
303+
},
304+
},
305+
TargetPools: &[]loadbalancer.TargetPool{
306+
{
307+
ActiveHealthCheck: &loadbalancer.ActiveHealthCheck{
308+
HealthyThreshold: utils.Ptr(int64(1)),
309+
Interval: utils.Ptr("2s"),
310+
IntervalJitter: utils.Ptr("3s"),
311+
Timeout: utils.Ptr("4s"),
312+
UnhealthyThreshold: utils.Ptr(int64(5)),
313+
},
314+
Name: utils.Ptr("name"),
315+
TargetPort: utils.Ptr(int64(80)),
316+
Targets: &[]loadbalancer.Target{
317+
{
318+
DisplayName: utils.Ptr("display_name"),
319+
Ip: utils.Ptr("ip"),
320+
},
321+
},
322+
SessionPersistence: &loadbalancer.SessionPersistence{
323+
UseSourceIpAddress: utils.Ptr(true),
324+
},
325+
},
326+
},
327+
},
328+
true,
329+
},
187330
{
188331
"nil_model",
189332
nil,

stackit/internal/services/loadbalancer/loadbalancer_acc_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var resourceMaxConfig string
3030

3131
var testConfigVarsMin = config.Variables{
3232
"project_id": config.StringVariable(testutil.ProjectId),
33+
"plan_id": config.StringVariable("p10"),
3334
"network_name": config.StringVariable(fmt.Sprintf("tf-acc-n%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
3435
"server_name": config.StringVariable(fmt.Sprintf("tf-acc-s%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
3536
"loadbalancer_name": config.StringVariable(fmt.Sprintf("tf-acc-l%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
@@ -47,6 +48,7 @@ var testConfigVarsMin = config.Variables{
4748

4849
var testConfigVarsMax = config.Variables{
4950
"project_id": config.StringVariable(testutil.ProjectId),
51+
"plan_id": config.StringVariable("p10"),
5052
"network_name": config.StringVariable(fmt.Sprintf("tf-acc-n%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
5153
"server_name": config.StringVariable(fmt.Sprintf("tf-acc-s%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
5254
"loadbalancer_name": config.StringVariable(fmt.Sprintf("tf-acc-l%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
@@ -146,6 +148,7 @@ func TestAccLoadBalancerResourceMin(t *testing.T) {
146148
// Load balancer instance
147149
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
148150
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "name", testutil.ConvertConfigVariable(testConfigVarsMin["loadbalancer_name"])),
151+
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "plan_id", testutil.ConvertConfigVariable(testConfigVarsMin["plan_id"])),
149152
resource.TestCheckResourceAttrPair(
150153
"data.stackit_loadbalancer.loadbalancer", "project_id",
151154
"stackit_loadbalancer.loadbalancer", "project_id",
@@ -218,6 +221,7 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
218221
// Load balancer instance resource
219222
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])),
220223
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "name", testutil.ConvertConfigVariable(testConfigVarsMax["loadbalancer_name"])),
224+
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "plan_id", testutil.ConvertConfigVariable(testConfigVarsMax["plan_id"])),
221225
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.name", testutil.ConvertConfigVariable(testConfigVarsMax["target_pool_name"])),
222226
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.target_port", testutil.ConvertConfigVariable(testConfigVarsMax["target_port"])),
223227
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.targets.0.display_name", testutil.ConvertConfigVariable(testConfigVarsMax["target_display_name"])),
@@ -278,6 +282,7 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
278282
// Load balancer instance
279283
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])),
280284
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "name", testutil.ConvertConfigVariable(testConfigVarsMax["loadbalancer_name"])),
285+
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "plan_id", testutil.ConvertConfigVariable(testConfigVarsMax["plan_id"])),
281286
resource.TestCheckResourceAttrPair(
282287
"data.stackit_loadbalancer.loadbalancer", "project_id",
283288
"stackit_loadbalancer.loadbalancer", "project_id",

stackit/internal/services/loadbalancer/testfiles/resource-max.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ variable "network_name" {}
44
variable "server_name" {}
55

66
variable "loadbalancer_name" {}
7+
variable "plan_id" {}
78
variable "target_pool_name" {}
89
variable "target_port" {}
910
variable "target_display_name" {}
@@ -73,6 +74,7 @@ resource "stackit_server" "server" {
7374
resource "stackit_loadbalancer" "loadbalancer" {
7475
project_id = var.project_id
7576
name = var.loadbalancer_name
77+
plan_id = var.plan_id
7678
target_pools = [
7779
{
7880
name = var.target_pool_name

0 commit comments

Comments
 (0)