feat(iaas): add warning that behavior of network resource will change (#1031)
relates to STACKITTPR-366 * feat(iaas): add warning that behavior of network resource will change * fix: changed payload for ipv6_nameservers * if is unset / null: ipv6_nameservers will not be sent * if set list / empty list: ipv6_nameserver will be sent with the set list / empty list
This commit is contained in:
parent
10eced46c7
commit
4552b14edd
6 changed files with 372 additions and 47 deletions
|
|
@ -4,11 +4,17 @@ page_title: "stackit_network Resource - stackit"
|
|||
subcategory: ""
|
||||
description: |-
|
||||
Network resource schema. Must have a region specified in the provider configuration.
|
||||
~> Behavior of not configured ipv4_nameservers will change from January 2026. When ipv4_nameservers is not set, it will be set to the network area's default_nameservers.
|
||||
To prevent any nameserver configuration, the ipv4_nameservers attribute should be explicitly set to an empty list [].
|
||||
In cases where ipv4_nameservers are defined within the resource, the existing behavior will remain unchanged.
|
||||
---
|
||||
|
||||
# stackit_network (Resource)
|
||||
|
||||
Network resource schema. Must have a `region` specified in the provider configuration.
|
||||
~> Behavior of not configured `ipv4_nameservers` will change from January 2026. When `ipv4_nameservers` is not set, it will be set to the network area's `default_nameservers`.
|
||||
To prevent any nameserver configuration, the `ipv4_nameservers` attribute should be explicitly set to an empty list `[]`.
|
||||
In cases where `ipv4_nameservers` are defined within the resource, the existing behavior will remain unchanged.
|
||||
|
||||
## Example Usage
|
||||
|
||||
|
|
@ -68,7 +74,7 @@ import {
|
|||
- `ipv6_prefix` (String) The IPv6 prefix of the network (CIDR).
|
||||
- `ipv6_prefix_length` (Number) The IPv6 prefix length of the network.
|
||||
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container
|
||||
- `nameservers` (List of String, Deprecated) The nameservers of the network. This field is deprecated and will be removed soon, use `ipv4_nameservers` to configure the nameservers for IPv4.
|
||||
- `nameservers` (List of String, Deprecated) The nameservers of the network. This field is deprecated and will be removed in January 2026, use `ipv4_nameservers` to configure the nameservers for IPv4.
|
||||
- `no_ipv4_gateway` (Boolean) If set to `true`, the network doesn't have a gateway.
|
||||
- `no_ipv6_gateway` (Boolean) If set to `true`, the network doesn't have a gateway.
|
||||
- `region` (String) Can only be used when experimental "network" is set.
|
||||
|
|
@ -83,5 +89,5 @@ The ID of the routing table associated with the network.
|
|||
- `ipv4_prefixes` (List of String) The IPv4 prefixes of the network.
|
||||
- `ipv6_prefixes` (List of String) The IPv6 prefixes of the network.
|
||||
- `network_id` (String) The network ID.
|
||||
- `prefixes` (List of String, Deprecated) The prefixes of the network. This field is deprecated and will be removed soon, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.
|
||||
- `prefixes` (List of String, Deprecated) The prefixes of the network. This field is deprecated and will be removed in January 2026, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.
|
||||
- `public_ip` (String) The public IP of the network.
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ package network
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
|
|
@ -37,6 +39,13 @@ var (
|
|||
_ resource.ResourceWithImportState = &networkResource{}
|
||||
)
|
||||
|
||||
const (
|
||||
ipv4BehaviorChangeTitle = "Behavior of not configured `ipv4_nameservers` will change from January 2026"
|
||||
ipv4BehaviorChangeDescription = "When `ipv4_nameservers` is not set, it will be set to the network area's `default_nameservers`.\n" +
|
||||
"To prevent any nameserver configuration, the `ipv4_nameservers` attribute should be explicitly set to an empty list `[]`.\n" +
|
||||
"In cases where `ipv4_nameservers` are defined within the resource, the existing behavior will remain unchanged."
|
||||
)
|
||||
|
||||
// NewNetworkResource is a helper function to simplify the provider implementation.
|
||||
func NewNetworkResource() resource.Resource {
|
||||
return &networkResource{}
|
||||
|
|
@ -88,10 +97,6 @@ func (r *networkResource) Configure(ctx context.Context, req resource.ConfigureR
|
|||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
// Use the modifier to set the effective region in the current plan.
|
||||
func (r *networkResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// If the v1 api is used, it's not required to get the fallback region because it isn't used
|
||||
if !r.isExperimental {
|
||||
return
|
||||
}
|
||||
var configModel model.Model
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
|
|
@ -108,6 +113,15 @@ func (r *networkResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
|
|||
return
|
||||
}
|
||||
|
||||
// Warning should only be shown during the plan of the creation. This can be detected by checking if the ID is set.
|
||||
if utils.IsUndefined(planModel.Id) && utils.IsUndefined(planModel.IPv4Nameservers) {
|
||||
addIPv4Warning(&resp.Diagnostics)
|
||||
}
|
||||
|
||||
// If the v1 api is used, it's not required to get the fallback region because it isn't used
|
||||
if !r.isExperimental {
|
||||
return
|
||||
}
|
||||
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -171,8 +185,11 @@ func (r *networkResource) ConfigValidators(_ context.Context) []resource.ConfigV
|
|||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
description := "Network resource schema. Must have a `region` specified in the provider configuration."
|
||||
descriptionNote := fmt.Sprintf("~> %s. %s", ipv4BehaviorChangeTitle, ipv4BehaviorChangeDescription)
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Network resource schema. Must have a `region` specified in the provider configuration.",
|
||||
MarkdownDescription: fmt.Sprintf("%s\n%s", description, descriptionNote),
|
||||
Description: description,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`network_id`\".",
|
||||
|
|
@ -212,7 +229,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
},
|
||||
},
|
||||
"nameservers": schema.ListAttribute{
|
||||
Description: "The nameservers of the network. This field is deprecated and will be removed soon, use `ipv4_nameservers` to configure the nameservers for IPv4.",
|
||||
Description: "The nameservers of the network. This field is deprecated and will be removed in January 2026, use `ipv4_nameservers` to configure the nameservers for IPv4.",
|
||||
DeprecationMessage: "Use `ipv4_nameservers` to configure the nameservers for IPv4.",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
|
|
@ -259,7 +276,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
},
|
||||
},
|
||||
"prefixes": schema.ListAttribute{
|
||||
Description: "The prefixes of the network. This field is deprecated and will be removed soon, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.",
|
||||
Description: "The prefixes of the network. This field is deprecated and will be removed in January 2026, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.",
|
||||
DeprecationMessage: "Use `ipv4_prefixes` to read the prefixes of the IPv4 networks.",
|
||||
Computed: true,
|
||||
ElementType: types.StringType,
|
||||
|
|
@ -299,6 +316,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
"ipv6_prefix": schema.StringAttribute{
|
||||
Description: "The IPv6 prefix of the network (CIDR).",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
validate.CIDR(),
|
||||
},
|
||||
|
|
@ -309,6 +327,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
"ipv6_prefix_length": schema.Int64Attribute{
|
||||
Description: "The IPv6 prefix length of the network.",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"ipv6_prefixes": schema.ListAttribute{
|
||||
Description: "The IPv6 prefixes of the network.",
|
||||
|
|
@ -366,6 +385,18 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
|
||||
// Create creates the resource and sets the initial Terraform state.
|
||||
func (r *networkResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var planModel model.Model
|
||||
diags := req.Plan.Get(ctx, &planModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
// When IPv4Nameserver is not set, print warning that the behavior of ipv4_nameservers will change
|
||||
if utils.IsUndefined(planModel.IPv4Nameservers) {
|
||||
addIPv4Warning(&resp.Diagnostics)
|
||||
}
|
||||
|
||||
if !r.isExperimental {
|
||||
v1network.Create(ctx, req, resp, r.client)
|
||||
} else {
|
||||
|
|
@ -409,3 +440,9 @@ func (r *networkResource) ImportState(ctx context.Context, req resource.ImportSt
|
|||
v2network.ImportState(ctx, req, resp)
|
||||
}
|
||||
}
|
||||
|
||||
func addIPv4Warning(diags *diag.Diagnostics) {
|
||||
diags.AddAttributeWarning(path.Root("ipv4_nameservers"),
|
||||
ipv4BehaviorChangeTitle,
|
||||
ipv4BehaviorChangeDescription)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,8 +322,10 @@ func mapFields(ctx context.Context, networkResp *iaas.Network, model *networkMod
|
|||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.PrefixesV6 == nil {
|
||||
if networkResp.PrefixesV6 == nil || len(*networkResp.PrefixesV6) == 0 {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv6Prefix = types.StringNull()
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.PrefixesV6
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
|
|
@ -367,21 +369,32 @@ func toCreatePayload(ctx context.Context, model *networkModel.Model) (*iaas.Crea
|
|||
}
|
||||
addressFamily := &iaas.CreateNetworkAddressFamily{}
|
||||
|
||||
modelIPv6Nameservers := []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
var modelIPv6Nameservers []string
|
||||
// Is true when IPv6Nameservers is not null or unset
|
||||
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||
// empty slice != nil slice. Empty slice will result in an empty list in the payload []. Nil slice will result in a payload without the property set
|
||||
modelIPv6Nameservers = []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !(model.IPv6Prefix.IsNull() || model.IPv6PrefixLength.IsNull() || model.IPv6Nameservers.IsNull()) {
|
||||
if !utils.IsUndefined(model.IPv6Prefix) || !utils.IsUndefined(model.IPv6PrefixLength) || (modelIPv6Nameservers != nil) {
|
||||
addressFamily.Ipv6 = &iaas.CreateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv6PrefixLength),
|
||||
}
|
||||
// IPv6 nameservers should only be set, if it contains any value. If the slice is nil, it should NOT be set.
|
||||
// Setting it to a nil slice would result in a payload, where nameservers is set to null in the json payload,
|
||||
// but it should actually be unset. Setting it to "null" will result in an error, because it's NOT nullable.
|
||||
if modelIPv6Nameservers != nil {
|
||||
addressFamily.Ipv6.Nameservers = &modelIPv6Nameservers
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||
|
|
@ -445,23 +458,34 @@ func toUpdatePayload(ctx context.Context, model, stateModel *networkModel.Model)
|
|||
}
|
||||
addressFamily := &iaas.UpdateNetworkAddressFamily{}
|
||||
|
||||
modelIPv6Nameservers := []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
var modelIPv6Nameservers []string
|
||||
// Is true when IPv6Nameservers is not null or unset
|
||||
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||
// empty slice != nil slice. Empty slice will result in an empty list in the payload []. Nil slice will result in a payload without the property set
|
||||
modelIPv6Nameservers = []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !(model.IPv6Nameservers.IsNull() || model.IPv6Nameservers.IsUnknown()) {
|
||||
addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
if !utils.IsUndefined(model.NoIPv6Gateway) || !utils.IsUndefined(model.IPv6Gateway) || modelIPv6Nameservers != nil {
|
||||
addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{}
|
||||
|
||||
// IPv6 nameservers should only be set, if it contains any value. If the slice is nil, it should NOT be set.
|
||||
// Setting it to a nil slice would result in a payload, where nameservers is set to null in the json payload,
|
||||
// but it should actually be unset. Setting it to "null" will result in an error, because it's NOT nullable.
|
||||
if modelIPv6Nameservers != nil {
|
||||
addressFamily.Ipv6.Nameservers = &modelIPv6Nameservers
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
} else if !utils.IsUndefined(model.IPv6Gateway) {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -466,6 +466,66 @@ func TestToCreatePayload(t *testing.T) {
|
|||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_null",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Value(24),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(false),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
IPv6Prefix: types.StringValue("prefix"),
|
||||
},
|
||||
&iaas.CreateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
AddressFamily: &iaas.CreateNetworkAddressFamily{
|
||||
Ipv6: &iaas.CreateNetworkIPv6Body{
|
||||
Nameservers: nil,
|
||||
PrefixLength: utils.Ptr(int64(24)),
|
||||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
Prefix: utils.Ptr("prefix"),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(false),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_empty_list",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{}),
|
||||
IPv6PrefixLength: types.Int64Value(24),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(false),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
IPv6Prefix: types.StringValue("prefix"),
|
||||
},
|
||||
&iaas.CreateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
AddressFamily: &iaas.CreateNetworkAddressFamily{
|
||||
Ipv6: &iaas.CreateNetworkIPv6Body{
|
||||
Nameservers: utils.Ptr([]string{}),
|
||||
PrefixLength: utils.Ptr(int64(24)),
|
||||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
Prefix: utils.Ptr("prefix"),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(false),
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
|
|
@ -670,6 +730,66 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_null",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaas.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
AddressFamily: &iaas.UpdateNetworkAddressFamily{
|
||||
Ipv6: &iaas.UpdateNetworkIPv6Body{
|
||||
Nameservers: nil,
|
||||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_empty_list",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaas.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
AddressFamily: &iaas.UpdateNetworkAddressFamily{
|
||||
Ipv6: &iaas.UpdateNetworkIPv6Body{
|
||||
Nameservers: &[]string{},
|
||||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -389,23 +389,34 @@ func toCreatePayload(ctx context.Context, model *networkModel.Model) (*iaasalpha
|
|||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
modelIPv6Nameservers := []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
var modelIPv6Nameservers []string
|
||||
// Is true when IPv6Nameservers is not null or unset
|
||||
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||
// empty slice != nil slice. Empty slice will result in an empty list in the payload []. Nil slice will result in a payload without the property set
|
||||
modelIPv6Nameservers = []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
var ipv6Body *iaasalpha.CreateNetworkIPv6
|
||||
if !utils.IsUndefined(model.IPv6PrefixLength) {
|
||||
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
||||
CreateNetworkIPv6WithPrefixLength: &iaasalpha.CreateNetworkIPv6WithPrefixLength{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv6PrefixLength),
|
||||
},
|
||||
}
|
||||
// IPv6 nameservers should only be set, if it contains any value. If the slice is nil, it should NOT be set.
|
||||
// Setting it to a nil slice would result in a payload, where nameservers is set to null in the json payload,
|
||||
// but it should actually be unset. Setting it to "null" will result in an error, because it's NOT nullable.
|
||||
if modelIPv6Nameservers != nil {
|
||||
ipv6Body.CreateNetworkIPv6WithPrefixLength.Nameservers = &modelIPv6Nameservers
|
||||
}
|
||||
} else if !utils.IsUndefined(model.IPv6Prefix) {
|
||||
var gateway *iaasalpha.NullableString
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
|
|
@ -416,11 +427,16 @@ func toCreatePayload(ctx context.Context, model *networkModel.Model) (*iaasalpha
|
|||
|
||||
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
||||
CreateNetworkIPv6WithPrefix: &iaasalpha.CreateNetworkIPv6WithPrefix{
|
||||
Gateway: gateway,
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||
Gateway: gateway,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||
},
|
||||
}
|
||||
// IPv6 nameservers should only be set, if it contains any value. If the slice is nil, it should NOT be set.
|
||||
// Setting it to a nil slice would result in a payload, where nameservers is set to null in the json payload,
|
||||
// but it should actually be unset. Setting it to "null" will result in an error, because it's NOT nullable.
|
||||
if modelIPv6Nameservers != nil {
|
||||
ipv6Body.CreateNetworkIPv6WithPrefix.Nameservers = &modelIPv6Nameservers
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
|
|
@ -487,19 +503,29 @@ func toUpdatePayload(ctx context.Context, model, stateModel *networkModel.Model)
|
|||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
modelIPv6Nameservers := []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
var modelIPv6Nameservers []string
|
||||
// Is true when IPv6Nameservers is not null or unset
|
||||
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||
// empty slice != nil slice. Empty slice will result in an empty list in the payload []. Nil slice will result in a payload without the property set
|
||||
modelIPv6Nameservers = []string{}
|
||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
var ipv6Body *iaasalpha.UpdateNetworkIPv6Body
|
||||
if !(model.IPv6Nameservers.IsNull() || model.IPv6Nameservers.IsUnknown()) {
|
||||
ipv6Body = &iaasalpha.UpdateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
if modelIPv6Nameservers != nil || !utils.IsUndefined(model.NoIPv6Gateway) || !utils.IsUndefined(model.IPv6Gateway) {
|
||||
ipv6Body = &iaasalpha.UpdateNetworkIPv6Body{}
|
||||
// IPv6 nameservers should only be set, if it contains any value. If the slice is nil, it should NOT be set.
|
||||
// Setting it to a nil slice would result in a payload, where nameservers is set to null in the json payload,
|
||||
// but it should actually be unset. Setting it to "null" will result in an error, because it's NOT nullable.
|
||||
if modelIPv6Nameservers != nil {
|
||||
ipv6Body.Nameservers = &modelIPv6Nameservers
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
|
|
|
|||
|
|
@ -492,6 +492,62 @@ func TestToCreatePayload(t *testing.T) {
|
|||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_null",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(false),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
IPv6Prefix: types.StringValue("prefix"),
|
||||
},
|
||||
&iaasalpha.CreateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv6: &iaasalpha.CreateNetworkIPv6{
|
||||
CreateNetworkIPv6WithPrefix: &iaasalpha.CreateNetworkIPv6WithPrefix{
|
||||
Nameservers: nil,
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Prefix: utils.Ptr("prefix"),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(false),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_empty_list",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(false),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
IPv6Prefix: types.StringValue("prefix"),
|
||||
},
|
||||
&iaasalpha.CreateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv6: &iaasalpha.CreateNetworkIPv6{
|
||||
CreateNetworkIPv6WithPrefix: &iaasalpha.CreateNetworkIPv6WithPrefix{
|
||||
Nameservers: utils.Ptr([]string{}),
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Prefix: utils.Ptr("prefix"),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(false),
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
|
|
@ -686,6 +742,62 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_null",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaasalpha.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv6: &iaasalpha.UpdateNetworkIPv6Body{
|
||||
Nameservers: nil,
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameserver_empty_list",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaasalpha.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv6: &iaasalpha.UpdateNetworkIPv6Body{
|
||||
Nameservers: utils.Ptr([]string{}),
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue