terraform-provider-stackitp.../stackit/internal/services/loadbalancer/loadbalancer/resource_test.go
Christian Hamm ab232d6cb7
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>
2025-07-15 16:09:42 +02:00

919 lines
32 KiB
Go

package loadbalancer
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
)
const (
testExternalAddress = "95.46.74.109"
)
func TestToCreatePayload(t *testing.T) {
tests := []struct {
description string
input *Model
expected *loadbalancer.CreateLoadBalancerPayload
isValid bool
}{
{
"default_values_ok",
&Model{},
&loadbalancer.CreateLoadBalancerPayload{
ExternalAddress: nil,
Listeners: nil,
Name: nil,
Networks: nil,
Options: &loadbalancer.LoadBalancerOptions{
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
AllowedSourceRanges: nil,
},
PrivateNetworkOnly: nil,
Observability: &loadbalancer.LoadbalancerOptionObservability{},
},
TargetPools: nil,
},
true,
},
{
"simple_values_ok",
&Model{
ExternalAddress: types.StringValue("external_address"),
Listeners: types.ListValueMust(types.ObjectType{AttrTypes: listenerTypes}, []attr.Value{
types.ObjectValueMust(listenerTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"port": types.Int64Value(80),
"protocol": types.StringValue(string(loadbalancer.LISTENERPROTOCOL_TCP)),
"server_name_indicators": types.ListValueMust(types.ObjectType{AttrTypes: serverNameIndicatorTypes}, []attr.Value{
types.ObjectValueMust(
serverNameIndicatorTypes,
map[string]attr.Value{
"name": types.StringValue("domain.com"),
},
),
},
),
"target_pool": types.StringValue("target_pool"),
}),
}),
Name: types.StringValue("name"),
Networks: types.ListValueMust(types.ObjectType{AttrTypes: networkTypes}, []attr.Value{
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id_2"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
}),
Options: types.ObjectValueMust(
optionsTypes,
map[string]attr.Value{
"acl": types.SetValueMust(
types.StringType,
[]attr.Value{types.StringValue("cidr")}),
"private_network_only": types.BoolValue(true),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("logs-credentials_ref"),
"push_url": types.StringValue("logs-push_url"),
}),
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("metrics-credentials_ref"),
"push_url": types.StringValue("metrics-push_url"),
}),
}),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
types.ObjectValueMust(targetPoolTypes, map[string]attr.Value{
"active_health_check": types.ObjectValueMust(activeHealthCheckTypes, map[string]attr.Value{
"healthy_threshold": types.Int64Value(1),
"interval": types.StringValue("2s"),
"interval_jitter": types.StringValue("3s"),
"timeout": types.StringValue("4s"),
"unhealthy_threshold": types.Int64Value(5),
}),
"name": types.StringValue("name"),
"target_port": types.Int64Value(80),
"targets": types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
types.ObjectValueMust(targetTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"ip": types.StringValue("ip"),
}),
}),
"session_persistence": types.ObjectValueMust(sessionPersistenceTypes, map[string]attr.Value{
"use_source_ip_address": types.BoolValue(true),
}),
}),
}),
},
&loadbalancer.CreateLoadBalancerPayload{
ExternalAddress: utils.Ptr("external_address"),
Listeners: &[]loadbalancer.Listener{
{
DisplayName: utils.Ptr("display_name"),
Port: utils.Ptr(int64(80)),
Protocol: loadbalancer.LISTENERPROTOCOL_TCP.Ptr(),
ServerNameIndicators: &[]loadbalancer.ServerNameIndicator{
{
Name: utils.Ptr("domain.com"),
},
},
TargetPool: utils.Ptr("target_pool"),
},
},
Name: utils.Ptr("name"),
Networks: &[]loadbalancer.Network{
{
NetworkId: utils.Ptr("network_id"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
{
NetworkId: utils.Ptr("network_id_2"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
},
Options: &loadbalancer.LoadBalancerOptions{
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
AllowedSourceRanges: &[]string{"cidr"},
},
PrivateNetworkOnly: utils.Ptr(true),
Observability: &loadbalancer.LoadbalancerOptionObservability{
Logs: &loadbalancer.LoadbalancerOptionLogs{
CredentialsRef: utils.Ptr("logs-credentials_ref"),
PushUrl: utils.Ptr("logs-push_url"),
},
Metrics: &loadbalancer.LoadbalancerOptionMetrics{
CredentialsRef: utils.Ptr("metrics-credentials_ref"),
PushUrl: utils.Ptr("metrics-push_url"),
},
},
},
TargetPools: &[]loadbalancer.TargetPool{
{
ActiveHealthCheck: &loadbalancer.ActiveHealthCheck{
HealthyThreshold: utils.Ptr(int64(1)),
Interval: utils.Ptr("2s"),
IntervalJitter: utils.Ptr("3s"),
Timeout: utils.Ptr("4s"),
UnhealthyThreshold: utils.Ptr(int64(5)),
},
Name: utils.Ptr("name"),
TargetPort: utils.Ptr(int64(80)),
Targets: &[]loadbalancer.Target{
{
DisplayName: utils.Ptr("display_name"),
Ip: utils.Ptr("ip"),
},
},
SessionPersistence: &loadbalancer.SessionPersistence{
UseSourceIpAddress: utils.Ptr(true),
},
},
},
},
true,
},
{
"service_plan_ok",
&Model{
PlanId: types.StringValue("p10"),
ExternalAddress: types.StringValue("external_address"),
Listeners: types.ListValueMust(types.ObjectType{AttrTypes: listenerTypes}, []attr.Value{
types.ObjectValueMust(listenerTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"port": types.Int64Value(80),
"protocol": types.StringValue(string(loadbalancer.LISTENERPROTOCOL_TCP)),
"server_name_indicators": types.ListValueMust(types.ObjectType{AttrTypes: serverNameIndicatorTypes}, []attr.Value{
types.ObjectValueMust(
serverNameIndicatorTypes,
map[string]attr.Value{
"name": types.StringValue("domain.com"),
},
),
},
),
"target_pool": types.StringValue("target_pool"),
}),
}),
Name: types.StringValue("name"),
Networks: types.ListValueMust(types.ObjectType{AttrTypes: networkTypes}, []attr.Value{
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id_2"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
}),
Options: types.ObjectValueMust(
optionsTypes,
map[string]attr.Value{
"acl": types.SetValueMust(
types.StringType,
[]attr.Value{types.StringValue("cidr")}),
"private_network_only": types.BoolValue(true),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("logs-credentials_ref"),
"push_url": types.StringValue("logs-push_url"),
}),
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("metrics-credentials_ref"),
"push_url": types.StringValue("metrics-push_url"),
}),
}),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
types.ObjectValueMust(targetPoolTypes, map[string]attr.Value{
"active_health_check": types.ObjectValueMust(activeHealthCheckTypes, map[string]attr.Value{
"healthy_threshold": types.Int64Value(1),
"interval": types.StringValue("2s"),
"interval_jitter": types.StringValue("3s"),
"timeout": types.StringValue("4s"),
"unhealthy_threshold": types.Int64Value(5),
}),
"name": types.StringValue("name"),
"target_port": types.Int64Value(80),
"targets": types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
types.ObjectValueMust(targetTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"ip": types.StringValue("ip"),
}),
}),
"session_persistence": types.ObjectValueMust(sessionPersistenceTypes, map[string]attr.Value{
"use_source_ip_address": types.BoolValue(true),
}),
}),
}),
},
&loadbalancer.CreateLoadBalancerPayload{
PlanId: utils.Ptr("p10"),
ExternalAddress: utils.Ptr("external_address"),
Listeners: &[]loadbalancer.Listener{
{
DisplayName: utils.Ptr("display_name"),
Port: utils.Ptr(int64(80)),
Protocol: loadbalancer.LISTENERPROTOCOL_TCP.Ptr(),
ServerNameIndicators: &[]loadbalancer.ServerNameIndicator{
{
Name: utils.Ptr("domain.com"),
},
},
TargetPool: utils.Ptr("target_pool"),
},
},
Name: utils.Ptr("name"),
Networks: &[]loadbalancer.Network{
{
NetworkId: utils.Ptr("network_id"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
{
NetworkId: utils.Ptr("network_id_2"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
},
Options: &loadbalancer.LoadBalancerOptions{
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
AllowedSourceRanges: &[]string{"cidr"},
},
PrivateNetworkOnly: utils.Ptr(true),
Observability: &loadbalancer.LoadbalancerOptionObservability{
Logs: &loadbalancer.LoadbalancerOptionLogs{
CredentialsRef: utils.Ptr("logs-credentials_ref"),
PushUrl: utils.Ptr("logs-push_url"),
},
Metrics: &loadbalancer.LoadbalancerOptionMetrics{
CredentialsRef: utils.Ptr("metrics-credentials_ref"),
PushUrl: utils.Ptr("metrics-push_url"),
},
},
},
TargetPools: &[]loadbalancer.TargetPool{
{
ActiveHealthCheck: &loadbalancer.ActiveHealthCheck{
HealthyThreshold: utils.Ptr(int64(1)),
Interval: utils.Ptr("2s"),
IntervalJitter: utils.Ptr("3s"),
Timeout: utils.Ptr("4s"),
UnhealthyThreshold: utils.Ptr(int64(5)),
},
Name: utils.Ptr("name"),
TargetPort: utils.Ptr(int64(80)),
Targets: &[]loadbalancer.Target{
{
DisplayName: utils.Ptr("display_name"),
Ip: utils.Ptr("ip"),
},
},
SessionPersistence: &loadbalancer.SessionPersistence{
UseSourceIpAddress: utils.Ptr(true),
},
},
},
},
true,
},
{
"nil_model",
nil,
nil,
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
output, err := toCreatePayload(context.Background(), tt.input)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(output, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
func TestToTargetPoolUpdatePayload(t *testing.T) {
tests := []struct {
description string
input *targetPool
expected *loadbalancer.UpdateTargetPoolPayload
isValid bool
}{
{
"default_values_ok",
&targetPool{},
&loadbalancer.UpdateTargetPoolPayload{},
true,
},
{
"simple_values_ok",
&targetPool{
ActiveHealthCheck: types.ObjectValueMust(activeHealthCheckTypes, map[string]attr.Value{
"healthy_threshold": types.Int64Value(1),
"interval": types.StringValue("2s"),
"interval_jitter": types.StringValue("3s"),
"timeout": types.StringValue("4s"),
"unhealthy_threshold": types.Int64Value(5),
}),
Name: types.StringValue("name"),
TargetPort: types.Int64Value(80),
Targets: types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
types.ObjectValueMust(targetTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"ip": types.StringValue("ip"),
}),
}),
SessionPersistence: types.ObjectValueMust(sessionPersistenceTypes, map[string]attr.Value{
"use_source_ip_address": types.BoolValue(false),
}),
},
&loadbalancer.UpdateTargetPoolPayload{
ActiveHealthCheck: &loadbalancer.ActiveHealthCheck{
HealthyThreshold: utils.Ptr(int64(1)),
Interval: utils.Ptr("2s"),
IntervalJitter: utils.Ptr("3s"),
Timeout: utils.Ptr("4s"),
UnhealthyThreshold: utils.Ptr(int64(5)),
},
Name: utils.Ptr("name"),
TargetPort: utils.Ptr(int64(80)),
Targets: &[]loadbalancer.Target{
{
DisplayName: utils.Ptr("display_name"),
Ip: utils.Ptr("ip"),
},
},
SessionPersistence: &loadbalancer.SessionPersistence{
UseSourceIpAddress: utils.Ptr(false),
},
},
true,
},
{
"nil_target_pool",
nil,
nil,
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
output, err := toTargetPoolUpdatePayload(context.Background(), tt.input)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(output, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
func TestMapFields(t *testing.T) {
const testRegion = "eu01"
id := fmt.Sprintf("%s,%s,%s", "pid", testRegion, "name")
tests := []struct {
description string
input *loadbalancer.LoadBalancer
modelPrivateNetworkOnly *bool
region string
expected *Model
isValid bool
}{
{
"default_values_ok",
&loadbalancer.LoadBalancer{
ExternalAddress: nil,
Listeners: nil,
Name: utils.Ptr("name"),
Networks: nil,
Options: &loadbalancer.LoadBalancerOptions{
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
AllowedSourceRanges: nil,
},
PrivateNetworkOnly: nil,
Observability: &loadbalancer.LoadbalancerOptionObservability{
Logs: &loadbalancer.LoadbalancerOptionLogs{},
Metrics: &loadbalancer.LoadbalancerOptionMetrics{},
},
},
TargetPools: nil,
},
nil,
testRegion,
&Model{
Id: types.StringValue(id),
ProjectId: types.StringValue("pid"),
ExternalAddress: types.StringNull(),
Listeners: types.ListNull(types.ObjectType{AttrTypes: listenerTypes}),
Name: types.StringValue("name"),
Networks: types.ListNull(types.ObjectType{AttrTypes: networkTypes}),
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
"acl": types.SetNull(types.StringType),
"private_network_only": types.BoolNull(),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringNull(),
"push_url": types.StringNull(),
}),
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringNull(),
"push_url": types.StringNull(),
}),
}),
}),
PrivateAddress: types.StringNull(),
TargetPools: types.ListNull(types.ObjectType{AttrTypes: targetPoolTypes}),
Region: types.StringValue(testRegion),
},
true,
},
{
"simple_values_ok",
&loadbalancer.LoadBalancer{
ExternalAddress: utils.Ptr("external_address"),
Listeners: utils.Ptr([]loadbalancer.Listener{
{
DisplayName: utils.Ptr("display_name"),
Port: utils.Ptr(int64(80)),
Protocol: loadbalancer.LISTENERPROTOCOL_TCP.Ptr(),
ServerNameIndicators: &[]loadbalancer.ServerNameIndicator{
{
Name: utils.Ptr("domain.com"),
},
},
TargetPool: utils.Ptr("target_pool"),
},
}),
Name: utils.Ptr("name"),
Networks: utils.Ptr([]loadbalancer.Network{
{
NetworkId: utils.Ptr("network_id"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
{
NetworkId: utils.Ptr("network_id_2"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
}),
Options: utils.Ptr(loadbalancer.LoadBalancerOptions{
PrivateNetworkOnly: utils.Ptr(true),
Observability: &loadbalancer.LoadbalancerOptionObservability{
Logs: &loadbalancer.LoadbalancerOptionLogs{
CredentialsRef: utils.Ptr("logs_credentials_ref"),
PushUrl: utils.Ptr("logs_push_url"),
},
Metrics: &loadbalancer.LoadbalancerOptionMetrics{
CredentialsRef: utils.Ptr("metrics_credentials_ref"),
PushUrl: utils.Ptr("metrics_push_url"),
},
},
}),
TargetPools: utils.Ptr([]loadbalancer.TargetPool{
{
ActiveHealthCheck: utils.Ptr(loadbalancer.ActiveHealthCheck{
HealthyThreshold: utils.Ptr(int64(1)),
Interval: utils.Ptr("2s"),
IntervalJitter: utils.Ptr("3s"),
Timeout: utils.Ptr("4s"),
UnhealthyThreshold: utils.Ptr(int64(5)),
}),
Name: utils.Ptr("name"),
TargetPort: utils.Ptr(int64(80)),
Targets: utils.Ptr([]loadbalancer.Target{
{
DisplayName: utils.Ptr("display_name"),
Ip: utils.Ptr("ip"),
},
}),
SessionPersistence: utils.Ptr(loadbalancer.SessionPersistence{
UseSourceIpAddress: utils.Ptr(true),
}),
},
}),
},
nil,
testRegion,
&Model{
Id: types.StringValue(id),
ProjectId: types.StringValue("pid"),
ExternalAddress: types.StringValue("external_address"),
Listeners: types.ListValueMust(types.ObjectType{AttrTypes: listenerTypes}, []attr.Value{
types.ObjectValueMust(listenerTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"port": types.Int64Value(80),
"protocol": types.StringValue(string(loadbalancer.LISTENERPROTOCOL_TCP)),
"server_name_indicators": types.ListValueMust(types.ObjectType{AttrTypes: serverNameIndicatorTypes}, []attr.Value{
types.ObjectValueMust(
serverNameIndicatorTypes,
map[string]attr.Value{
"name": types.StringValue("domain.com"),
},
),
},
),
"target_pool": types.StringValue("target_pool"),
}),
}),
Name: types.StringValue("name"),
Networks: types.ListValueMust(types.ObjectType{AttrTypes: networkTypes}, []attr.Value{
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id_2"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
}),
Options: types.ObjectValueMust(
optionsTypes,
map[string]attr.Value{
"private_network_only": types.BoolValue(true),
"acl": types.SetNull(types.StringType),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("logs_credentials_ref"),
"push_url": types.StringValue("logs_push_url"),
}),
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("metrics_credentials_ref"),
"push_url": types.StringValue("metrics_push_url"),
}),
}),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
types.ObjectValueMust(targetPoolTypes, map[string]attr.Value{
"active_health_check": types.ObjectValueMust(activeHealthCheckTypes, map[string]attr.Value{
"healthy_threshold": types.Int64Value(1),
"interval": types.StringValue("2s"),
"interval_jitter": types.StringValue("3s"),
"timeout": types.StringValue("4s"),
"unhealthy_threshold": types.Int64Value(5),
}),
"name": types.StringValue("name"),
"target_port": types.Int64Value(80),
"targets": types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
types.ObjectValueMust(targetTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"ip": types.StringValue("ip"),
}),
}),
"session_persistence": types.ObjectValueMust(sessionPersistenceTypes, map[string]attr.Value{
"use_source_ip_address": types.BoolValue(true),
}),
}),
}),
Region: types.StringValue(testRegion),
},
true,
},
{
"simple_values_ok_with_null_private_network_only_response",
&loadbalancer.LoadBalancer{
ExternalAddress: utils.Ptr("external_address"),
Listeners: utils.Ptr([]loadbalancer.Listener{
{
DisplayName: utils.Ptr("display_name"),
Port: utils.Ptr(int64(80)),
Protocol: loadbalancer.LISTENERPROTOCOL_TCP.Ptr(),
ServerNameIndicators: &[]loadbalancer.ServerNameIndicator{
{
Name: utils.Ptr("domain.com"),
},
},
TargetPool: utils.Ptr("target_pool"),
},
}),
Name: utils.Ptr("name"),
Networks: utils.Ptr([]loadbalancer.Network{
{
NetworkId: utils.Ptr("network_id"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
{
NetworkId: utils.Ptr("network_id_2"),
Role: loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS.Ptr(),
},
}),
Options: utils.Ptr(loadbalancer.LoadBalancerOptions{
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
AllowedSourceRanges: utils.Ptr([]string{"cidr"}),
},
PrivateNetworkOnly: nil, // API sets this to nil if it's false in the request
Observability: &loadbalancer.LoadbalancerOptionObservability{
Logs: &loadbalancer.LoadbalancerOptionLogs{
CredentialsRef: utils.Ptr("logs_credentials_ref"),
PushUrl: utils.Ptr("logs_push_url"),
},
Metrics: &loadbalancer.LoadbalancerOptionMetrics{
CredentialsRef: utils.Ptr("metrics_credentials_ref"),
PushUrl: utils.Ptr("metrics_push_url"),
},
},
}),
TargetPools: utils.Ptr([]loadbalancer.TargetPool{
{
ActiveHealthCheck: utils.Ptr(loadbalancer.ActiveHealthCheck{
HealthyThreshold: utils.Ptr(int64(1)),
Interval: utils.Ptr("2s"),
IntervalJitter: utils.Ptr("3s"),
Timeout: utils.Ptr("4s"),
UnhealthyThreshold: utils.Ptr(int64(5)),
}),
Name: utils.Ptr("name"),
TargetPort: utils.Ptr(int64(80)),
Targets: utils.Ptr([]loadbalancer.Target{
{
DisplayName: utils.Ptr("display_name"),
Ip: utils.Ptr("ip"),
},
}),
SessionPersistence: utils.Ptr(loadbalancer.SessionPersistence{
UseSourceIpAddress: utils.Ptr(true),
}),
},
}),
},
utils.Ptr(false),
testRegion,
&Model{
Id: types.StringValue(id),
ProjectId: types.StringValue("pid"),
ExternalAddress: types.StringValue("external_address"),
Listeners: types.ListValueMust(types.ObjectType{AttrTypes: listenerTypes}, []attr.Value{
types.ObjectValueMust(listenerTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"port": types.Int64Value(80),
"protocol": types.StringValue(string(loadbalancer.LISTENERPROTOCOL_TCP)),
"server_name_indicators": types.ListValueMust(types.ObjectType{AttrTypes: serverNameIndicatorTypes}, []attr.Value{
types.ObjectValueMust(
serverNameIndicatorTypes,
map[string]attr.Value{
"name": types.StringValue("domain.com"),
},
),
},
),
"target_pool": types.StringValue("target_pool"),
}),
}),
Name: types.StringValue("name"),
Networks: types.ListValueMust(types.ObjectType{AttrTypes: networkTypes}, []attr.Value{
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
types.ObjectValueMust(networkTypes, map[string]attr.Value{
"network_id": types.StringValue("network_id_2"),
"role": types.StringValue(string(loadbalancer.NETWORKROLE_LISTENERS_AND_TARGETS)),
}),
}),
Options: types.ObjectValueMust(
optionsTypes,
map[string]attr.Value{
"acl": types.SetValueMust(
types.StringType,
[]attr.Value{types.StringValue("cidr")}),
"private_network_only": types.BoolValue(false),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("logs_credentials_ref"),
"push_url": types.StringValue("logs_push_url"),
}),
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringValue("metrics_credentials_ref"),
"push_url": types.StringValue("metrics_push_url"),
}),
}),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
types.ObjectValueMust(targetPoolTypes, map[string]attr.Value{
"active_health_check": types.ObjectValueMust(activeHealthCheckTypes, map[string]attr.Value{
"healthy_threshold": types.Int64Value(1),
"interval": types.StringValue("2s"),
"interval_jitter": types.StringValue("3s"),
"timeout": types.StringValue("4s"),
"unhealthy_threshold": types.Int64Value(5),
}),
"name": types.StringValue("name"),
"target_port": types.Int64Value(80),
"targets": types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
types.ObjectValueMust(targetTypes, map[string]attr.Value{
"display_name": types.StringValue("display_name"),
"ip": types.StringValue("ip"),
}),
}),
"session_persistence": types.ObjectValueMust(sessionPersistenceTypes, map[string]attr.Value{
"use_source_ip_address": types.BoolValue(true),
}),
}),
}),
Region: types.StringValue(testRegion),
},
true,
},
{
"nil_response",
nil,
nil,
testRegion,
&Model{},
false,
},
{
"no_name",
&loadbalancer.LoadBalancer{},
nil,
testRegion,
&Model{},
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
model := &Model{
ProjectId: tt.expected.ProjectId,
}
if tt.modelPrivateNetworkOnly != nil {
model.Options = types.ObjectValueMust(optionsTypes, map[string]attr.Value{
"private_network_only": types.BoolValue(*tt.modelPrivateNetworkOnly),
"acl": types.SetNull(types.StringType),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringNull(),
"push_url": types.StringNull(),
}),
"metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
"credentials_ref": types.StringNull(),
"push_url": types.StringNull(),
}),
}),
})
}
err := mapFields(context.Background(), tt.input, model, tt.region)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(model, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
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)
}
})
}
}