fix(loadbalancer): set external_address as optional (#854)
* fix: set external_address for lb to optional * add unit tests --------- Co-authored-by: Ruben Hoenle <Ruben.Hoenle@stackit.cloud>
This commit is contained in:
parent
29f9a01633
commit
aaf29e4c19
3 changed files with 126 additions and 2 deletions
|
|
@ -119,7 +119,6 @@ resource "stackit_loadbalancer" "example" {
|
||||||
|
|
||||||
### Required
|
### Required
|
||||||
|
|
||||||
- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed.
|
|
||||||
- `listeners` (Attributes List) List of all listeners which will accept traffic. Limited to 20. (see [below for nested schema](#nestedatt--listeners))
|
- `listeners` (Attributes List) List of all listeners which will accept traffic. Limited to 20. (see [below for nested schema](#nestedatt--listeners))
|
||||||
- `name` (String) Load balancer name.
|
- `name` (String) Load balancer name.
|
||||||
- `networks` (Attributes List) List of networks that listeners and targets reside in. (see [below for nested schema](#nestedatt--networks))
|
- `networks` (Attributes List) List of networks that listeners and targets reside in. (see [below for nested schema](#nestedatt--networks))
|
||||||
|
|
@ -128,6 +127,7 @@ resource "stackit_loadbalancer" "example" {
|
||||||
|
|
||||||
### Optional
|
### Optional
|
||||||
|
|
||||||
|
- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed.
|
||||||
- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options))
|
- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options))
|
||||||
- `region` (String) The resource region. If not defined, the provider region is used.
|
- `region` (String) The resource region. If not defined, the provider region is used.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||||
|
|
@ -237,6 +238,43 @@ func (r *loadBalancerResource) ModifyPlan(ctx context.Context, req resource.Modi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigValidators validates the resource configuration
|
||||||
|
func (r *loadBalancerResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
|
||||||
|
var model Model
|
||||||
|
resp.Diagnostics.Append(req.Config.Get(ctx, &model)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// validation is done in extracted func so it's easier to unit-test it
|
||||||
|
validateConfig(ctx, &resp.Diagnostics, &model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateConfig(ctx context.Context, diags *diag.Diagnostics, model *Model) {
|
||||||
|
externalAddressIsSet := !model.ExternalAddress.IsNull()
|
||||||
|
|
||||||
|
lbOptions, err := toOptionsPayload(ctx, model)
|
||||||
|
if err != nil || lbOptions == nil {
|
||||||
|
// private_network_only is not set and external_address is not set
|
||||||
|
if !externalAddressIsSet {
|
||||||
|
core.LogAndAddError(ctx, diags, "Error configuring load balancer", fmt.Sprintf("You need to provide either the `options.private_network_only = true` or `external_address` field. %v", err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lbOptions.PrivateNetworkOnly == nil || !*lbOptions.PrivateNetworkOnly {
|
||||||
|
// private_network_only is not set or false and external_address is not set
|
||||||
|
if !externalAddressIsSet {
|
||||||
|
core.LogAndAddError(ctx, diags, "Error configuring load balancer", "You need to provide either the `options.private_network_only = true` or `external_address` field.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both are set
|
||||||
|
if *lbOptions.PrivateNetworkOnly && externalAddressIsSet {
|
||||||
|
core.LogAndAddError(ctx, diags, "Error configuring load balancer", "You need to provide either the `options.private_network_only = true` or `external_address` field.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure adds the provider configured client to the resource.
|
// Configure adds the provider configured client to the resource.
|
||||||
func (r *loadBalancerResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
func (r *loadBalancerResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
@ -327,7 +365,7 @@ The example below creates the supporting infrastructure using the STACKIT Terraf
|
||||||
},
|
},
|
||||||
"external_address": schema.StringAttribute{
|
"external_address": schema.StringAttribute{
|
||||||
Description: descriptions["external_address"],
|
Description: descriptions["external_address"],
|
||||||
Required: true,
|
Optional: true,
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.String{
|
||||||
stringplanmodifier.RequiresReplace(),
|
stringplanmodifier.RequiresReplace(),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,16 @@ import (
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
|
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testExternalAddress = "95.46.74.109"
|
||||||
|
)
|
||||||
|
|
||||||
func TestToCreatePayload(t *testing.T) {
|
func TestToCreatePayload(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
description string
|
description string
|
||||||
|
|
@ -688,3 +693,84 @@ func TestMapFields(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_validateConfig(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
ExternalAddress *string
|
||||||
|
PrivateNetworkOnly *bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "happy case 1: private_network_only is not set and external_address is set",
|
||||||
|
args: args{
|
||||||
|
ExternalAddress: utils.Ptr(testExternalAddress),
|
||||||
|
PrivateNetworkOnly: nil,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy case 2: private_network_only is set to false and external_address is set",
|
||||||
|
args: args{
|
||||||
|
ExternalAddress: utils.Ptr(testExternalAddress),
|
||||||
|
PrivateNetworkOnly: utils.Ptr(false),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy case 3: private_network_only is set to true and external_address is not set",
|
||||||
|
args: args{
|
||||||
|
ExternalAddress: nil,
|
||||||
|
PrivateNetworkOnly: utils.Ptr(true),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error case 1: private_network_only and external_address are set",
|
||||||
|
args: args{
|
||||||
|
ExternalAddress: utils.Ptr(testExternalAddress),
|
||||||
|
PrivateNetworkOnly: utils.Ptr(true),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error case 2: private_network_only is not set and external_address is not set",
|
||||||
|
args: args{
|
||||||
|
ExternalAddress: nil,
|
||||||
|
PrivateNetworkOnly: nil,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error case 3: private_network_only is set to false and external_address is not set",
|
||||||
|
args: args{
|
||||||
|
ExternalAddress: nil,
|
||||||
|
PrivateNetworkOnly: utils.Ptr(false),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
diags := diag.Diagnostics{}
|
||||||
|
model := &Model{
|
||||||
|
ExternalAddress: types.StringPointerValue(tt.args.ExternalAddress),
|
||||||
|
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
|
||||||
|
"acl": types.SetNull(types.StringType),
|
||||||
|
"observability": types.ObjectNull(observabilityTypes),
|
||||||
|
"private_network_only": types.BoolPointerValue(tt.args.PrivateNetworkOnly),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
validateConfig(ctx, &diags, model)
|
||||||
|
|
||||||
|
if diags.HasError() != tt.wantErr {
|
||||||
|
t.Errorf("validateConfig() = %v, want %v", diags.HasError(), tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue