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: ""
|
subcategory: ""
|
||||||
description: |-
|
description: |-
|
||||||
Network resource schema. Must have a region specified in the provider configuration.
|
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)
|
# stackit_network (Resource)
|
||||||
|
|
||||||
Network resource schema. Must have a `region` specified in the provider configuration.
|
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
|
## Example Usage
|
||||||
|
|
||||||
|
|
@ -68,7 +74,7 @@ import {
|
||||||
- `ipv6_prefix` (String) The IPv6 prefix of the network (CIDR).
|
- `ipv6_prefix` (String) The IPv6 prefix of the network (CIDR).
|
||||||
- `ipv6_prefix_length` (Number) The IPv6 prefix length of the network.
|
- `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
|
- `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_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.
|
- `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.
|
- `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.
|
- `ipv4_prefixes` (List of String) The IPv4 prefixes of the network.
|
||||||
- `ipv6_prefixes` (List of String) The IPv6 prefixes of the network.
|
- `ipv6_prefixes` (List of String) The IPv6 prefixes of the network.
|
||||||
- `network_id` (String) The network ID.
|
- `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.
|
- `public_ip` (String) The public IP of the network.
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"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/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"
|
||||||
|
|
@ -37,6 +39,13 @@ var (
|
||||||
_ resource.ResourceWithImportState = &networkResource{}
|
_ 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.
|
// NewNetworkResource is a helper function to simplify the provider implementation.
|
||||||
func NewNetworkResource() resource.Resource {
|
func NewNetworkResource() resource.Resource {
|
||||||
return &networkResource{}
|
return &networkResource{}
|
||||||
|
|
@ -88,10 +97,6 @@ func (r *networkResource) Configure(ctx context.Context, req resource.ConfigureR
|
||||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||||
// Use the modifier to set the effective region in the current plan.
|
// 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
|
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
|
var configModel model.Model
|
||||||
// skip initial empty configuration to avoid follow-up errors
|
// skip initial empty configuration to avoid follow-up errors
|
||||||
if req.Config.Raw.IsNull() {
|
if req.Config.Raw.IsNull() {
|
||||||
|
|
@ -108,6 +113,15 @@ func (r *networkResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
|
||||||
return
|
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)
|
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
|
|
@ -171,8 +185,11 @@ func (r *networkResource) ConfigValidators(_ context.Context) []resource.ConfigV
|
||||||
|
|
||||||
// Schema defines the schema for the resource.
|
// Schema defines the schema for the resource.
|
||||||
func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
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{
|
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{
|
Attributes: map[string]schema.Attribute{
|
||||||
"id": schema.StringAttribute{
|
"id": schema.StringAttribute{
|
||||||
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`network_id`\".",
|
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{
|
"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.",
|
DeprecationMessage: "Use `ipv4_nameservers` to configure the nameservers for IPv4.",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
|
@ -259,7 +276,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"prefixes": schema.ListAttribute{
|
"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.",
|
DeprecationMessage: "Use `ipv4_prefixes` to read the prefixes of the IPv4 networks.",
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ElementType: types.StringType,
|
ElementType: types.StringType,
|
||||||
|
|
@ -299,6 +316,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
||||||
"ipv6_prefix": schema.StringAttribute{
|
"ipv6_prefix": schema.StringAttribute{
|
||||||
Description: "The IPv6 prefix of the network (CIDR).",
|
Description: "The IPv6 prefix of the network (CIDR).",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
Validators: []validator.String{
|
Validators: []validator.String{
|
||||||
validate.CIDR(),
|
validate.CIDR(),
|
||||||
},
|
},
|
||||||
|
|
@ -309,6 +327,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
||||||
"ipv6_prefix_length": schema.Int64Attribute{
|
"ipv6_prefix_length": schema.Int64Attribute{
|
||||||
Description: "The IPv6 prefix length of the network.",
|
Description: "The IPv6 prefix length of the network.",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
},
|
},
|
||||||
"ipv6_prefixes": schema.ListAttribute{
|
"ipv6_prefixes": schema.ListAttribute{
|
||||||
Description: "The IPv6 prefixes of the network.",
|
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.
|
// 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
|
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 {
|
if !r.isExperimental {
|
||||||
v1network.Create(ctx, req, resp, r.client)
|
v1network.Create(ctx, req, resp, r.client)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -409,3 +440,9 @@ func (r *networkResource) ImportState(ctx context.Context, req resource.ImportSt
|
||||||
v2network.ImportState(ctx, req, resp)
|
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
|
model.IPv6Nameservers = ipv6NameserversTF
|
||||||
}
|
}
|
||||||
|
|
||||||
if networkResp.PrefixesV6 == nil {
|
if networkResp.PrefixesV6 == nil || len(*networkResp.PrefixesV6) == 0 {
|
||||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||||
|
model.IPv6Prefix = types.StringNull()
|
||||||
|
model.IPv6PrefixLength = types.Int64Null()
|
||||||
} else {
|
} else {
|
||||||
respPrefixesV6 := *networkResp.PrefixesV6
|
respPrefixesV6 := *networkResp.PrefixesV6
|
||||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
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{}
|
addressFamily := &iaas.CreateNetworkAddressFamily{}
|
||||||
|
|
||||||
modelIPv6Nameservers := []string{}
|
var modelIPv6Nameservers []string
|
||||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
// Is true when IPv6Nameservers is not null or unset
|
||||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||||
if !ok {
|
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||||
return nil, fmt.Errorf("type assertion failed")
|
// 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{
|
addressFamily.Ipv6 = &iaas.CreateNetworkIPv6Body{
|
||||||
Nameservers: &modelIPv6Nameservers,
|
|
||||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv6PrefixLength),
|
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() {
|
if model.NoIPv6Gateway.ValueBool() {
|
||||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||||
|
|
@ -445,23 +458,34 @@ func toUpdatePayload(ctx context.Context, model, stateModel *networkModel.Model)
|
||||||
}
|
}
|
||||||
addressFamily := &iaas.UpdateNetworkAddressFamily{}
|
addressFamily := &iaas.UpdateNetworkAddressFamily{}
|
||||||
|
|
||||||
modelIPv6Nameservers := []string{}
|
var modelIPv6Nameservers []string
|
||||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
// Is true when IPv6Nameservers is not null or unset
|
||||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||||
if !ok {
|
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||||
return nil, fmt.Errorf("type assertion failed")
|
// 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()) {
|
if !utils.IsUndefined(model.NoIPv6Gateway) || !utils.IsUndefined(model.IPv6Gateway) || modelIPv6Nameservers != nil {
|
||||||
addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{
|
addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{}
|
||||||
Nameservers: &modelIPv6Nameservers,
|
|
||||||
|
// 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() {
|
if model.NoIPv6Gateway.ValueBool() {
|
||||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
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))
|
addressFamily.Ipv6.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,66 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
true,
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
t.Run(tt.description, func(t *testing.T) {
|
||||||
|
|
@ -670,6 +730,66 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
true,
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
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")
|
return nil, fmt.Errorf("nil model")
|
||||||
}
|
}
|
||||||
|
|
||||||
modelIPv6Nameservers := []string{}
|
var modelIPv6Nameservers []string
|
||||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
// Is true when IPv6Nameservers is not null or unset
|
||||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||||
if !ok {
|
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||||
return nil, fmt.Errorf("type assertion failed")
|
// 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
|
var ipv6Body *iaasalpha.CreateNetworkIPv6
|
||||||
if !utils.IsUndefined(model.IPv6PrefixLength) {
|
if !utils.IsUndefined(model.IPv6PrefixLength) {
|
||||||
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
||||||
CreateNetworkIPv6WithPrefixLength: &iaasalpha.CreateNetworkIPv6WithPrefixLength{
|
CreateNetworkIPv6WithPrefixLength: &iaasalpha.CreateNetworkIPv6WithPrefixLength{
|
||||||
Nameservers: &modelIPv6Nameservers,
|
|
||||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv6PrefixLength),
|
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) {
|
} else if !utils.IsUndefined(model.IPv6Prefix) {
|
||||||
var gateway *iaasalpha.NullableString
|
var gateway *iaasalpha.NullableString
|
||||||
if model.NoIPv6Gateway.ValueBool() {
|
if model.NoIPv6Gateway.ValueBool() {
|
||||||
|
|
@ -416,11 +427,16 @@ func toCreatePayload(ctx context.Context, model *networkModel.Model) (*iaasalpha
|
||||||
|
|
||||||
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
||||||
CreateNetworkIPv6WithPrefix: &iaasalpha.CreateNetworkIPv6WithPrefix{
|
CreateNetworkIPv6WithPrefix: &iaasalpha.CreateNetworkIPv6WithPrefix{
|
||||||
Gateway: gateway,
|
Gateway: gateway,
|
||||||
Nameservers: &modelIPv6Nameservers,
|
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||||
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{}
|
modelIPv4Nameservers := []string{}
|
||||||
|
|
@ -487,19 +503,29 @@ func toUpdatePayload(ctx context.Context, model, stateModel *networkModel.Model)
|
||||||
return nil, fmt.Errorf("nil model")
|
return nil, fmt.Errorf("nil model")
|
||||||
}
|
}
|
||||||
|
|
||||||
modelIPv6Nameservers := []string{}
|
var modelIPv6Nameservers []string
|
||||||
for _, ipv6ns := range model.IPv6Nameservers.Elements() {
|
// Is true when IPv6Nameservers is not null or unset
|
||||||
ipv6NameserverString, ok := ipv6ns.(types.String)
|
if !utils.IsUndefined(model.IPv6Nameservers) {
|
||||||
if !ok {
|
// If ipv6Nameservers is empty, modelIPv6Nameservers will be set to an empty slice.
|
||||||
return nil, fmt.Errorf("type assertion failed")
|
// 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
|
var ipv6Body *iaasalpha.UpdateNetworkIPv6Body
|
||||||
if !(model.IPv6Nameservers.IsNull() || model.IPv6Nameservers.IsUnknown()) {
|
if modelIPv6Nameservers != nil || !utils.IsUndefined(model.NoIPv6Gateway) || !utils.IsUndefined(model.IPv6Gateway) {
|
||||||
ipv6Body = &iaasalpha.UpdateNetworkIPv6Body{
|
ipv6Body = &iaasalpha.UpdateNetworkIPv6Body{}
|
||||||
Nameservers: &modelIPv6Nameservers,
|
// 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() {
|
if model.NoIPv6Gateway.ValueBool() {
|
||||||
|
|
|
||||||
|
|
@ -492,6 +492,62 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
true,
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
t.Run(tt.description, func(t *testing.T) {
|
||||||
|
|
@ -686,6 +742,62 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
true,
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
t.Run(tt.description, func(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue